From 43ce25cda19b0803e880017f118f62ae2311813c Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Sun, 27 Dec 2020 05:27:59 +0800 Subject: [PATCH 01/73] Remove unused mods in actix-utils (#229) --- actix-utils/CHANGES.md | 5 +- actix-utils/Cargo.toml | 17 +- actix-utils/src/condition.rs | 129 -------------- actix-utils/src/counter.rs | 5 +- actix-utils/src/dispatcher.rs | 68 ++++---- actix-utils/src/either.rs | 153 ---------------- actix-utils/src/inflight.rs | 169 ------------------ actix-utils/src/keepalive.rs | 125 -------------- actix-utils/src/lib.rs | 8 - actix-utils/src/mpsc.rs | 13 +- actix-utils/src/oneshot.rs | 316 ---------------------------------- actix-utils/src/order.rs | 283 ------------------------------ actix-utils/src/stream.rs | 76 -------- actix-utils/src/task.rs | 11 +- actix-utils/src/time.rs | 225 ------------------------ actix-utils/src/timeout.rs | 72 +++++--- 16 files changed, 111 insertions(+), 1564 deletions(-) delete mode 100644 actix-utils/src/condition.rs delete mode 100644 actix-utils/src/either.rs delete mode 100644 actix-utils/src/inflight.rs delete mode 100644 actix-utils/src/keepalive.rs delete mode 100644 actix-utils/src/oneshot.rs delete mode 100644 actix-utils/src/order.rs delete mode 100644 actix-utils/src/stream.rs delete mode 100644 actix-utils/src/time.rs diff --git a/actix-utils/CHANGES.md b/actix-utils/CHANGES.md index 22b7c7bf..b4d59ed0 100644 --- a/actix-utils/CHANGES.md +++ b/actix-utils/CHANGES.md @@ -1,7 +1,10 @@ # Changes ## Unreleased - 2020-xx-xx -* Upgrade `pin-project` to `1.0`. +* Use `pin-project-lite` to replace `pin-project`. [#229] +* Remove `condition`,`either`,`inflight`,`keepalive`,`oneshot`,`order`,`stream` and `time` mods. [#229] + +[#229]: https://github.com/actix/actix-net/pull/229 ## 2.0.0 - 2020-08-23 * No changes from beta 1. diff --git a/actix-utils/Cargo.toml b/actix-utils/Cargo.toml index 7f47f359..f5bd5793 100644 --- a/actix-utils/Cargo.toml +++ b/actix-utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-utils" -version = "2.0.0" +version = "3.0.0" authors = ["Nikolay Kim "] description = "Various network related services and utilities for the Actix ecosystem." keywords = ["network", "framework", "async", "futures"] @@ -19,12 +19,11 @@ path = "src/lib.rs" actix-codec = "0.3.0" actix-rt = "1.1.1" actix-service = "1.0.6" -bitflags = "1.2.1" -bytes = "0.5.3" -either = "1.5.3" -futures-channel = { version = "0.3.4", default-features = false } -futures-sink = { version = "0.3.4", default-features = false } -futures-util = { version = "0.3.4", default-features = false } + +futures-core = { version = "0.3.7", default-features = false } +futures-sink = { version = "0.3.7", default-features = false } log = "0.4" -pin-project = "1.0.0" -slab = "0.4" +pin-project-lite = "0.2.0" + +[dev-dependencies] +futures-util = { version = "0.3.7", default-features = false } diff --git a/actix-utils/src/condition.rs b/actix-utils/src/condition.rs deleted file mode 100644 index 9c7c977c..00000000 --- a/actix-utils/src/condition.rs +++ /dev/null @@ -1,129 +0,0 @@ -use std::cell::RefCell; -use std::future::Future; -use std::pin::Pin; -use std::rc::Rc; -use std::task::{Context, Poll}; - -use slab::Slab; - -use crate::task::LocalWaker; - -/// Condition allows to notify multiple receivers at the same time -pub struct Condition(Rc>); - -struct Inner { - data: Slab>, -} - -impl Default for Condition { - fn default() -> Self { - Self::new() - } -} - -impl Condition { - pub fn new() -> Condition { - Condition(Rc::new(RefCell::new(Inner { data: Slab::new() }))) - } - - /// Get condition waiter - pub fn wait(&mut self) -> Waiter { - let token = self.0.borrow_mut().data.insert(None); - Waiter { - token, - inner: self.0.clone(), - } - } - - /// Notify all waiters - pub fn notify(&self) { - let inner = self.0.borrow(); - for item in inner.data.iter() { - if let Some(waker) = item.1 { - waker.wake(); - } - } - } -} - -impl Drop for Condition { - fn drop(&mut self) { - self.notify() - } -} - -#[must_use = "Waiter do nothing unless polled"] -pub struct Waiter { - token: usize, - inner: Rc>, -} - -impl Clone for Waiter { - fn clone(&self) -> Self { - let token = self.inner.borrow_mut().data.insert(None); - Waiter { - token, - inner: self.inner.clone(), - } - } -} - -impl Future for Waiter { - type Output = (); - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let this = self.get_mut(); - - let mut inner = this.inner.borrow_mut(); - let inner = unsafe { inner.data.get_unchecked_mut(this.token) }; - if inner.is_none() { - let waker = LocalWaker::default(); - waker.register(cx.waker()); - *inner = Some(waker); - Poll::Pending - } else if inner.as_mut().unwrap().register(cx.waker()) { - Poll::Pending - } else { - Poll::Ready(()) - } - } -} - -impl Drop for Waiter { - fn drop(&mut self) { - self.inner.borrow_mut().data.remove(self.token); - } -} - -#[cfg(test)] -mod tests { - use super::*; - use futures_util::future::lazy; - - #[actix_rt::test] - async fn test_condition() { - let mut cond = Condition::new(); - let mut waiter = cond.wait(); - assert_eq!( - lazy(|cx| Pin::new(&mut waiter).poll(cx)).await, - Poll::Pending - ); - cond.notify(); - waiter.await; - - let mut waiter = cond.wait(); - assert_eq!( - lazy(|cx| Pin::new(&mut waiter).poll(cx)).await, - Poll::Pending - ); - let mut waiter2 = waiter.clone(); - assert_eq!( - lazy(|cx| Pin::new(&mut waiter2).poll(cx)).await, - Poll::Pending - ); - - drop(cond); - waiter.await; - waiter2.await; - } -} diff --git a/actix-utils/src/counter.rs b/actix-utils/src/counter.rs index 4fe9dd0a..0b5984d2 100644 --- a/actix-utils/src/counter.rs +++ b/actix-utils/src/counter.rs @@ -1,6 +1,7 @@ -use std::cell::Cell; +use core::cell::Cell; +use core::task; + use std::rc::Rc; -use std::task; use crate::task::LocalWaker; diff --git a/actix-utils/src/dispatcher.rs b/actix-utils/src/dispatcher.rs index 1ee72564..c3cb4f16 100644 --- a/actix-utils/src/dispatcher.rs +++ b/actix-utils/src/dispatcher.rs @@ -2,13 +2,14 @@ #![allow(type_alias_bounds)] -use std::pin::Pin; -use std::task::{Context, Poll}; -use std::{fmt, mem}; +use core::future::Future; +use core::pin::Pin; +use core::task::{Context, Poll}; +use core::{fmt, mem}; use actix_codec::{AsyncRead, AsyncWrite, Decoder, Encoder, Framed}; use actix_service::{IntoService, Service}; -use futures_util::{future::Future, stream::Stream, FutureExt}; +use futures_core::stream::Stream; use log::debug; use crate::mpsc; @@ -61,25 +62,28 @@ pub enum Message { Close, } -/// Dispatcher is a future that reads frames from Framed object -/// and passes them to the service. -#[pin_project::pin_project] -pub struct Dispatcher -where - S: Service::Item, Response = I>, - S::Error: 'static, - S::Future: 'static, - T: AsyncRead + AsyncWrite, - U: Encoder + Decoder, - I: 'static, - >::Error: std::fmt::Debug, -{ - service: S, - state: State, - #[pin] - framed: Framed, - rx: mpsc::Receiver, S::Error>>, - tx: mpsc::Sender, S::Error>>, +pin_project_lite::pin_project! { + /// Dispatcher is a future that reads frames from Framed object + /// and passes them to the service. + pub struct Dispatcher + where + S: Service::Item, Response = I>, + S::Error: 'static, + S::Future: 'static, + T: AsyncRead, + T: AsyncWrite, + U: Encoder, + U: Decoder, + I: 'static, + >::Error: fmt::Debug, + { + service: S, + state: State, + #[pin] + framed: Framed, + rx: mpsc::Receiver, S::Error>>, + tx: mpsc::Sender, S::Error>>, + } } enum State + Decoder, I> { @@ -114,8 +118,8 @@ where T: AsyncRead + AsyncWrite, U: Decoder + Encoder, I: 'static, - ::Error: std::fmt::Debug, - >::Error: std::fmt::Debug, + ::Error: fmt::Debug, + >::Error: fmt::Debug, { pub fn new>(framed: Framed, service: F) -> Self { let (tx, rx) = mpsc::channel(); @@ -178,7 +182,7 @@ where T: AsyncRead + AsyncWrite, U: Decoder + Encoder, I: 'static, - >::Error: std::fmt::Debug, + >::Error: fmt::Debug, { loop { let this = self.as_mut().project(); @@ -198,9 +202,11 @@ where }; let tx = this.tx.clone(); - actix_rt::spawn(this.service.call(item).map(move |item| { + let fut = this.service.call(item); + actix_rt::spawn(async move { + let item = fut.await; let _ = tx.send(item.map(Message::Item)); - })); + }); } Poll::Pending => return false, Poll::Ready(Err(err)) => { @@ -220,7 +226,7 @@ where T: AsyncRead + AsyncWrite, U: Decoder + Encoder, I: 'static, - >::Error: std::fmt::Debug, + >::Error: fmt::Debug, { loop { let mut this = self.as_mut().project(); @@ -271,8 +277,8 @@ where T: AsyncRead + AsyncWrite, U: Decoder + Encoder, I: 'static, - >::Error: std::fmt::Debug, - ::Error: std::fmt::Debug, + >::Error: fmt::Debug, + ::Error: fmt::Debug, { type Output = Result<(), DispatcherError>; diff --git a/actix-utils/src/either.rs b/actix-utils/src/either.rs deleted file mode 100644 index fdf15ffe..00000000 --- a/actix-utils/src/either.rs +++ /dev/null @@ -1,153 +0,0 @@ -//! Contains `Either` service and related types and functions. -use std::pin::Pin; -use std::task::{Context, Poll}; - -use actix_service::{Service, ServiceFactory}; -use futures_util::{future, future::Future, ready}; - -/// Combine two different service types into a single type. -/// -/// Both services must be of the same request, response, and error types. -/// `EitherService` is useful for handling conditional branching in service -/// middleware to different inner service types. -pub struct EitherService { - left: A, - right: B, -} - -impl Clone for EitherService { - fn clone(&self) -> Self { - EitherService { - left: self.left.clone(), - right: self.right.clone(), - } - } -} - -impl Service for EitherService -where - A: Service, - B: Service, -{ - type Request = either::Either; - type Response = A::Response; - type Error = A::Error; - type Future = future::Either; - - fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { - let left = self.left.poll_ready(cx)?; - let right = self.right.poll_ready(cx)?; - - if left.is_ready() && right.is_ready() { - Poll::Ready(Ok(())) - } else { - Poll::Pending - } - } - - fn call(&mut self, req: either::Either) -> Self::Future { - match req { - either::Either::Left(req) => future::Either::Left(self.left.call(req)), - either::Either::Right(req) => future::Either::Right(self.right.call(req)), - } - } -} - -/// Combine two different new service types into a single service. -pub struct Either { - left: A, - right: B, -} - -impl Either { - pub fn new(left: A, right: B) -> Either - where - A: ServiceFactory, - A::Config: Clone, - B: ServiceFactory< - Config = A::Config, - Response = A::Response, - Error = A::Error, - InitError = A::InitError, - >, - { - Either { left, right } - } -} - -impl ServiceFactory for Either -where - A: ServiceFactory, - A::Config: Clone, - B: ServiceFactory< - Config = A::Config, - Response = A::Response, - Error = A::Error, - InitError = A::InitError, - >, -{ - type Request = either::Either; - type Response = A::Response; - type Error = A::Error; - type InitError = A::InitError; - type Config = A::Config; - type Service = EitherService; - type Future = EitherNewService; - - fn new_service(&self, cfg: A::Config) -> Self::Future { - EitherNewService { - left: None, - right: None, - left_fut: self.left.new_service(cfg.clone()), - right_fut: self.right.new_service(cfg), - } - } -} - -impl Clone for Either { - fn clone(&self) -> Self { - Self { - left: self.left.clone(), - right: self.right.clone(), - } - } -} - -#[doc(hidden)] -#[pin_project::pin_project] -pub struct EitherNewService { - left: Option, - right: Option, - #[pin] - left_fut: A::Future, - #[pin] - right_fut: B::Future, -} - -impl Future for EitherNewService -where - A: ServiceFactory, - B: ServiceFactory, -{ - type Output = Result, A::InitError>; - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let this = self.project(); - - if this.left.is_none() { - *this.left = Some(ready!(this.left_fut.poll(cx))?); - } - if this.right.is_none() { - *this.right = Some(ready!(this.right_fut.poll(cx))?); - } - - if this.left.is_some() && this.right.is_some() { - Poll::Ready(Ok(EitherService { - left: this.left.take().unwrap(), - right: this.right.take().unwrap(), - })) - } else { - Poll::Pending - } - } -} diff --git a/actix-utils/src/inflight.rs b/actix-utils/src/inflight.rs deleted file mode 100644 index 8975a2d2..00000000 --- a/actix-utils/src/inflight.rs +++ /dev/null @@ -1,169 +0,0 @@ -use std::convert::Infallible; -use std::future::Future; -use std::pin::Pin; -use std::task::{Context, Poll}; - -use actix_service::{IntoService, Service, Transform}; -use futures_util::future::{ok, Ready}; - -use super::counter::{Counter, CounterGuard}; - -/// InFlight - new service for service that can limit number of in-flight -/// async requests. -/// -/// Default number of in-flight requests is 15 -pub struct InFlight { - max_inflight: usize, -} - -impl InFlight { - pub fn new(max: usize) -> Self { - Self { max_inflight: max } - } -} - -impl Default for InFlight { - fn default() -> Self { - Self::new(15) - } -} - -impl Transform for InFlight -where - S: Service, -{ - type Request = S::Request; - type Response = S::Response; - type Error = S::Error; - type InitError = Infallible; - type Transform = InFlightService; - type Future = Ready>; - - fn new_transform(&self, service: S) -> Self::Future { - ok(InFlightService::new(self.max_inflight, service)) - } -} - -pub struct InFlightService { - count: Counter, - service: S, -} - -impl InFlightService -where - S: Service, -{ - pub fn new(max: usize, service: U) -> Self - where - U: IntoService, - { - Self { - count: Counter::new(max), - service: service.into_service(), - } - } -} - -impl Service for InFlightService -where - T: Service, -{ - type Request = T::Request; - type Response = T::Response; - type Error = T::Error; - type Future = InFlightServiceResponse; - - fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { - if self.service.poll_ready(cx)?.is_pending() { - Poll::Pending - } else if !self.count.available(cx) { - log::trace!("InFlight limit exceeded"); - Poll::Pending - } else { - Poll::Ready(Ok(())) - } - } - - fn call(&mut self, req: T::Request) -> Self::Future { - InFlightServiceResponse { - fut: self.service.call(req), - _guard: self.count.get(), - } - } -} - -#[doc(hidden)] -#[pin_project::pin_project] -pub struct InFlightServiceResponse { - #[pin] - fut: T::Future, - _guard: CounterGuard, -} - -impl Future for InFlightServiceResponse { - type Output = Result; - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - self.project().fut.poll(cx) - } -} - -#[cfg(test)] -mod tests { - - use std::task::{Context, Poll}; - use std::time::Duration; - - use super::*; - use actix_service::{apply, fn_factory, Service, ServiceFactory}; - use futures_util::future::{lazy, ok, FutureExt, LocalBoxFuture}; - - struct SleepService(Duration); - - impl Service for SleepService { - type Request = (); - type Response = (); - type Error = (); - type Future = LocalBoxFuture<'static, Result<(), ()>>; - - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } - - fn call(&mut self, _: ()) -> Self::Future { - actix_rt::time::delay_for(self.0) - .then(|_| ok::<_, ()>(())) - .boxed_local() - } - } - - #[actix_rt::test] - async fn test_transform() { - let wait_time = Duration::from_millis(50); - - let mut srv = InFlightService::new(1, SleepService(wait_time)); - assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(()))); - - let res = srv.call(()); - assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Pending); - - let _ = res.await; - assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(()))); - } - - #[actix_rt::test] - async fn test_new_transform() { - let wait_time = Duration::from_millis(50); - - let srv = apply(InFlight::new(1), fn_factory(|| ok(SleepService(wait_time)))); - - let mut srv = srv.new_service(&()).await.unwrap(); - assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(()))); - - let res = srv.call(()); - assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Pending); - - let _ = res.await; - assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(()))); - } -} diff --git a/actix-utils/src/keepalive.rs b/actix-utils/src/keepalive.rs deleted file mode 100644 index 4413dcd5..00000000 --- a/actix-utils/src/keepalive.rs +++ /dev/null @@ -1,125 +0,0 @@ -use std::convert::Infallible; -use std::future::Future; -use std::marker::PhantomData; -use std::pin::Pin; -use std::task::{Context, Poll}; -use std::time::Duration; - -use actix_rt::time::{delay_until, Delay, Instant}; -use actix_service::{Service, ServiceFactory}; -use futures_util::future::{ok, Ready}; - -use super::time::{LowResTime, LowResTimeService}; - -pub struct KeepAlive { - f: F, - ka: Duration, - time: LowResTime, - _t: PhantomData<(R, E)>, -} - -impl KeepAlive -where - F: Fn() -> E + Clone, -{ - pub fn new(ka: Duration, time: LowResTime, f: F) -> Self { - KeepAlive { - f, - ka, - time, - _t: PhantomData, - } - } -} - -impl Clone for KeepAlive -where - F: Clone, -{ - fn clone(&self) -> Self { - KeepAlive { - f: self.f.clone(), - ka: self.ka, - time: self.time.clone(), - _t: PhantomData, - } - } -} - -impl ServiceFactory for KeepAlive -where - F: Fn() -> E + Clone, -{ - type Request = R; - type Response = R; - type Error = E; - type InitError = Infallible; - type Config = (); - type Service = KeepAliveService; - type Future = Ready>; - - fn new_service(&self, _: ()) -> Self::Future { - ok(KeepAliveService::new( - self.ka, - self.time.timer(), - self.f.clone(), - )) - } -} - -pub struct KeepAliveService { - f: F, - ka: Duration, - time: LowResTimeService, - delay: Delay, - expire: Instant, - _t: PhantomData<(R, E)>, -} - -impl KeepAliveService -where - F: Fn() -> E, -{ - pub fn new(ka: Duration, time: LowResTimeService, f: F) -> Self { - let expire = Instant::from_std(time.now() + ka); - KeepAliveService { - f, - ka, - time, - expire, - delay: delay_until(expire), - _t: PhantomData, - } - } -} - -impl Service for KeepAliveService -where - F: Fn() -> E, -{ - type Request = R; - type Response = R; - type Error = E; - type Future = Ready>; - - fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { - match Pin::new(&mut self.delay).poll(cx) { - Poll::Ready(_) => { - let now = Instant::from_std(self.time.now()); - if self.expire <= now { - Poll::Ready(Err((self.f)())) - } else { - self.delay.reset(self.expire); - let _ = Pin::new(&mut self.delay).poll(cx); - Poll::Ready(Ok(())) - } - } - Poll::Pending => Poll::Ready(Ok(())), - } - } - - fn call(&mut self, req: R) -> Self::Future { - self.expire = Instant::from_std(self.time.now() + self.ka); - ok(req) - } -} diff --git a/actix-utils/src/lib.rs b/actix-utils/src/lib.rs index 19df225b..4c4f019c 100644 --- a/actix-utils/src/lib.rs +++ b/actix-utils/src/lib.rs @@ -5,16 +5,8 @@ #![doc(html_logo_url = "https://actix.rs/img/logo.png")] #![doc(html_favicon_url = "https://actix.rs/favicon.ico")] -pub mod condition; pub mod counter; pub mod dispatcher; -pub mod either; -pub mod inflight; -pub mod keepalive; pub mod mpsc; -pub mod oneshot; -pub mod order; -pub mod stream; pub mod task; -pub mod time; pub mod timeout; diff --git a/actix-utils/src/mpsc.rs b/actix-utils/src/mpsc.rs index 5905e123..2299dedb 100644 --- a/actix-utils/src/mpsc.rs +++ b/actix-utils/src/mpsc.rs @@ -1,15 +1,16 @@ //! A multi-producer, single-consumer, futures-aware, FIFO queue. -use std::any::Any; -use std::cell::RefCell; +use core::any::Any; +use core::cell::RefCell; +use core::fmt; +use core::pin::Pin; +use core::task::{Context, Poll}; + use std::collections::VecDeque; use std::error::Error; -use std::fmt; -use std::pin::Pin; use std::rc::Rc; -use std::task::{Context, Poll}; +use futures_core::stream::Stream; use futures_sink::Sink; -use futures_util::stream::Stream; use crate::task::LocalWaker; diff --git a/actix-utils/src/oneshot.rs b/actix-utils/src/oneshot.rs deleted file mode 100644 index e75fad60..00000000 --- a/actix-utils/src/oneshot.rs +++ /dev/null @@ -1,316 +0,0 @@ -//! A one-shot, futures-aware channel. -use std::cell::RefCell; -use std::future::Future; -use std::pin::Pin; -use std::rc::Rc; -use std::task::{Context, Poll}; - -pub use futures_channel::oneshot::Canceled; -use slab::Slab; - -use crate::task::LocalWaker; - -/// Creates a new futures-aware, one-shot channel. -pub fn channel() -> (Sender, Receiver) { - let inner = Rc::new(RefCell::new(Inner { - value: None, - rx_task: LocalWaker::new(), - })); - let tx = Sender { - inner: inner.clone(), - }; - let rx = Receiver { inner }; - (tx, rx) -} - -/// Creates a new futures-aware, pool of one-shot's. -pub fn pool() -> Pool { - Pool(Rc::new(RefCell::new(Slab::new()))) -} - -/// Represents the completion half of a oneshot through which the result of a -/// computation is signaled. -#[derive(Debug)] -pub struct Sender { - inner: Rc>>, -} - -/// A future representing the completion of a computation happening elsewhere in -/// memory. -#[derive(Debug)] -#[must_use = "futures do nothing unless polled"] -pub struct Receiver { - inner: Rc>>, -} - -// The channels do not ever project Pin to the inner T -impl Unpin for Receiver {} -impl Unpin for Sender {} - -#[derive(Debug)] -struct Inner { - value: Option, - rx_task: LocalWaker, -} - -impl Sender { - /// Completes this oneshot with a successful result. - /// - /// This function will consume `self` and indicate to the other end, the - /// `Receiver`, that the error provided is the result of the computation this - /// represents. - /// - /// If the value is successfully enqueued for the remote end to receive, - /// then `Ok(())` is returned. If the receiving end was dropped before - /// this function was called, however, then `Err` is returned with the value - /// provided. - pub fn send(self, val: T) -> Result<(), T> { - if Rc::strong_count(&self.inner) == 2 { - let mut inner = self.inner.borrow_mut(); - inner.value = Some(val); - inner.rx_task.wake(); - Ok(()) - } else { - Err(val) - } - } - - /// Tests to see whether this `Sender`'s corresponding `Receiver` - /// has gone away. - pub fn is_canceled(&self) -> bool { - Rc::strong_count(&self.inner) == 1 - } -} - -impl Drop for Sender { - fn drop(&mut self) { - self.inner.borrow().rx_task.wake(); - } -} - -impl Future for Receiver { - type Output = Result; - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let this = self.get_mut(); - - // If we've got a value, then skip the logic below as we're done. - if let Some(val) = this.inner.borrow_mut().value.take() { - return Poll::Ready(Ok(val)); - } - - // Check if sender is dropped and return error if it is. - if Rc::strong_count(&this.inner) == 1 { - Poll::Ready(Err(Canceled)) - } else { - this.inner.borrow().rx_task.register(cx.waker()); - Poll::Pending - } - } -} - -/// Futures-aware, pool of one-shot's. -pub struct Pool(Rc>>>); - -bitflags::bitflags! { - pub struct Flags: u8 { - const SENDER = 0b0000_0001; - const RECEIVER = 0b0000_0010; - } -} - -#[derive(Debug)] -struct PoolInner { - flags: Flags, - value: Option, - waker: LocalWaker, -} - -impl Pool { - pub fn channel(&mut self) -> (PSender, PReceiver) { - let token = self.0.borrow_mut().insert(PoolInner { - flags: Flags::all(), - value: None, - waker: LocalWaker::default(), - }); - - ( - PSender { - token, - inner: self.0.clone(), - }, - PReceiver { - token, - inner: self.0.clone(), - }, - ) - } -} - -impl Clone for Pool { - fn clone(&self) -> Self { - Pool(self.0.clone()) - } -} - -/// Represents the completion half of a oneshot through which the result of a -/// computation is signaled. -#[derive(Debug)] -pub struct PSender { - token: usize, - inner: Rc>>>, -} - -/// A future representing the completion of a computation happening elsewhere in -/// memory. -#[derive(Debug)] -#[must_use = "futures do nothing unless polled"] -pub struct PReceiver { - token: usize, - inner: Rc>>>, -} - -// The one-shots do not ever project Pin to the inner T -impl Unpin for PReceiver {} -impl Unpin for PSender {} - -impl PSender { - /// Completes this oneshot with a successful result. - /// - /// This function will consume `self` and indicate to the other end, the - /// `Receiver`, that the error provided is the result of the computation this - /// represents. - /// - /// If the value is successfully enqueued for the remote end to receive, - /// then `Ok(())` is returned. If the receiving end was dropped before - /// this function was called, however, then `Err` is returned with the value - /// provided. - pub fn send(self, val: T) -> Result<(), T> { - let mut inner = self.inner.borrow_mut(); - let inner = unsafe { inner.get_unchecked_mut(self.token) }; - - if inner.flags.contains(Flags::RECEIVER) { - inner.value = Some(val); - inner.waker.wake(); - Ok(()) - } else { - Err(val) - } - } - - /// Tests to see whether this `Sender`'s corresponding `Receiver` - /// has gone away. - pub fn is_canceled(&self) -> bool { - !unsafe { self.inner.borrow().get_unchecked(self.token) } - .flags - .contains(Flags::RECEIVER) - } -} - -impl Drop for PSender { - fn drop(&mut self) { - let mut inner = self.inner.borrow_mut(); - let inner_token = unsafe { inner.get_unchecked_mut(self.token) }; - if inner_token.flags.contains(Flags::RECEIVER) { - inner_token.waker.wake(); - inner_token.flags.remove(Flags::SENDER); - } else { - inner.remove(self.token); - } - } -} - -impl Drop for PReceiver { - fn drop(&mut self) { - let mut inner = self.inner.borrow_mut(); - let inner_token = unsafe { inner.get_unchecked_mut(self.token) }; - if inner_token.flags.contains(Flags::SENDER) { - inner_token.flags.remove(Flags::RECEIVER); - } else { - inner.remove(self.token); - } - } -} - -impl Future for PReceiver { - type Output = Result; - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let this = self.get_mut(); - let mut inner = this.inner.borrow_mut(); - let inner = unsafe { inner.get_unchecked_mut(this.token) }; - - // If we've got a value, then skip the logic below as we're done. - if let Some(val) = inner.value.take() { - return Poll::Ready(Ok(val)); - } - - // Check if sender is dropped and return error if it is. - if !inner.flags.contains(Flags::SENDER) { - Poll::Ready(Err(Canceled)) - } else { - inner.waker.register(cx.waker()); - Poll::Pending - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use futures_util::future::lazy; - - #[actix_rt::test] - async fn test_oneshot() { - let (tx, rx) = channel(); - tx.send("test").unwrap(); - assert_eq!(rx.await.unwrap(), "test"); - - let (tx, rx) = channel(); - assert!(!tx.is_canceled()); - drop(rx); - assert!(tx.is_canceled()); - assert!(tx.send("test").is_err()); - - let (tx, rx) = channel::<&'static str>(); - drop(tx); - assert!(rx.await.is_err()); - - let (tx, mut rx) = channel::<&'static str>(); - assert_eq!(lazy(|cx| Pin::new(&mut rx).poll(cx)).await, Poll::Pending); - tx.send("test").unwrap(); - assert_eq!(rx.await.unwrap(), "test"); - - let (tx, mut rx) = channel::<&'static str>(); - assert_eq!(lazy(|cx| Pin::new(&mut rx).poll(cx)).await, Poll::Pending); - drop(tx); - assert!(rx.await.is_err()); - } - - #[actix_rt::test] - async fn test_pool() { - let (tx, rx) = pool().channel(); - tx.send("test").unwrap(); - assert_eq!(rx.await.unwrap(), "test"); - - let (tx, rx) = pool().channel(); - assert!(!tx.is_canceled()); - drop(rx); - assert!(tx.is_canceled()); - assert!(tx.send("test").is_err()); - - let (tx, rx) = pool::<&'static str>().channel(); - drop(tx); - assert!(rx.await.is_err()); - - let (tx, mut rx) = pool::<&'static str>().channel(); - assert_eq!(lazy(|cx| Pin::new(&mut rx).poll(cx)).await, Poll::Pending); - tx.send("test").unwrap(); - assert_eq!(rx.await.unwrap(), "test"); - - let (tx, mut rx) = pool::<&'static str>().channel(); - assert_eq!(lazy(|cx| Pin::new(&mut rx).poll(cx)).await, Poll::Pending); - drop(tx); - assert!(rx.await.is_err()); - } -} diff --git a/actix-utils/src/order.rs b/actix-utils/src/order.rs deleted file mode 100644 index 2d11b491..00000000 --- a/actix-utils/src/order.rs +++ /dev/null @@ -1,283 +0,0 @@ -use std::collections::VecDeque; -use std::convert::Infallible; -use std::fmt; -use std::future::Future; -use std::marker::PhantomData; -use std::pin::Pin; -use std::rc::Rc; -use std::task::{Context, Poll}; - -use actix_service::{IntoService, Service, Transform}; -use futures_util::future::{ok, Ready}; - -use crate::oneshot; -use crate::task::LocalWaker; - -struct Record { - rx: oneshot::Receiver>, - tx: oneshot::Sender>, -} - -/// Timeout error -pub enum InOrderError { - /// Service error - Service(E), - /// Service call dropped - Disconnected, -} - -impl From for InOrderError { - fn from(err: E) -> Self { - InOrderError::Service(err) - } -} - -impl fmt::Debug for InOrderError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - InOrderError::Service(e) => write!(f, "InOrderError::Service({:?})", e), - InOrderError::Disconnected => write!(f, "InOrderError::Disconnected"), - } - } -} - -impl fmt::Display for InOrderError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - InOrderError::Service(e) => e.fmt(f), - InOrderError::Disconnected => write!(f, "InOrder service disconnected"), - } - } -} - -/// InOrder - The service will yield responses as they become available, -/// in the order that their originating requests were submitted to the service. -pub struct InOrder { - _t: PhantomData, -} - -impl InOrder -where - S: Service, - S::Response: 'static, - S::Future: 'static, - S::Error: 'static, -{ - pub fn new() -> Self { - Self { _t: PhantomData } - } - - pub fn service(service: S) -> InOrderService { - InOrderService::new(service) - } -} - -impl Default for InOrder -where - S: Service, - S::Response: 'static, - S::Future: 'static, - S::Error: 'static, -{ - fn default() -> Self { - Self::new() - } -} - -impl Transform for InOrder -where - S: Service, - S::Response: 'static, - S::Future: 'static, - S::Error: 'static, -{ - type Request = S::Request; - type Response = S::Response; - type Error = InOrderError; - type InitError = Infallible; - type Transform = InOrderService; - type Future = Ready>; - - fn new_transform(&self, service: S) -> Self::Future { - ok(InOrderService::new(service)) - } -} - -pub struct InOrderService { - service: S, - waker: Rc, - acks: VecDeque>, -} - -impl InOrderService -where - S: Service, - S::Response: 'static, - S::Future: 'static, - S::Error: 'static, -{ - pub fn new(service: U) -> Self - where - U: IntoService, - { - Self { - service: service.into_service(), - acks: VecDeque::new(), - waker: Rc::new(LocalWaker::new()), - } - } -} - -impl Service for InOrderService -where - S: Service, - S::Response: 'static, - S::Future: 'static, - S::Error: 'static, -{ - type Request = S::Request; - type Response = S::Response; - type Error = InOrderError; - type Future = InOrderServiceResponse; - - fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { - // poll_ready could be called from different task - self.waker.register(cx.waker()); - - // check acks - while !self.acks.is_empty() { - let rec = self.acks.front_mut().unwrap(); - match Pin::new(&mut rec.rx).poll(cx) { - Poll::Ready(Ok(res)) => { - let rec = self.acks.pop_front().unwrap(); - let _ = rec.tx.send(res); - } - Poll::Pending => break, - Poll::Ready(Err(oneshot::Canceled)) => { - return Poll::Ready(Err(InOrderError::Disconnected)) - } - } - } - - // check nested service - if self - .service - .poll_ready(cx) - .map_err(InOrderError::Service)? - .is_pending() - { - Poll::Pending - } else { - Poll::Ready(Ok(())) - } - } - - fn call(&mut self, request: S::Request) -> Self::Future { - let (tx1, rx1) = oneshot::channel(); - let (tx2, rx2) = oneshot::channel(); - self.acks.push_back(Record { rx: rx1, tx: tx2 }); - - let waker = self.waker.clone(); - let fut = self.service.call(request); - actix_rt::spawn(async move { - let res = fut.await; - waker.wake(); - let _ = tx1.send(res); - }); - - InOrderServiceResponse { rx: rx2 } - } -} - -#[doc(hidden)] -pub struct InOrderServiceResponse { - rx: oneshot::Receiver>, -} - -impl Future for InOrderServiceResponse { - type Output = Result>; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - match Pin::new(&mut self.rx).poll(cx) { - Poll::Pending => Poll::Pending, - Poll::Ready(Ok(Ok(res))) => Poll::Ready(Ok(res)), - Poll::Ready(Ok(Err(e))) => Poll::Ready(Err(e.into())), - Poll::Ready(Err(_)) => Poll::Ready(Err(InOrderError::Disconnected)), - } - } -} - -#[cfg(test)] -mod tests { - - use std::task::{Context, Poll}; - use std::time::Duration; - - use super::*; - use actix_service::Service; - use futures_channel::oneshot; - use futures_util::future::{lazy, poll_fn, FutureExt, LocalBoxFuture}; - - struct Srv; - - impl Service for Srv { - type Request = oneshot::Receiver; - type Response = usize; - type Error = (); - type Future = LocalBoxFuture<'static, Result>; - - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } - - fn call(&mut self, req: oneshot::Receiver) -> Self::Future { - req.map(|res| res.map_err(|_| ())).boxed_local() - } - } - - #[actix_rt::test] - async fn test_in_order() { - let (tx1, rx1) = oneshot::channel(); - let (tx2, rx2) = oneshot::channel(); - let (tx3, rx3) = oneshot::channel(); - let (tx_stop, rx_stop) = oneshot::channel(); - - let h = std::thread::spawn(move || { - let rx1 = rx1; - let rx2 = rx2; - let rx3 = rx3; - let tx_stop = tx_stop; - actix_rt::System::new("test").block_on(async { - let mut srv = InOrderService::new(Srv); - - let _ = lazy(|cx| srv.poll_ready(cx)).await; - let res1 = srv.call(rx1); - let res2 = srv.call(rx2); - let res3 = srv.call(rx3); - - actix_rt::spawn(async move { - poll_fn(|cx| { - let _ = srv.poll_ready(cx); - Poll::<()>::Pending - }) - .await; - }); - - assert_eq!(res1.await.unwrap(), 1); - assert_eq!(res2.await.unwrap(), 2); - assert_eq!(res3.await.unwrap(), 3); - - let _ = tx_stop.send(()); - actix_rt::System::current().stop(); - }); - }); - - let _ = tx3.send(3); - std::thread::sleep(Duration::from_millis(50)); - let _ = tx2.send(2); - let _ = tx1.send(1); - - let _ = rx_stop.await; - let _ = h.join(); - } -} diff --git a/actix-utils/src/stream.rs b/actix-utils/src/stream.rs deleted file mode 100644 index 72e9e019..00000000 --- a/actix-utils/src/stream.rs +++ /dev/null @@ -1,76 +0,0 @@ -use std::future::Future; -use std::pin::Pin; -use std::task::{Context, Poll}; - -use actix_service::{IntoService, Service}; -use futures_util::{stream::Stream, FutureExt}; - -use crate::mpsc; - -#[pin_project::pin_project] -pub struct Dispatcher -where - S: Stream, - T: Service + 'static, -{ - #[pin] - stream: S, - service: T, - err_rx: mpsc::Receiver, - err_tx: mpsc::Sender, -} - -impl Dispatcher -where - S: Stream, - T: Service + 'static, -{ - pub fn new(stream: S, service: F) -> Self - where - F: IntoService, - { - let (err_tx, err_rx) = mpsc::channel(); - Dispatcher { - err_rx, - err_tx, - stream, - service: service.into_service(), - } - } -} - -impl Future for Dispatcher -where - S: Stream, - T: Service + 'static, -{ - type Output = Result<(), T::Error>; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let mut this = self.as_mut().project(); - - if let Poll::Ready(Some(e)) = Pin::new(&mut this.err_rx).poll_next(cx) { - return Poll::Ready(Err(e)); - } - - loop { - return match this.service.poll_ready(cx)? { - Poll::Ready(_) => match this.stream.poll_next(cx) { - Poll::Ready(Some(item)) => { - let stop = this.err_tx.clone(); - actix_rt::spawn(this.service.call(item).map(move |res| { - if let Err(e) = res { - let _ = stop.send(e); - } - })); - this = self.as_mut().project(); - continue; - } - Poll::Pending => Poll::Pending, - Poll::Ready(None) => Poll::Ready(Ok(())), - }, - Poll::Pending => Poll::Pending, - }; - } - } -} diff --git a/actix-utils/src/task.rs b/actix-utils/src/task.rs index cb32eb8d..8f85f5e4 100644 --- a/actix-utils/src/task.rs +++ b/actix-utils/src/task.rs @@ -1,7 +1,7 @@ -use std::cell::UnsafeCell; -use std::marker::PhantomData; -use std::task::Waker; -use std::{fmt, rc}; +use core::cell::UnsafeCell; +use core::fmt; +use core::marker::PhantomData; +use core::task::Waker; /// A synchronization primitive for task wakeup. /// @@ -23,7 +23,8 @@ use std::{fmt, rc}; #[derive(Default)] pub struct LocalWaker { pub(crate) waker: UnsafeCell>, - _t: PhantomData>, + // mark LocalWaker as a !Send type. + _t: PhantomData<*const ()>, } impl LocalWaker { diff --git a/actix-utils/src/time.rs b/actix-utils/src/time.rs deleted file mode 100644 index 2ce65bc3..00000000 --- a/actix-utils/src/time.rs +++ /dev/null @@ -1,225 +0,0 @@ -use std::cell::RefCell; -use std::convert::Infallible; -use std::rc::Rc; -use std::task::{Context, Poll}; -use std::time::{self, Duration, Instant}; - -use actix_rt::time::delay_for; -use actix_service::{Service, ServiceFactory}; -use futures_util::future::{ok, ready, FutureExt, Ready}; - -#[derive(Clone, Debug)] -pub struct LowResTime(Rc>); - -#[derive(Debug)] -struct Inner { - resolution: Duration, - current: Option, -} - -impl Inner { - fn new(resolution: Duration) -> Self { - Inner { - resolution, - current: None, - } - } -} - -impl LowResTime { - pub fn with(resolution: Duration) -> LowResTime { - LowResTime(Rc::new(RefCell::new(Inner::new(resolution)))) - } - - pub fn timer(&self) -> LowResTimeService { - LowResTimeService(self.0.clone()) - } -} - -impl Default for LowResTime { - fn default() -> Self { - LowResTime(Rc::new(RefCell::new(Inner::new(Duration::from_secs(1))))) - } -} - -impl ServiceFactory for LowResTime { - type Request = (); - type Response = Instant; - type Error = Infallible; - type InitError = Infallible; - type Config = (); - type Service = LowResTimeService; - type Future = Ready>; - - fn new_service(&self, _: ()) -> Self::Future { - ok(self.timer()) - } -} - -#[derive(Clone, Debug)] -pub struct LowResTimeService(Rc>); - -impl LowResTimeService { - pub fn with(resolution: Duration) -> LowResTimeService { - LowResTimeService(Rc::new(RefCell::new(Inner::new(resolution)))) - } - - /// Get current time. This function has to be called from - /// future's poll method, otherwise it panics. - pub fn now(&self) -> Instant { - let cur = self.0.borrow().current; - if let Some(cur) = cur { - cur - } else { - let now = Instant::now(); - let inner = self.0.clone(); - let interval = { - let mut b = inner.borrow_mut(); - b.current = Some(now); - b.resolution - }; - - actix_rt::spawn(delay_for(interval).then(move |_| { - inner.borrow_mut().current.take(); - ready(()) - })); - now - } - } -} - -impl Service for LowResTimeService { - type Request = (); - type Response = Instant; - type Error = Infallible; - type Future = Ready>; - - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } - - fn call(&mut self, _: ()) -> Self::Future { - ok(self.now()) - } -} - -#[derive(Clone, Debug)] -pub struct SystemTime(Rc>); - -#[derive(Debug)] -struct SystemTimeInner { - resolution: Duration, - current: Option, -} - -impl SystemTimeInner { - fn new(resolution: Duration) -> Self { - SystemTimeInner { - resolution, - current: None, - } - } -} - -#[derive(Clone, Debug)] -pub struct SystemTimeService(Rc>); - -impl SystemTimeService { - pub fn with(resolution: Duration) -> SystemTimeService { - SystemTimeService(Rc::new(RefCell::new(SystemTimeInner::new(resolution)))) - } - - /// Get current time. This function has to be called from - /// future's poll method, otherwise it panics. - pub fn now(&self) -> time::SystemTime { - let cur = self.0.borrow().current; - if let Some(cur) = cur { - cur - } else { - let now = time::SystemTime::now(); - let inner = self.0.clone(); - let interval = { - let mut b = inner.borrow_mut(); - b.current = Some(now); - b.resolution - }; - - actix_rt::spawn(delay_for(interval).then(move |_| { - inner.borrow_mut().current.take(); - ready(()) - })); - now - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use std::time::{Duration, SystemTime}; - - /// State Under Test: Two calls of `SystemTimeService::now()` return the same value if they are done within resolution interval of `SystemTimeService`. - /// - /// Expected Behavior: Two back-to-back calls of `SystemTimeService::now()` return the same value. - #[actix_rt::test] - async fn system_time_service_time_does_not_immediately_change() { - let resolution = Duration::from_millis(50); - - let time_service = SystemTimeService::with(resolution); - assert_eq!(time_service.now(), time_service.now()); - } - - /// State Under Test: Two calls of `LowResTimeService::now()` return the same value if they are done within resolution interval of `SystemTimeService`. - /// - /// Expected Behavior: Two back-to-back calls of `LowResTimeService::now()` return the same value. - #[actix_rt::test] - async fn low_res_time_service_time_does_not_immediately_change() { - let resolution = Duration::from_millis(50); - let time_service = LowResTimeService::with(resolution); - assert_eq!(time_service.now(), time_service.now()); - } - - /// State Under Test: `SystemTimeService::now()` updates returned value every resolution period. - /// - /// Expected Behavior: Two calls of `LowResTimeService::now()` made in subsequent resolution interval return different values - /// and second value is greater than the first one at least by a resolution interval. - #[actix_rt::test] - async fn system_time_service_time_updates_after_resolution_interval() { - let resolution = Duration::from_millis(100); - let wait_time = Duration::from_millis(300); - - let time_service = SystemTimeService::with(resolution); - - let first_time = time_service - .now() - .duration_since(SystemTime::UNIX_EPOCH) - .unwrap(); - - delay_for(wait_time).await; - - let second_time = time_service - .now() - .duration_since(SystemTime::UNIX_EPOCH) - .unwrap(); - - assert!(second_time - first_time >= wait_time); - } - - /// State Under Test: `LowResTimeService::now()` updates returned value every resolution period. - /// - /// Expected Behavior: Two calls of `LowResTimeService::now()` made in subsequent resolution interval return different values - /// and second value is greater than the first one at least by a resolution interval. - #[actix_rt::test] - async fn low_res_time_service_time_updates_after_resolution_interval() { - let resolution = Duration::from_millis(100); - let wait_time = Duration::from_millis(300); - let time_service = LowResTimeService::with(resolution); - - let first_time = time_service.now(); - - delay_for(wait_time).await; - - let second_time = time_service.now(); - assert!(second_time - first_time >= wait_time); - } -} diff --git a/actix-utils/src/timeout.rs b/actix-utils/src/timeout.rs index f1d30f19..f3489b85 100644 --- a/actix-utils/src/timeout.rs +++ b/actix-utils/src/timeout.rs @@ -2,15 +2,14 @@ //! //! If the response does not complete within the specified timeout, the response //! will be aborted. -use std::future::Future; -use std::marker::PhantomData; -use std::pin::Pin; -use std::task::{Context, Poll}; -use std::{fmt, time}; +use core::future::Future; +use core::marker::PhantomData; +use core::pin::Pin; +use core::task::{Context, Poll}; +use core::{fmt, time}; use actix_rt::time::{delay_for, Delay}; use actix_service::{IntoService, Service, Transform}; -use futures_util::future::{ok, Ready}; /// Applies a timeout to requests. #[derive(Debug)] @@ -85,15 +84,35 @@ where type Request = S::Request; type Response = S::Response; type Error = TimeoutError; - type InitError = E; type Transform = TimeoutService; - type Future = Ready>; + type InitError = E; + type Future = TimeoutFuture; fn new_transform(&self, service: S) -> Self::Future { - ok(TimeoutService { + let service = TimeoutService { service, timeout: self.timeout, - }) + }; + + TimeoutFuture { + service: Some(service), + _err: PhantomData, + } + } +} + +pub struct TimeoutFuture { + service: Option, + _err: PhantomData, +} + +impl Unpin for TimeoutFuture {} + +impl Future for TimeoutFuture { + type Output = Result; + + fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll { + Poll::Ready(Ok(self.get_mut().service.take().unwrap())) } } @@ -140,13 +159,14 @@ where } } -/// `TimeoutService` response future -#[pin_project::pin_project] -#[derive(Debug)] -pub struct TimeoutServiceResponse { - #[pin] - fut: T::Future, - sleep: Delay, +pin_project_lite::pin_project! { + /// `TimeoutService` response future + #[derive(Debug)] + pub struct TimeoutServiceResponse { + #[pin] + fut: T::Future, + sleep: Delay, + } } impl Future for TimeoutServiceResponse @@ -156,20 +176,20 @@ where type Output = Result>; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let mut this = self.project(); + let this = self.project(); // First, try polling the future - match this.fut.poll(cx) { - Poll::Ready(Ok(v)) => return Poll::Ready(Ok(v)), - Poll::Ready(Err(e)) => return Poll::Ready(Err(TimeoutError::Service(e))), - Poll::Pending => {} + if let Poll::Ready(res) = this.fut.poll(cx) { + return match res { + Ok(v) => Poll::Ready(Ok(v)), + Err(e) => Poll::Ready(Err(TimeoutError::Service(e))), + }; } // Now check the sleep - match Pin::new(&mut this.sleep).poll(cx) { - Poll::Pending => Poll::Pending, - Poll::Ready(_) => Poll::Ready(Err(TimeoutError::Timeout)), - } + Pin::new(this.sleep) + .poll(cx) + .map(|_| Err(TimeoutError::Timeout)) } } From 518bf3f6a68345ec473090b1c3834073de78d554 Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Sun, 27 Dec 2020 07:26:02 +0800 Subject: [PATCH 02/73] remove RUNNING Q PENDING thread locals from actix-rt (#207) --- actix-rt/CHANGES.md | 12 ++ actix-rt/Cargo.toml | 7 +- actix-rt/src/arbiter.rs | 218 ++++++++++++---------------- actix-rt/src/builder.rs | 67 ++++----- actix-rt/src/lib.rs | 11 +- actix-rt/src/runtime.rs | 6 +- actix-rt/src/system.rs | 2 +- actix-rt/tests/integration_tests.rs | 102 +++++++------ actix-server/tests/test_server.rs | 107 +++++++------- actix-testing/src/lib.rs | 15 +- 10 files changed, 273 insertions(+), 274 deletions(-) diff --git a/actix-rt/CHANGES.md b/actix-rt/CHANGES.md index 9b5fb636..c3479db1 100644 --- a/actix-rt/CHANGES.md +++ b/actix-rt/CHANGES.md @@ -6,6 +6,18 @@ * Add `System::attach_to_tokio` method. [#173] +### Changed + +* Remove `'static` lifetime requirement for `Runtime::block_on` and `SystemRunner::block_on`. + Remove `'static` lifetime requirement for `System::run` and `Builder::run`. + `Arbiter::spawn` would panic when `System` is not in scope. [#207] + +### Fixed + +* Fix work load issue by removing `PENDDING` thread local. [#207] + +[#207]: https://github.com/actix/actix-net/pull/207 + ## [1.1.1] - 2020-04-30 ### Fixed diff --git a/actix-rt/Cargo.toml b/actix-rt/Cargo.toml index 68bca814..57710a7f 100644 --- a/actix-rt/Cargo.toml +++ b/actix-rt/Cargo.toml @@ -17,11 +17,10 @@ path = "src/lib.rs" [dependencies] actix-macros = "0.1.0" -copyless = "0.1.4" -futures-channel = "0.3.4" -futures-util = { version = "0.3.4", default-features = false, features = ["alloc"] } -smallvec = "1" + +futures-channel = "0.3.7" tokio = { version = "0.2.6", default-features = false, features = ["rt-core", "rt-util", "io-driver", "tcp", "uds", "udp", "time", "signal", "stream"] } [dev-dependencies] +futures-util = { version = "0.3.7", default-features = false, features = ["alloc"] } tokio = { version = "0.2.6", features = ["full"] } diff --git a/actix-rt/src/arbiter.rs b/actix-rt/src/arbiter.rs index 295d2624..3fe81b99 100644 --- a/actix-rt/src/arbiter.rs +++ b/actix-rt/src/arbiter.rs @@ -1,6 +1,7 @@ use std::any::{Any, TypeId}; -use std::cell::{Cell, RefCell}; +use std::cell::RefCell; use std::collections::HashMap; +use std::future::Future; use std::pin::Pin; use std::sync::atomic::{AtomicUsize, Ordering}; use std::task::{Context, Poll}; @@ -8,24 +9,23 @@ use std::{fmt, thread}; use futures_channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender}; use futures_channel::oneshot::{channel, Canceled, Sender}; -use futures_util::{ - future::{self, Future, FutureExt}, - stream::Stream, -}; +// use futures_util::stream::FuturesUnordered; +// use tokio::task::JoinHandle; +// use tokio::stream::StreamExt; +use tokio::stream::Stream; +use tokio::task::LocalSet; use crate::runtime::Runtime; use crate::system::System; -use copyless::BoxHelper; - -use smallvec::SmallVec; -pub use tokio::task::JoinHandle; - thread_local!( static ADDR: RefCell> = RefCell::new(None); - static RUNNING: Cell = Cell::new(false); - static Q: RefCell>>>> = RefCell::new(Vec::new()); - static PENDING: RefCell; 8]>> = RefCell::new(SmallVec::new()); + // TODO: Commented out code are for Arbiter::local_join function. + // It can be safely removed if this function is not used in actix-*. + // + // /// stores join handle for spawned async tasks. + // static HANDLE: RefCell>> = + // RefCell::new(FuturesUnordered::new()); static STORAGE: RefCell>> = RefCell::new(HashMap::new()); ); @@ -69,14 +69,14 @@ impl Default for Arbiter { } impl Arbiter { - pub(crate) fn new_system() -> Self { + pub(crate) fn new_system(local: &LocalSet) -> Self { let (tx, rx) = unbounded(); let arb = Arbiter::with_sender(tx); ADDR.with(|cell| *cell.borrow_mut() = Some(arb.clone())); - RUNNING.with(|cell| cell.set(false)); STORAGE.with(|cell| cell.borrow_mut().clear()); - Arbiter::spawn(ArbiterController { stop: None, rx }); + + local.spawn_local(ArbiterController { rx }); arb } @@ -91,8 +91,9 @@ impl Arbiter { } /// Check if current arbiter is running. + #[deprecated(note = "Thread local variables for running state of Arbiter is removed")] pub fn is_running() -> bool { - RUNNING.with(|cell| cell.get()) + false } /// Stop arbiter from continuing it's event loop. @@ -106,69 +107,47 @@ impl Arbiter { let id = COUNT.fetch_add(1, Ordering::Relaxed); let name = format!("actix-rt:worker:{}", id); let sys = System::current(); - let (arb_tx, arb_rx) = unbounded(); - let arb_tx2 = arb_tx.clone(); + let (tx, rx) = unbounded(); let handle = thread::Builder::new() .name(name.clone()) - .spawn(move || { - let mut rt = Runtime::new().expect("Can not create Runtime"); - let arb = Arbiter::with_sender(arb_tx); + .spawn({ + let tx = tx.clone(); + move || { + let mut rt = Runtime::new().expect("Can not create Runtime"); + let arb = Arbiter::with_sender(tx); - let (stop, stop_rx) = channel(); - RUNNING.with(|cell| cell.set(true)); - STORAGE.with(|cell| cell.borrow_mut().clear()); + STORAGE.with(|cell| cell.borrow_mut().clear()); - System::set_current(sys); + System::set_current(sys); - // start arbiter controller - rt.spawn(ArbiterController { - stop: Some(stop), - rx: arb_rx, - }); - ADDR.with(|cell| *cell.borrow_mut() = Some(arb.clone())); + ADDR.with(|cell| *cell.borrow_mut() = Some(arb.clone())); - // register arbiter - let _ = System::current() - .sys() - .unbounded_send(SystemCommand::RegisterArbiter(id, arb)); + // register arbiter + let _ = System::current() + .sys() + .unbounded_send(SystemCommand::RegisterArbiter(id, arb)); - // run loop - let _ = rt.block_on(stop_rx).unwrap_or(1); + // start arbiter controller + // run loop + rt.block_on(ArbiterController { rx }); - // unregister arbiter - let _ = System::current() - .sys() - .unbounded_send(SystemCommand::UnregisterArbiter(id)); + // unregister arbiter + let _ = System::current() + .sys() + .unbounded_send(SystemCommand::UnregisterArbiter(id)); + } }) .unwrap_or_else(|err| { panic!("Cannot spawn an arbiter's thread {:?}: {:?}", &name, err) }); Arbiter { - sender: arb_tx2, + sender: tx, thread_handle: Some(handle), } } - pub(crate) fn run_system(rt: Option<&Runtime>) { - RUNNING.with(|cell| cell.set(true)); - Q.with(|cell| { - let mut v = cell.borrow_mut(); - for fut in v.drain(..) { - if let Some(rt) = rt { - rt.spawn(fut); - } else { - tokio::task::spawn_local(fut); - } - } - }); - } - - pub(crate) fn stop_system() { - RUNNING.with(|cell| cell.set(false)); - } - /// Spawn a future on the current thread. This does not create a new Arbiter /// or Arbiter address, it is simply a helper for spawning futures on the current /// thread. @@ -176,26 +155,12 @@ impl Arbiter { where F: Future + 'static, { - RUNNING.with(move |cell| { - if cell.get() { - // Spawn the future on running executor - let len = PENDING.with(move |cell| { - let mut p = cell.borrow_mut(); - p.push(tokio::task::spawn_local(future)); - p.len() - }); - if len > 7 { - // Before reaching the inline size - tokio::task::spawn_local(CleanupPending); - } - } else { - // Box the future and push it to the queue, this results in double boxing - // because the executor boxes the future again, but works for now - Q.with(move |cell| { - cell.borrow_mut().push(Pin::from(Box::alloc().init(future))) - }); - } - }); + // HANDLE.with(|handle| { + // let handle = handle.borrow(); + // handle.push(tokio::task::spawn_local(future)); + // }); + // let _ = tokio::task::spawn_local(CleanupPending); + let _ = tokio::task::spawn_local(future); } /// Executes a future on the current thread. This does not create a new Arbiter @@ -206,7 +171,9 @@ impl Arbiter { F: FnOnce() -> R + 'static, R: Future + 'static, { - Arbiter::spawn(future::lazy(|_| f()).flatten()) + Arbiter::spawn(async { + f(); + }) } /// Send a future to the Arbiter's thread, and spawn it. @@ -313,40 +280,33 @@ impl Arbiter { /// Returns a future that will be completed once all currently spawned futures /// have completed. - pub fn local_join() -> impl Future { - PENDING.with(move |cell| { - let current = cell.replace(SmallVec::new()); - future::join_all(current).map(|_| ()) - }) + #[deprecated(since = "1.2.0", note = "Arbiter::local_join function is removed.")] + pub async fn local_join() { + // let handle = HANDLE.with(|fut| std::mem::take(&mut *fut.borrow_mut())); + // async move { + // handle.collect::>().await; + // } + unimplemented!("Arbiter::local_join function is removed.") } } -/// Future used for cleaning-up already finished `JoinHandle`s -/// from the `PENDING` list so the vector doesn't grow indefinitely -struct CleanupPending; - -impl Future for CleanupPending { - type Output = (); - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - PENDING.with(move |cell| { - let mut pending = cell.borrow_mut(); - let mut i = 0; - while i != pending.len() { - if Pin::new(&mut pending[i]).poll(cx).is_ready() { - pending.remove(i); - } else { - i += 1; - } - } - }); - - Poll::Ready(()) - } -} +// /// Future used for cleaning-up already finished `JoinHandle`s +// /// from the `PENDING` list so the vector doesn't grow indefinitely +// struct CleanupPending; +// +// impl Future for CleanupPending { +// type Output = (); +// +// fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { +// HANDLE.with(move |handle| { +// recycle_join_handle(&mut *handle.borrow_mut(), cx); +// }); +// +// Poll::Ready(()) +// } +// } struct ArbiterController { - stop: Option>, rx: UnboundedReceiver, } @@ -371,22 +331,14 @@ impl Future for ArbiterController { match Pin::new(&mut self.rx).poll_next(cx) { Poll::Ready(None) => return Poll::Ready(()), Poll::Ready(Some(item)) => match item { - ArbiterCommand::Stop => { - if let Some(stop) = self.stop.take() { - let _ = stop.send(0); - }; - return Poll::Ready(()); - } + ArbiterCommand::Stop => return Poll::Ready(()), ArbiterCommand::Execute(fut) => { - let len = PENDING.with(move |cell| { - let mut p = cell.borrow_mut(); - p.push(tokio::task::spawn_local(fut)); - p.len() - }); - if len > 7 { - // Before reaching the inline size - tokio::task::spawn_local(CleanupPending); - } + // HANDLE.with(|handle| { + // let mut handle = handle.borrow_mut(); + // handle.push(tokio::task::spawn_local(fut)); + // recycle_join_handle(&mut *handle, cx); + // }); + tokio::task::spawn_local(fut); } ArbiterCommand::ExecuteFn(f) => { f.call_box(); @@ -398,6 +350,20 @@ impl Future for ArbiterController { } } +// fn recycle_join_handle(handle: &mut FuturesUnordered>, cx: &mut Context<'_>) { +// let _ = Pin::new(&mut *handle).poll_next(cx); +// +// // Try to recycle more join handles and free up memory. +// // +// // this is a guess. The yield limit for FuturesUnordered is 32. +// // So poll an extra 3 times would make the total poll below 128. +// if handle.len() > 64 { +// (0..3).for_each(|_| { +// let _ = Pin::new(&mut *handle).poll_next(cx); +// }) +// } +// } + #[derive(Debug)] pub(crate) enum SystemCommand { Exit(i32), diff --git a/actix-rt/src/builder.rs b/actix-rt/src/builder.rs index f4d9b1bf..83aed064 100644 --- a/actix-rt/src/builder.rs +++ b/actix-rt/src/builder.rs @@ -1,9 +1,9 @@ use std::borrow::Cow; +use std::future::Future; use std::io; use futures_channel::mpsc::unbounded; use futures_channel::oneshot::{channel, Receiver}; -use futures_util::future::{lazy, Future, FutureExt}; use tokio::task::LocalSet; use crate::arbiter::{Arbiter, SystemArbiter}; @@ -65,7 +65,7 @@ impl Builder { /// Function `f` get called within tokio runtime context. pub fn run(self, f: F) -> io::Result<()> where - F: FnOnce() + 'static, + F: FnOnce(), { self.create_runtime(f).run() } @@ -74,7 +74,8 @@ impl Builder { let (stop_tx, stop) = channel(); let (sys_sender, sys_receiver) = unbounded(); - let system = System::construct(sys_sender, Arbiter::new_system(), self.stop_on_panic); + let system = + System::construct(sys_sender, Arbiter::new_system(local), self.stop_on_panic); // system arbiter let arb = SystemArbiter::new(stop_tx, sys_receiver); @@ -87,21 +88,26 @@ impl Builder { fn create_runtime(self, f: F) -> SystemRunner where - F: FnOnce() + 'static, + F: FnOnce(), { let (stop_tx, stop) = channel(); let (sys_sender, sys_receiver) = unbounded(); - let system = System::construct(sys_sender, Arbiter::new_system(), self.stop_on_panic); + let mut rt = Runtime::new().unwrap(); + + let system = System::construct( + sys_sender, + Arbiter::new_system(rt.local()), + self.stop_on_panic, + ); // system arbiter let arb = SystemArbiter::new(stop_tx, sys_receiver); - let mut rt = Runtime::new().unwrap(); rt.spawn(arb); // init system arbiter and run configuration method - rt.block_on(lazy(move |_| f())); + rt.block_on(async { f() }); SystemRunner { rt, stop, system } } @@ -120,27 +126,21 @@ impl AsyncSystemRunner { let AsyncSystemRunner { stop, .. } = self; // run loop - lazy(|_| { - Arbiter::run_system(None); - async { - let res = match stop.await { - Ok(code) => { - if code != 0 { - Err(io::Error::new( - io::ErrorKind::Other, - format!("Non-zero exit code: {}", code), - )) - } else { - Ok(()) - } + async { + match stop.await { + Ok(code) => { + if code != 0 { + Err(io::Error::new( + io::ErrorKind::Other, + format!("Non-zero exit code: {}", code), + )) + } else { + Ok(()) } - Err(e) => Err(io::Error::new(io::ErrorKind::Other, e)), - }; - Arbiter::stop_system(); - res + } + Err(e) => Err(io::Error::new(io::ErrorKind::Other, e)), } - }) - .flatten() + } } } @@ -160,8 +160,7 @@ impl SystemRunner { let SystemRunner { mut rt, stop, .. } = self; // run loop - Arbiter::run_system(Some(&rt)); - let result = match rt.block_on(stop) { + match rt.block_on(stop) { Ok(code) => { if code != 0 { Err(io::Error::new( @@ -173,19 +172,15 @@ impl SystemRunner { } } Err(e) => Err(io::Error::new(io::ErrorKind::Other, e)), - }; - Arbiter::stop_system(); - result + } } /// Execute a future and wait for result. + #[inline] pub fn block_on(&mut self, fut: F) -> O where - F: Future + 'static, + F: Future, { - Arbiter::run_system(Some(&self.rt)); - let res = self.rt.block_on(fut); - Arbiter::stop_system(); - res + self.rt.block_on(fut) } } diff --git a/actix-rt/src/lib.rs b/actix-rt/src/lib.rs index dccd9202..b2e23c0f 100644 --- a/actix-rt/src/lib.rs +++ b/actix-rt/src/lib.rs @@ -4,6 +4,8 @@ #![doc(html_logo_url = "https://actix.rs/img/logo.png")] #![doc(html_favicon_url = "https://actix.rs/favicon.ico")] +use std::future::Future; + #[cfg(not(test))] // Work around for rust-lang/rust#62127 pub use actix_macros::{main, test}; @@ -22,15 +24,12 @@ pub use self::system::System; /// # Panics /// /// This function panics if actix system is not running. +#[inline] pub fn spawn(f: F) where - F: futures_util::future::Future + 'static, + F: Future + 'static, { - if !System::is_set() { - panic!("System is not running"); - } - - Arbiter::spawn(f); + Arbiter::spawn(f) } /// Asynchronous signal handling diff --git a/actix-rt/src/runtime.rs b/actix-rt/src/runtime.rs index d3231192..7ee02b02 100644 --- a/actix-rt/src/runtime.rs +++ b/actix-rt/src/runtime.rs @@ -30,6 +30,10 @@ impl Runtime { }) } + pub(super) fn local(&self) -> &LocalSet { + &self.local + } + /// Spawn a future onto the single-threaded runtime. /// /// See [module level][mod] documentation for more details. @@ -84,7 +88,7 @@ impl Runtime { /// complete execution by calling `block_on` or `run`. pub fn block_on(&mut self, f: F) -> F::Output where - F: Future + 'static, + F: Future, { self.local.block_on(&mut self.rt, f) } diff --git a/actix-rt/src/system.rs b/actix-rt/src/system.rs index 2e1ae174..16b96439 100644 --- a/actix-rt/src/system.rs +++ b/actix-rt/src/system.rs @@ -256,7 +256,7 @@ impl System { /// Function `f` get called within tokio runtime context. pub fn run(f: F) -> io::Result<()> where - F: FnOnce() + 'static, + F: FnOnce(), { Self::builder().run(f) } diff --git a/actix-rt/tests/integration_tests.rs b/actix-rt/tests/integration_tests.rs index 8e775bab..b3265476 100644 --- a/actix-rt/tests/integration_tests.rs +++ b/actix-rt/tests/integration_tests.rs @@ -1,19 +1,5 @@ use std::time::{Duration, Instant}; -#[test] -fn start_and_stop() { - actix_rt::System::new("start_and_stop").block_on(async move { - assert!( - actix_rt::Arbiter::is_running(), - "System doesn't seem to have started" - ); - }); - assert!( - !actix_rt::Arbiter::is_running(), - "System doesn't seem to have stopped" - ); -} - #[test] fn await_for_timer() { let time = Duration::from_secs(2); @@ -76,39 +62,65 @@ fn join_another_arbiter() { ); } +// #[test] +// fn join_current_arbiter() { +// let time = Duration::from_secs(2); +// +// let instant = Instant::now(); +// actix_rt::System::new("test_join_current_arbiter").block_on(async move { +// actix_rt::spawn(async move { +// tokio::time::delay_for(time).await; +// actix_rt::Arbiter::current().stop(); +// }); +// actix_rt::Arbiter::local_join().await; +// }); +// assert!( +// instant.elapsed() >= time, +// "Join on current arbiter should wait for all spawned futures" +// ); +// +// let large_timer = Duration::from_secs(20); +// let instant = Instant::now(); +// actix_rt::System::new("test_join_current_arbiter").block_on(async move { +// actix_rt::spawn(async move { +// tokio::time::delay_for(time).await; +// actix_rt::Arbiter::current().stop(); +// }); +// let f = actix_rt::Arbiter::local_join(); +// actix_rt::spawn(async move { +// tokio::time::delay_for(large_timer).await; +// actix_rt::Arbiter::current().stop(); +// }); +// f.await; +// }); +// assert!( +// instant.elapsed() < large_timer, +// "local_join should await only for the already spawned futures" +// ); +// } + #[test] -fn join_current_arbiter() { - let time = Duration::from_secs(2); +fn non_static_block_on() { + let string = String::from("test_str"); + let str = string.as_str(); - let instant = Instant::now(); - actix_rt::System::new("test_join_current_arbiter").block_on(async move { - actix_rt::spawn(async move { - tokio::time::delay_for(time).await; - actix_rt::Arbiter::current().stop(); - }); - actix_rt::Arbiter::local_join().await; - }); - assert!( - instant.elapsed() >= time, - "Join on current arbiter should wait for all spawned futures" - ); + let mut sys = actix_rt::System::new("borrow some"); - let large_timer = Duration::from_secs(20); - let instant = Instant::now(); - actix_rt::System::new("test_join_current_arbiter").block_on(async move { - actix_rt::spawn(async move { - tokio::time::delay_for(time).await; - actix_rt::Arbiter::current().stop(); - }); - let f = actix_rt::Arbiter::local_join(); - actix_rt::spawn(async move { - tokio::time::delay_for(large_timer).await; - actix_rt::Arbiter::current().stop(); - }); - f.await; + sys.block_on(async { + actix_rt::time::delay_for(Duration::from_millis(1)).await; + assert_eq!("test_str", str); }); - assert!( - instant.elapsed() < large_timer, - "local_join should await only for the already spawned futures" - ); + + let mut rt = actix_rt::Runtime::new().unwrap(); + + rt.block_on(async { + actix_rt::time::delay_for(Duration::from_millis(1)).await; + assert_eq!("test_str", str); + }); + + actix_rt::System::run(|| { + assert_eq!("test_str", str); + actix_rt::System::current().stop(); + }) + .unwrap(); } diff --git a/actix-server/tests/test_server.rs b/actix-server/tests/test_server.rs index ce309c94..838c3cf1 100644 --- a/actix-server/tests/test_server.rs +++ b/actix-server/tests/test_server.rs @@ -22,13 +22,16 @@ fn test_bind() { let (tx, rx) = mpsc::channel(); let h = thread::spawn(move || { - let sys = actix_rt::System::new("test"); - let srv = Server::build() - .workers(1) - .disable_signals() - .bind("test", addr, move || fn_service(|_| ok::<_, ()>(()))) - .unwrap() - .start(); + let mut sys = actix_rt::System::new("test"); + + let srv = sys.block_on(lazy(|_| { + Server::build() + .workers(1) + .disable_signals() + .bind("test", addr, move || fn_service(|_| ok::<_, ()>(()))) + .unwrap() + .start() + })); let _ = tx.send((srv, actix_rt::System::current())); let _ = sys.run(); }); @@ -46,14 +49,16 @@ fn test_listen() { let (tx, rx) = mpsc::channel(); let h = thread::spawn(move || { - let sys = actix_rt::System::new("test"); + let mut sys = actix_rt::System::new("test"); let lst = net::TcpListener::bind(addr).unwrap(); - Server::build() - .disable_signals() - .workers(1) - .listen("test", lst, move || fn_service(|_| ok::<_, ()>(()))) - .unwrap() - .start(); + sys.block_on(lazy(|_| { + Server::build() + .disable_signals() + .workers(1) + .listen("test", lst, move || fn_service(|_| ok::<_, ()>(()))) + .unwrap() + .start() + })); let _ = tx.send(actix_rt::System::current()); let _ = sys.run(); }); @@ -78,19 +83,21 @@ fn test_start() { let (tx, rx) = mpsc::channel(); let h = thread::spawn(move || { - let sys = actix_rt::System::new("test"); - let srv: Server = Server::build() - .backlog(100) - .disable_signals() - .bind("test", addr, move || { - fn_service(|io: TcpStream| async move { - let mut f = Framed::new(io, BytesCodec); - f.send(Bytes::from_static(b"test")).await.unwrap(); - Ok::<_, ()>(()) + let mut sys = actix_rt::System::new("test"); + let srv = sys.block_on(lazy(|_| { + Server::build() + .backlog(100) + .disable_signals() + .bind("test", addr, move || { + fn_service(|io: TcpStream| async move { + let mut f = Framed::new(io, BytesCodec); + f.send(Bytes::from_static(b"test")).await.unwrap(); + Ok::<_, ()>(()) + }) }) - }) - .unwrap() - .start(); + .unwrap() + .start() + })); let _ = tx.send((srv, actix_rt::System::current())); let _ = sys.run(); @@ -144,29 +151,31 @@ fn test_configure() { let h = thread::spawn(move || { let num = num2.clone(); - let sys = actix_rt::System::new("test"); - let srv = Server::build() - .disable_signals() - .configure(move |cfg| { - let num = num.clone(); - let lst = net::TcpListener::bind(addr3).unwrap(); - cfg.bind("addr1", addr1) - .unwrap() - .bind("addr2", addr2) - .unwrap() - .listen("addr3", lst) - .apply(move |rt| { - let num = num.clone(); - rt.service("addr1", fn_service(|_| ok::<_, ()>(()))); - rt.service("addr3", fn_service(|_| ok::<_, ()>(()))); - rt.on_start(lazy(move |_| { - let _ = num.fetch_add(1, Relaxed); - })) - }) - }) - .unwrap() - .workers(1) - .start(); + let mut sys = actix_rt::System::new("test"); + let srv = sys.block_on(lazy(|_| { + Server::build() + .disable_signals() + .configure(move |cfg| { + let num = num.clone(); + let lst = net::TcpListener::bind(addr3).unwrap(); + cfg.bind("addr1", addr1) + .unwrap() + .bind("addr2", addr2) + .unwrap() + .listen("addr3", lst) + .apply(move |rt| { + let num = num.clone(); + rt.service("addr1", fn_service(|_| ok::<_, ()>(()))); + rt.service("addr3", fn_service(|_| ok::<_, ()>(()))); + rt.on_start(lazy(move |_| { + let _ = num.fetch_add(1, Relaxed); + })) + }) + }) + .unwrap() + .workers(1) + .start() + })); let _ = tx.send((srv, actix_rt::System::current())); let _ = sys.run(); }); diff --git a/actix-testing/src/lib.rs b/actix-testing/src/lib.rs index efcdd394..eadfe6c9 100644 --- a/actix-testing/src/lib.rs +++ b/actix-testing/src/lib.rs @@ -83,15 +83,18 @@ impl TestServer { // run server in separate thread thread::spawn(move || { - let sys = System::new("actix-test-server"); + let mut sys = System::new("actix-test-server"); let tcp = net::TcpListener::bind("127.0.0.1:0").unwrap(); let local_addr = tcp.local_addr().unwrap(); - Server::build() - .listen("test", tcp, factory)? - .workers(1) - .disable_signals() - .start(); + sys.block_on(async { + Server::build() + .listen("test", tcp, factory) + .unwrap() + .workers(1) + .disable_signals() + .start(); + }); tx.send((System::current(), local_addr)).unwrap(); sys.run() From 3ab8c3eb6954c92d136acf27944bb9e768b7c45a Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sun, 27 Dec 2020 04:28:00 +0000 Subject: [PATCH 03/73] service trait takes request type parameter (#232) --- actix-connect/src/connector.rs | 6 +- actix-connect/src/lib.rs | 12 +- actix-connect/src/resolve.rs | 6 +- actix-connect/src/service.rs | 17 +- actix-server/src/config.rs | 28 +- actix-server/src/service.rs | 31 ++- actix-service/CHANGES.md | 6 + actix-service/benches/and_then.rs | 138 ++++----- .../benches/unsafecell_vs_refcell.rs | 8 +- actix-service/src/and_then.rs | 105 ++++--- actix-service/src/and_then_apply_fn.rs | 262 +++++++++--------- actix-service/src/apply.rs | 227 +++++++-------- actix-service/src/apply_cfg.rs | 203 +++++++------- actix-service/src/boxed.rs | 103 ++++--- actix-service/src/fn_service.rs | 109 ++++---- actix-service/src/lib.rs | 102 +++---- actix-service/src/map.rs | 81 +++--- actix-service/src/map_config.rs | 130 ++++----- actix-service/src/map_err.rs | 79 +++--- actix-service/src/map_init_err.rs | 31 +-- actix-service/src/pipeline.rs | 250 +++++++++-------- actix-service/src/then.rs | 100 ++++--- actix-service/src/transform.rs | 76 +++-- actix-service/src/transform_err.rs | 45 ++- actix-tracing/src/lib.rs | 35 +-- actix-utils/Cargo.toml | 2 +- actix-utils/src/dispatcher.rs | 39 ++- actix-utils/src/timeout.rs | 47 ++-- 28 files changed, 1142 insertions(+), 1136 deletions(-) diff --git a/actix-connect/src/connector.rs b/actix-connect/src/connector.rs index 9dc9a2ad..e4c86d91 100644 --- a/actix-connect/src/connector.rs +++ b/actix-connect/src/connector.rs @@ -40,8 +40,7 @@ impl Clone for TcpConnectorFactory { } } -impl ServiceFactory for TcpConnectorFactory { - type Request = Connect; +impl ServiceFactory> for TcpConnectorFactory { type Response = Connection; type Error = ConnectError; type Config = (); @@ -70,8 +69,7 @@ impl Clone for TcpConnector { } } -impl Service for TcpConnector { - type Request = Connect; +impl Service> for TcpConnector { type Response = Connection; type Error = ConnectError; #[allow(clippy::type_complexity)] diff --git a/actix-connect/src/lib.rs b/actix-connect/src/lib.rs index 36d0b98a..d2111d41 100644 --- a/actix-connect/src/lib.rs +++ b/actix-connect/src/lib.rs @@ -76,8 +76,8 @@ pub async fn start_default_resolver() -> Result { /// Create TCP connector service. pub fn new_connector( resolver: AsyncResolver, -) -> impl Service, Response = Connection, Error = ConnectError> - + Clone { +) -> impl Service, Response = Connection, Error = ConnectError> + Clone +{ pipeline(Resolver::new(resolver)).and_then(TcpConnector::new()) } @@ -85,8 +85,8 @@ pub fn new_connector( pub fn new_connector_factory( resolver: AsyncResolver, ) -> impl ServiceFactory< + Connect, Config = (), - Request = Connect, Response = Connection, Error = ConnectError, InitError = (), @@ -96,15 +96,15 @@ pub fn new_connector_factory( /// Create connector service with default parameters. pub fn default_connector( -) -> impl Service, Response = Connection, Error = ConnectError> - + Clone { +) -> impl Service, Response = Connection, Error = ConnectError> + Clone +{ pipeline(Resolver::default()).and_then(TcpConnector::new()) } /// Create connector service factory with default parameters. pub fn default_connector_factory() -> impl ServiceFactory< + Connect, Config = (), - Request = Connect, Response = Connection, Error = ConnectError, InitError = (), diff --git a/actix-connect/src/resolve.rs b/actix-connect/src/resolve.rs index bfb1482f..85edf0d8 100644 --- a/actix-connect/src/resolve.rs +++ b/actix-connect/src/resolve.rs @@ -54,8 +54,7 @@ impl Clone for ResolverFactory { } } -impl ServiceFactory for ResolverFactory { - type Request = Connect; +impl ServiceFactory> for ResolverFactory { type Response = Connect; type Error = ConnectError; type Config = (); @@ -102,8 +101,7 @@ impl Clone for Resolver { } } -impl Service for Resolver { - type Request = Connect; +impl Service> for Resolver { type Response = Connect; type Error = ConnectError; #[allow(clippy::type_complexity)] diff --git a/actix-connect/src/service.rs b/actix-connect/src/service.rs index 1047e3df..ef5d04da 100644 --- a/actix-connect/src/service.rs +++ b/actix-connect/src/service.rs @@ -70,8 +70,7 @@ impl Clone for ConnectServiceFactory { } } -impl ServiceFactory for ConnectServiceFactory { - type Request = Connect; +impl ServiceFactory> for ConnectServiceFactory { type Response = Connection; type Error = ConnectError; type Config = (); @@ -90,8 +89,7 @@ pub struct ConnectService { resolver: Resolver, } -impl Service for ConnectService { - type Request = Connect; +impl Service> for ConnectService { type Response = Connection; type Error = ConnectError; type Future = ConnectServiceResponse; @@ -109,8 +107,8 @@ impl Service for ConnectService { } enum ConnectState { - Resolve( as Service>::Future), - Connect( as Service>::Future), + Resolve( as Service>>::Future), + Connect( as Service>>::Future), } impl ConnectState { @@ -160,8 +158,7 @@ pub struct TcpConnectService { resolver: Resolver, } -impl Service for TcpConnectService { - type Request = Connect; +impl Service> for TcpConnectService { type Response = TcpStream; type Error = ConnectError; type Future = TcpConnectServiceResponse; @@ -179,8 +176,8 @@ impl Service for TcpConnectService { } enum TcpConnectState { - Resolve( as Service>::Future), - Connect( as Service>::Future), + Resolve( as Service>>::Future), + Connect( as Service>>::Future), } impl TcpConnectState { diff --git a/actix-server/src/config.rs b/actix-server/src/config.rs index 28996b9b..a1315a72 100644 --- a/actix-server/src/config.rs +++ b/actix-server/src/config.rs @@ -2,7 +2,10 @@ use std::collections::HashMap; use std::{fmt, io, net}; use actix_rt::net::TcpStream; -use actix_service as actix; +use actix_service::{ + fn_service, IntoServiceFactory as IntoBaseServiceFactory, + ServiceFactory as BaseServiceFactory, +}; use actix_utils::counter::CounterGuard; use futures_util::future::{ok, Future, FutureExt, LocalBoxFuture}; use log::error; @@ -141,12 +144,10 @@ impl InternalServiceFactory for ConfiguredService { let name = names.remove(&token).unwrap().0; res.push(( token, - Box::new(StreamService::new(actix::fn_service( - move |_: TcpStream| { - error!("Service {:?} is not configured", name); - ok::<_, ()>(()) - }, - ))), + Box::new(StreamService::new(fn_service(move |_: TcpStream| { + error!("Service {:?} is not configured", name); + ok::<_, ()>(()) + }))), )); }; } @@ -208,8 +209,8 @@ impl ServiceRuntime { /// *ServiceConfig::bind()* or *ServiceConfig::listen()* methods. pub fn service(&mut self, name: &str, service: F) where - F: actix::IntoServiceFactory, - T: actix::ServiceFactory + 'static, + F: IntoBaseServiceFactory, + T: BaseServiceFactory + 'static, T::Future: 'static, T::Service: 'static, T::InitError: fmt::Debug, @@ -237,8 +238,8 @@ impl ServiceRuntime { } type BoxedNewService = Box< - dyn actix::ServiceFactory< - Request = (Option, StdStream), + dyn BaseServiceFactory< + (Option, StdStream), Response = (), Error = (), InitError = (), @@ -252,15 +253,14 @@ struct ServiceFactory { inner: T, } -impl actix::ServiceFactory for ServiceFactory +impl BaseServiceFactory<(Option, StdStream)> for ServiceFactory where - T: actix::ServiceFactory, + T: BaseServiceFactory, T::Future: 'static, T::Service: 'static, T::Error: 'static, T::InitError: fmt::Debug + 'static, { - type Request = (Option, StdStream); type Response = (); type Error = (); type Config = (); diff --git a/actix-server/src/service.rs b/actix-server/src/service.rs index 4fc49586..569ce048 100644 --- a/actix-server/src/service.rs +++ b/actix-server/src/service.rs @@ -3,7 +3,7 @@ use std::net::SocketAddr; use std::task::{Context, Poll}; use actix_rt::spawn; -use actix_service::{self as actix, Service, ServiceFactory as ActixServiceFactory}; +use actix_service::{Service, ServiceFactory as BaseServiceFactory}; use actix_utils::counter::CounterGuard; use futures_util::future::{err, ok, LocalBoxFuture, Ready}; use futures_util::{FutureExt, TryFutureExt}; @@ -13,7 +13,7 @@ use super::Token; use crate::socket::{FromStream, StdStream}; pub trait ServiceFactory: Send + Clone + 'static { - type Factory: actix::ServiceFactory; + type Factory: BaseServiceFactory; fn create(&self) -> Self::Factory; } @@ -28,31 +28,34 @@ pub(crate) trait InternalServiceFactory: Send { pub(crate) type BoxedServerService = Box< dyn Service< - Request = (Option, StdStream), + (Option, StdStream), Response = (), Error = (), Future = Ready>, >, >; -pub(crate) struct StreamService { - service: T, +pub(crate) struct StreamService { + service: S, + _phantom: PhantomData, } -impl StreamService { - pub(crate) fn new(service: T) -> Self { - StreamService { service } +impl StreamService { + pub(crate) fn new(service: S) -> Self { + StreamService { + service, + _phantom: PhantomData, + } } } -impl Service for StreamService +impl Service<(Option, StdStream)> for StreamService where - T: Service, - T::Future: 'static, - T::Error: 'static, + S: Service, + S::Future: 'static, + S::Error: 'static, I: FromStream, { - type Request = (Option, StdStream); type Response = (); type Error = (); type Future = Ready>; @@ -144,7 +147,7 @@ where impl ServiceFactory for F where F: Fn() -> T + Send + Clone + 'static, - T: actix::ServiceFactory, + T: BaseServiceFactory, I: FromStream, { type Factory = T; diff --git a/actix-service/CHANGES.md b/actix-service/CHANGES.md index 82481f46..971741e8 100644 --- a/actix-service/CHANGES.md +++ b/actix-service/CHANGES.md @@ -1,8 +1,14 @@ # Changes ## Unreleased - 2020-xx-xx +* `Service`, other traits, and many type signatures now take the the request type as a type + parameter instead of an associated type. [#232] * Upgrade `pin-project` to `1.0`. + +[#232]: https://github.com/actix/actix-net/pull/232 + + ## 1.0.6 - 2020-08-09 ### Fixed diff --git a/actix-service/benches/and_then.rs b/actix-service/benches/and_then.rs index f4174dd7..c8aa315d 100644 --- a/actix-service/benches/and_then.rs +++ b/actix-service/benches/and_then.rs @@ -5,11 +5,14 @@ use actix_service::Service; use criterion::{criterion_main, Criterion}; use futures_util::future::join_all; use futures_util::future::TryFutureExt; -use std::cell::{RefCell, UnsafeCell}; use std::future::Future; use std::pin::Pin; use std::rc::Rc; use std::task::{Context, Poll}; +use std::{ + cell::{RefCell, UnsafeCell}, + marker::PhantomData, +}; /* * Test services A,B for AndThen service implementations @@ -28,71 +31,72 @@ async fn svc2(req: usize) -> Result { * Cut down version of actix_service::AndThenService based on actix-service::Cell */ -struct AndThenUC(Rc>); +struct AndThenUC(Rc>, PhantomData); -impl AndThenUC { +impl AndThenUC { fn new(a: A, b: B) -> Self where - A: Service, - B: Service, + A: Service, + B: Service, { - Self(Rc::new(UnsafeCell::new((a, b)))) + Self(Rc::new(UnsafeCell::new((a, b))), PhantomData) } } -impl Clone for AndThenUC { +impl Clone for AndThenUC { fn clone(&self) -> Self { - Self(self.0.clone()) + Self(self.0.clone(), PhantomData) } } -impl Service for AndThenUC +impl Service for AndThenUC where - A: Service, - B: Service, + A: Service, + B: Service, { - type Request = A::Request; type Response = B::Response; type Error = A::Error; - type Future = AndThenServiceResponse; + type Future = AndThenServiceResponse; fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) } - fn call(&mut self, req: A::Request) -> Self::Future { + fn call(&mut self, req: Req) -> Self::Future { let fut = unsafe { &mut *(*self.0).get() }.0.call(req); AndThenServiceResponse { state: State::A(fut, Some(self.0.clone())), + _phantom: PhantomData, } } } #[pin_project::pin_project] -pub(crate) struct AndThenServiceResponse +pub(crate) struct AndThenServiceResponse where - A: Service, - B: Service, + A: Service, + B: Service, { #[pin] - state: State, + state: State, + _phantom: PhantomData, } #[pin_project::pin_project(project = StateProj)] -enum State +enum State where - A: Service, - B: Service, + A: Service, + B: Service, { A(#[pin] A::Future, Option>>), B(#[pin] B::Future), - Empty, + Empty(PhantomData), } -impl Future for AndThenServiceResponse +impl Future for AndThenServiceResponse where - A: Service, - B: Service, + A: Service, + B: Service, { type Output = Result; @@ -103,7 +107,7 @@ where StateProj::A(fut, b) => match fut.poll(cx)? { Poll::Ready(res) => { let b = b.take().unwrap(); - this.state.set(State::Empty); // drop fut A + this.state.set(State::Empty(PhantomData)); // drop fut A let fut = unsafe { &mut (*b.get()).1 }.call(res); this.state.set(State::B(fut)); self.poll(cx) @@ -111,10 +115,10 @@ where Poll::Pending => Poll::Pending, }, StateProj::B(fut) => fut.poll(cx).map(|r| { - this.state.set(State::Empty); + this.state.set(State::Empty(PhantomData)); r }), - StateProj::Empty => { + StateProj::Empty(_) => { panic!("future must not be polled after it returned `Poll::Ready`") } } @@ -125,39 +129,38 @@ where * AndThenRC - AndThen service based on RefCell */ -struct AndThenRC(Rc>); +struct AndThenRC(Rc>, PhantomData); -impl AndThenRC { +impl AndThenRC { fn new(a: A, b: B) -> Self where - A: Service, - B: Service, + A: Service, + B: Service, { - Self(Rc::new(RefCell::new((a, b)))) + Self(Rc::new(RefCell::new((a, b))), PhantomData) } } -impl Clone for AndThenRC { +impl Clone for AndThenRC { fn clone(&self) -> Self { - Self(self.0.clone()) + Self(self.0.clone(), PhantomData) } } -impl Service for AndThenRC +impl Service for AndThenRC where - A: Service, - B: Service, + A: Service, + B: Service, { - type Request = A::Request; type Response = B::Response; type Error = A::Error; - type Future = AndThenServiceResponseRC; + type Future = AndThenServiceResponseRC; fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) } - fn call(&mut self, req: A::Request) -> Self::Future { + fn call(&mut self, req: Req) -> Self::Future { let fut = self.0.borrow_mut().0.call(req); AndThenServiceResponseRC { state: StateRC::A(fut, Some(self.0.clone())), @@ -166,30 +169,30 @@ where } #[pin_project::pin_project] -pub(crate) struct AndThenServiceResponseRC +pub(crate) struct AndThenServiceResponseRC where - A: Service, - B: Service, + A: Service, + B: Service, { #[pin] - state: StateRC, + state: StateRC, } #[pin_project::pin_project(project = StateRCProj)] -enum StateRC +enum StateRC where - A: Service, - B: Service, + A: Service, + B: Service, { A(#[pin] A::Future, Option>>), B(#[pin] B::Future), - Empty, + Empty(PhantomData), } -impl Future for AndThenServiceResponseRC +impl Future for AndThenServiceResponseRC where - A: Service, - B: Service, + A: Service, + B: Service, { type Output = Result; @@ -200,7 +203,7 @@ where StateRCProj::A(fut, b) => match fut.poll(cx)? { Poll::Ready(res) => { let b = b.take().unwrap(); - this.state.set(StateRC::Empty); // drop fut A + this.state.set(StateRC::Empty(PhantomData)); // drop fut A let fut = b.borrow_mut().1.call(res); this.state.set(StateRC::B(fut)); self.poll(cx) @@ -208,10 +211,10 @@ where Poll::Pending => Poll::Pending, }, StateRCProj::B(fut) => fut.poll(cx).map(|r| { - this.state.set(StateRC::Empty); + this.state.set(StateRC::Empty(PhantomData)); r }), - StateRCProj::Empty => { + StateRCProj::Empty(_) => { panic!("future must not be polled after it returned `Poll::Ready`") } } @@ -223,32 +226,31 @@ where * and standard futures::future::and_then combinator in a Box */ -struct AndThenRCFuture(Rc>); +struct AndThenRCFuture(Rc>, PhantomData); -impl AndThenRCFuture { +impl AndThenRCFuture { fn new(a: A, b: B) -> Self where - A: Service, - B: Service, + A: Service, + B: Service, { - Self(Rc::new(RefCell::new((a, b)))) + Self(Rc::new(RefCell::new((a, b))), PhantomData) } } -impl Clone for AndThenRCFuture { +impl Clone for AndThenRCFuture { fn clone(&self) -> Self { - Self(self.0.clone()) + Self(self.0.clone(), PhantomData) } } -impl Service for AndThenRCFuture +impl Service for AndThenRCFuture where - A: Service + 'static, + A: Service + 'static, A::Future: 'static, - B: Service + 'static, + B: Service + 'static, B::Future: 'static, { - type Request = A::Request; type Response = B::Response; type Error = A::Error; type Future = BoxFuture; @@ -257,7 +259,7 @@ where Poll::Ready(Ok(())) } - fn call(&mut self, req: A::Request) -> Self::Future { + fn call(&mut self, req: Req) -> Self::Future { let fut = self.0.borrow_mut().0.call(req); let core = self.0.clone(); let fut2 = move |res| (*core).borrow_mut().1.call(res); @@ -281,7 +283,7 @@ where /// async_service_direct time: [1.0908 us 1.1656 us 1.2613 us] pub fn bench_async_service(c: &mut Criterion, srv: S, name: &str) where - S: Service + Clone + 'static, + S: Service<(), Response = usize, Error = ()> + Clone + 'static, { let mut rt = actix_rt::System::new("test"); diff --git a/actix-service/benches/unsafecell_vs_refcell.rs b/actix-service/benches/unsafecell_vs_refcell.rs index a599795f..cdf91233 100644 --- a/actix-service/benches/unsafecell_vs_refcell.rs +++ b/actix-service/benches/unsafecell_vs_refcell.rs @@ -20,8 +20,7 @@ impl Clone for SrvUC { } } -impl Service for SrvUC { - type Request = (); +impl Service<()> for SrvUC { type Response = usize; type Error = (); type Future = Ready>; @@ -50,8 +49,7 @@ impl Clone for SrvRC { } } -impl Service for SrvRC { - type Request = (); +impl Service<()> for SrvRC { type Response = usize; type Error = (); type Future = Ready>; @@ -83,7 +81,7 @@ impl Service for SrvRC { /// async_service_direct time: [1.0908 us 1.1656 us 1.2613 us] pub fn bench_async_service(c: &mut Criterion, srv: S, name: &str) where - S: Service + Clone + 'static, + S: Service<(), Response = usize, Error = ()> + Clone + 'static, { let mut rt = actix_rt::System::new("test"); diff --git a/actix-service/src/and_then.rs b/actix-service/src/and_then.rs index caaf8615..04caf79d 100644 --- a/actix-service/src/and_then.rs +++ b/actix-service/src/and_then.rs @@ -1,8 +1,8 @@ -use std::cell::RefCell; use std::future::Future; use std::pin::Pin; use std::rc::Rc; use std::task::{Context, Poll}; +use std::{cell::RefCell, marker::PhantomData}; use super::{Service, ServiceFactory}; @@ -10,34 +10,33 @@ use super::{Service, ServiceFactory}; /// of another service which completes successfully. /// /// This is created by the `Pipeline::and_then` method. -pub(crate) struct AndThenService(Rc>); +pub(crate) struct AndThenService(Rc>, PhantomData); -impl AndThenService { +impl AndThenService { /// Create new `AndThen` combinator pub(crate) fn new(a: A, b: B) -> Self where - A: Service, - B: Service, + A: Service, + B: Service, { - Self(Rc::new(RefCell::new((a, b)))) + Self(Rc::new(RefCell::new((a, b))), PhantomData) } } -impl Clone for AndThenService { +impl Clone for AndThenService { fn clone(&self) -> Self { - AndThenService(self.0.clone()) + AndThenService(self.0.clone(), PhantomData) } } -impl Service for AndThenService +impl Service for AndThenService where - A: Service, - B: Service, + A: Service, + B: Service, { - type Request = A::Request; type Response = B::Response; type Error = A::Error; - type Future = AndThenServiceResponse; + type Future = AndThenServiceResponse; fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { let mut srv = self.0.borrow_mut(); @@ -49,7 +48,7 @@ where } } - fn call(&mut self, req: A::Request) -> Self::Future { + fn call(&mut self, req: Req) -> Self::Future { AndThenServiceResponse { state: State::A(self.0.borrow_mut().0.call(req), Some(self.0.clone())), } @@ -57,30 +56,30 @@ where } #[pin_project::pin_project] -pub(crate) struct AndThenServiceResponse +pub(crate) struct AndThenServiceResponse where - A: Service, - B: Service, + A: Service, + B: Service, { #[pin] - state: State, + state: State, } #[pin_project::pin_project(project = StateProj)] -enum State +enum State where - A: Service, - B: Service, + A: Service, + B: Service, { A(#[pin] A::Future, Option>>), B(#[pin] B::Future), Empty, } -impl Future for AndThenServiceResponse +impl Future for AndThenServiceResponse where - A: Service, - B: Service, + A: Service, + B: Service, { type Output = Result; @@ -110,27 +109,28 @@ where } /// `.and_then()` service factory combinator -pub(crate) struct AndThenServiceFactory +pub(crate) struct AndThenServiceFactory where - A: ServiceFactory, + A: ServiceFactory, A::Config: Clone, B: ServiceFactory< + A::Response, Config = A::Config, - Request = A::Response, Error = A::Error, InitError = A::InitError, >, { inner: Rc<(A, B)>, + _phantom: PhantomData, } -impl AndThenServiceFactory +impl AndThenServiceFactory where - A: ServiceFactory, + A: ServiceFactory, A::Config: Clone, B: ServiceFactory< + A::Response, Config = A::Config, - Request = A::Response, Error = A::Error, InitError = A::InitError, >, @@ -139,29 +139,29 @@ where pub(crate) fn new(a: A, b: B) -> Self { Self { inner: Rc::new((a, b)), + _phantom: PhantomData, } } } -impl ServiceFactory for AndThenServiceFactory +impl ServiceFactory for AndThenServiceFactory where - A: ServiceFactory, + A: ServiceFactory, A::Config: Clone, B: ServiceFactory< + A::Response, Config = A::Config, - Request = A::Response, Error = A::Error, InitError = A::InitError, >, { - type Request = A::Request; type Response = B::Response; type Error = A::Error; type Config = A::Config; - type Service = AndThenService; + type Service = AndThenService; type InitError = A::InitError; - type Future = AndThenServiceFactoryResponse; + type Future = AndThenServiceFactoryResponse; fn new_service(&self, cfg: A::Config) -> Self::Future { let inner = &*self.inner; @@ -172,13 +172,13 @@ where } } -impl Clone for AndThenServiceFactory +impl Clone for AndThenServiceFactory where - A: ServiceFactory, + A: ServiceFactory, A::Config: Clone, B: ServiceFactory< + A::Response, Config = A::Config, - Request = A::Response, Error = A::Error, InitError = A::InitError, >, @@ -186,15 +186,16 @@ where fn clone(&self) -> Self { Self { inner: self.inner.clone(), + _phantom: PhantomData, } } } #[pin_project::pin_project] -pub(crate) struct AndThenServiceFactoryResponse +pub(crate) struct AndThenServiceFactoryResponse where - A: ServiceFactory, - B: ServiceFactory, + A: ServiceFactory, + B: ServiceFactory, { #[pin] fut_a: A::Future, @@ -205,10 +206,10 @@ where b: Option, } -impl AndThenServiceFactoryResponse +impl AndThenServiceFactoryResponse where - A: ServiceFactory, - B: ServiceFactory, + A: ServiceFactory, + B: ServiceFactory, { fn new(fut_a: A::Future, fut_b: B::Future) -> Self { AndThenServiceFactoryResponse { @@ -220,12 +221,12 @@ where } } -impl Future for AndThenServiceFactoryResponse +impl Future for AndThenServiceFactoryResponse where - A: ServiceFactory, - B: ServiceFactory, + A: ServiceFactory, + B: ServiceFactory, { - type Output = Result, A::InitError>; + type Output = Result, A::InitError>; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let this = self.project(); @@ -263,8 +264,7 @@ mod tests { struct Srv1(Rc>); - impl Service for Srv1 { - type Request = &'static str; + impl Service<&'static str> for Srv1 { type Response = &'static str; type Error = (); type Future = Ready>; @@ -282,8 +282,7 @@ mod tests { #[derive(Clone)] struct Srv2(Rc>); - impl Service for Srv2 { - type Request = &'static str; + impl Service<&'static str> for Srv2 { type Response = (&'static str, &'static str); type Error = (); type Future = Ready>; diff --git a/actix-service/src/and_then_apply_fn.rs b/actix-service/src/and_then_apply_fn.rs index b62cf505..c7bd098c 100644 --- a/actix-service/src/and_then_apply_fn.rs +++ b/actix-service/src/and_then_apply_fn.rs @@ -8,66 +8,67 @@ use std::task::{Context, Poll}; use crate::{Service, ServiceFactory}; /// `Apply` service combinator -pub(crate) struct AndThenApplyFn +pub(crate) struct AndThenApplyFn where - A: Service, - B: Service, - F: FnMut(A::Response, &mut B) -> Fut, + S1: Service, + S2: Service, + F: FnMut(S1::Response, &mut S2) -> Fut, Fut: Future>, - Err: From + From, + Err: From + From, { - srv: Rc>, - r: PhantomData<(Fut, Res, Err)>, + svc: Rc>, + _phantom: PhantomData<(Fut, Req, In, Res, Err)>, } -impl AndThenApplyFn +impl AndThenApplyFn where - A: Service, - B: Service, - F: FnMut(A::Response, &mut B) -> Fut, + S1: Service, + S2: Service, + F: FnMut(S1::Response, &mut S2) -> Fut, Fut: Future>, - Err: From + From, + Err: From + From, { /// Create new `Apply` combinator - pub(crate) fn new(a: A, b: B, f: F) -> Self { + pub(crate) fn new(a: S1, b: S2, wrap_fn: F) -> Self { Self { - srv: Rc::new(RefCell::new((a, b, f))), - r: PhantomData, + svc: Rc::new(RefCell::new((a, b, wrap_fn))), + _phantom: PhantomData, } } } -impl Clone for AndThenApplyFn +impl Clone + for AndThenApplyFn where - A: Service, - B: Service, - F: FnMut(A::Response, &mut B) -> Fut, + S1: Service, + S2: Service, + F: FnMut(S1::Response, &mut S2) -> Fut, Fut: Future>, - Err: From + From, + Err: From + From, { fn clone(&self) -> Self { AndThenApplyFn { - srv: self.srv.clone(), - r: PhantomData, + svc: self.svc.clone(), + _phantom: PhantomData, } } } -impl Service for AndThenApplyFn +impl Service + for AndThenApplyFn where - A: Service, - B: Service, - F: FnMut(A::Response, &mut B) -> Fut, + S1: Service, + S2: Service, + F: FnMut(S1::Response, &mut S2) -> Fut, Fut: Future>, - Err: From + From, + Err: From + From, { - type Request = A::Request; type Response = Res; type Error = Err; - type Future = AndThenApplyFnFuture; + type Future = AndThenApplyFnFuture; fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { - let mut inner = self.srv.borrow_mut(); + let mut inner = self.svc.borrow_mut(); let not_ready = inner.0.poll_ready(cx)?.is_pending(); if inner.1.poll_ready(cx)?.is_pending() || not_ready { Poll::Pending @@ -76,50 +77,49 @@ where } } - fn call(&mut self, req: A::Request) -> Self::Future { - let fut = self.srv.borrow_mut().0.call(req); + fn call(&mut self, req: Req) -> Self::Future { + let fut = self.svc.borrow_mut().0.call(req); AndThenApplyFnFuture { - state: State::A(fut, Some(self.srv.clone())), + state: State::A(fut, Some(self.svc.clone())), } } } #[pin_project::pin_project] -pub(crate) struct AndThenApplyFnFuture +pub(crate) struct AndThenApplyFnFuture where - A: Service, - B: Service, - F: FnMut(A::Response, &mut B) -> Fut, + S1: Service, + S2: Service, + F: FnMut(S1::Response, &mut S2) -> Fut, Fut: Future>, - Err: From, - Err: From, + Err: From + From, { #[pin] - state: State, + state: State, } #[pin_project::pin_project(project = StateProj)] -enum State +enum State where - A: Service, - B: Service, - F: FnMut(A::Response, &mut B) -> Fut, + S1: Service, + S2: Service, + F: FnMut(S1::Response, &mut S2) -> Fut, Fut: Future>, - Err: From, - Err: From, + Err: From + From, { - A(#[pin] A::Future, Option>>), + A(#[pin] S1::Future, Option>>), B(#[pin] Fut), - Empty, + Empty(PhantomData), } -impl Future for AndThenApplyFnFuture +impl Future + for AndThenApplyFnFuture where - A: Service, - B: Service, - F: FnMut(A::Response, &mut B) -> Fut, + S1: Service, + S2: Service, + F: FnMut(S1::Response, &mut S2) -> Fut, Fut: Future>, - Err: From + From, + Err: From + From, { type Output = Result; @@ -129,8 +129,8 @@ where match this.state.as_mut().project() { StateProj::A(fut, b) => match fut.poll(cx)? { Poll::Ready(res) => { - let b = b.take().unwrap(); - this.state.set(State::Empty); + let b = Option::take(b).unwrap(); + this.state.set(State::Empty(PhantomData)); let (_, b, f) = &mut *b.borrow_mut(); let fut = f(res, b); this.state.set(State::B(fut)); @@ -139,10 +139,10 @@ where Poll::Pending => Poll::Pending, }, StateProj::B(fut) => fut.poll(cx).map(|r| { - this.state.set(State::Empty); + this.state.set(State::Empty(PhantomData)); r }), - StateProj::Empty => { + StateProj::Empty(_) => { panic!("future must not be polled after it returned `Poll::Ready`") } } @@ -150,119 +150,127 @@ where } /// `AndThenApplyFn` service factory -pub(crate) struct AndThenApplyFnFactory { - srv: Rc<(A, B, F)>, - r: PhantomData<(Fut, Res, Err)>, +pub(crate) struct AndThenApplyFnFactory { + srv: Rc<(SF1, SF2, F)>, + _phantom: PhantomData<(Fut, Req, In, Res, Err)>, } -impl AndThenApplyFnFactory +impl + AndThenApplyFnFactory where - A: ServiceFactory, - B: ServiceFactory, - F: FnMut(A::Response, &mut B::Service) -> Fut + Clone, + SF1: ServiceFactory, + SF2: ServiceFactory, + F: FnMut(SF1::Response, &mut SF2::Service) -> Fut + Clone, Fut: Future>, - Err: From + From, + Err: From + From, { /// Create new `ApplyNewService` new service instance - pub(crate) fn new(a: A, b: B, f: F) -> Self { + pub(crate) fn new(a: SF1, b: SF2, wrap_fn: F) -> Self { Self { - srv: Rc::new((a, b, f)), - r: PhantomData, + srv: Rc::new((a, b, wrap_fn)), + _phantom: PhantomData, } } } -impl Clone for AndThenApplyFnFactory { +impl Clone + for AndThenApplyFnFactory +{ fn clone(&self) -> Self { Self { srv: self.srv.clone(), - r: PhantomData, + _phantom: PhantomData, } } } -impl ServiceFactory for AndThenApplyFnFactory +impl ServiceFactory + for AndThenApplyFnFactory where - A: ServiceFactory, - A::Config: Clone, - B: ServiceFactory, - F: FnMut(A::Response, &mut B::Service) -> Fut + Clone, + SF1: ServiceFactory, + SF1::Config: Clone, + SF2: ServiceFactory, + F: FnMut(SF1::Response, &mut SF2::Service) -> Fut + Clone, Fut: Future>, - Err: From + From, + Err: From + From, { - type Request = A::Request; type Response = Res; type Error = Err; - type Service = AndThenApplyFn; - type Config = A::Config; - type InitError = A::InitError; - type Future = AndThenApplyFnFactoryResponse; + type Service = AndThenApplyFn; + type Config = SF1::Config; + type InitError = SF1::InitError; + type Future = AndThenApplyFnFactoryResponse; - fn new_service(&self, cfg: A::Config) -> Self::Future { + fn new_service(&self, cfg: SF1::Config) -> Self::Future { let srv = &*self.srv; AndThenApplyFnFactoryResponse { - a: None, - b: None, - f: srv.2.clone(), - fut_a: srv.0.new_service(cfg.clone()), - fut_b: srv.1.new_service(cfg), + s1: None, + s2: None, + wrap_fn: srv.2.clone(), + fut_s1: srv.0.new_service(cfg.clone()), + fut_s2: srv.1.new_service(cfg), + _phantom: PhantomData, } } } #[pin_project::pin_project] -pub(crate) struct AndThenApplyFnFactoryResponse +pub(crate) struct AndThenApplyFnFactoryResponse where - A: ServiceFactory, - B: ServiceFactory, - F: FnMut(A::Response, &mut B::Service) -> Fut + Clone, + SF1: ServiceFactory, + SF2: ServiceFactory, + F: FnMut(SF1::Response, &mut SF2::Service) -> Fut + Clone, Fut: Future>, - Err: From, - Err: From, + Err: From, + Err: From, { #[pin] - fut_b: B::Future, + fut_s1: SF1::Future, #[pin] - fut_a: A::Future, - f: F, - a: Option, - b: Option, + fut_s2: SF2::Future, + wrap_fn: F, + s1: Option, + s2: Option, + _phantom: PhantomData, } -impl Future for AndThenApplyFnFactoryResponse +impl Future + for AndThenApplyFnFactoryResponse where - A: ServiceFactory, - B: ServiceFactory, - F: FnMut(A::Response, &mut B::Service) -> Fut + Clone, + SF1: ServiceFactory, + SF2: ServiceFactory, + F: FnMut(SF1::Response, &mut SF2::Service) -> Fut + Clone, Fut: Future>, - Err: From + From, + Err: From + From, { - type Output = - Result, A::InitError>; + type Output = Result< + AndThenApplyFn, + SF1::InitError, + >; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let this = self.project(); - if this.a.is_none() { - if let Poll::Ready(service) = this.fut_a.poll(cx)? { - *this.a = Some(service); + if this.s1.is_none() { + if let Poll::Ready(service) = this.fut_s1.poll(cx)? { + *this.s1 = Some(service); } } - if this.b.is_none() { - if let Poll::Ready(service) = this.fut_b.poll(cx)? { - *this.b = Some(service); + if this.s2.is_none() { + if let Poll::Ready(service) = this.fut_s2.poll(cx)? { + *this.s2 = Some(service); } } - if this.a.is_some() && this.b.is_some() { + if this.s1.is_some() && this.s2.is_some() { Poll::Ready(Ok(AndThenApplyFn { - srv: Rc::new(RefCell::new(( - this.a.take().unwrap(), - this.b.take().unwrap(), - this.f.clone(), + svc: Rc::new(RefCell::new(( + Option::take(this.s1).unwrap(), + Option::take(this.s2).unwrap(), + this.wrap_fn.clone(), ))), - r: PhantomData, + _phantom: PhantomData, })) } else { Poll::Pending @@ -280,29 +288,29 @@ mod tests { #[derive(Clone)] struct Srv; - impl Service for Srv { - type Request = (); + + impl Service for Srv { type Response = (); type Error = (); - type Future = Ready>; + type Future = Ready>; fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) } - #[allow(clippy::unit_arg)] - fn call(&mut self, req: Self::Request) -> Self::Future { - ok(req) + fn call(&mut self, req: u8) -> Self::Future { + let _ = req; + ok(()) } } #[actix_rt::test] async fn test_service() { let mut srv = pipeline(ok).and_then_apply_fn(Srv, |req: &'static str, s| { - s.call(()).map_ok(move |res| (req, res)) + s.call(1).map_ok(move |res| (req, res)) }); let res = lazy(|cx| srv.poll_ready(cx)).await; - assert_eq!(res, Poll::Ready(Ok(()))); + assert!(res.is_ready()); let res = srv.call("srv").await; assert!(res.is_ok()); @@ -313,11 +321,11 @@ mod tests { async fn test_service_factory() { let new_srv = pipeline_factory(|| ok::<_, ()>(fn_service(ok))).and_then_apply_fn( || ok(Srv), - |req: &'static str, s| s.call(()).map_ok(move |res| (req, res)), + |req: &'static str, s| s.call(1).map_ok(move |res| (req, res)), ); let mut srv = new_srv.new_service(()).await.unwrap(); let res = lazy(|cx| srv.poll_ready(cx)).await; - assert_eq!(res, Poll::Ready(Ok(()))); + assert!(res.is_ready()); let res = srv.call("srv").await; assert!(res.is_ok()); diff --git a/actix-service/src/apply.rs b/actix-service/src/apply.rs index d1f8ff3a..27a09684 100644 --- a/actix-service/src/apply.rs +++ b/actix-service/src/apply.rs @@ -1,203 +1,209 @@ -use std::future::Future; -use std::marker::PhantomData; -use std::pin::Pin; -use std::task::{Context, Poll}; +use std::{ + future::Future, + marker::PhantomData, + pin::Pin, + task::{Context, Poll}, +}; + +use futures_util::ready; use super::{IntoService, IntoServiceFactory, Service, ServiceFactory}; /// Apply transform function to a service. -pub fn apply_fn(service: U, f: F) -> Apply +/// +/// The In and Out type params refer to the request and response types for the wrapped service. +pub fn apply_fn( + service: I, + wrap_fn: F, +) -> Apply where - T: Service, - F: FnMut(In, &mut T) -> R, - R: Future>, - U: IntoService, + I: IntoService, + S: Service, + F: FnMut(Req, &mut S) -> Fut, + Fut: Future>, { - Apply::new(service.into_service(), f) + Apply::new(service.into_service(), wrap_fn) } /// Service factory that produces `apply_fn` service. -pub fn apply_fn_factory( - service: U, +/// +/// The In and Out type params refer to the request and response types for the wrapped service. +pub fn apply_fn_factory( + service: I, f: F, -) -> ApplyServiceFactory +) -> ApplyFactory where - T: ServiceFactory, - F: FnMut(In, &mut T::Service) -> R + Clone, - R: Future>, - U: IntoServiceFactory, + I: IntoServiceFactory, + SF: ServiceFactory, + F: FnMut(Req, &mut SF::Service) -> Fut + Clone, + Fut: Future>, { - ApplyServiceFactory::new(service.into_factory(), f) + ApplyFactory::new(service.into_factory(), f) } -/// `Apply` service combinator -pub struct Apply +/// `Apply` service combinator. +/// +/// The In and Out type params refer to the request and response types for the wrapped service. +pub struct Apply where - T: Service, + S: Service, { - service: T, - f: F, - r: PhantomData<(In, Out, R)>, + service: S, + wrap_fn: F, + _phantom: PhantomData<(Req, In, Res, Err)>, } -impl Apply +impl Apply where - T: Service, - F: FnMut(In, &mut T) -> R, - R: Future>, + S: Service, + F: FnMut(Req, &mut S) -> Fut, + Fut: Future>, { /// Create new `Apply` combinator - fn new(service: T, f: F) -> Self { + fn new(service: S, wrap_fn: F) -> Self { Self { service, - f, - r: PhantomData, + wrap_fn, + _phantom: PhantomData, } } } -impl Clone for Apply +impl Clone for Apply where - T: Service + Clone, - F: FnMut(In, &mut T) -> R + Clone, - R: Future>, + S: Service + Clone, + F: FnMut(Req, &mut S) -> Fut + Clone, + Fut: Future>, { fn clone(&self) -> Self { Apply { service: self.service.clone(), - f: self.f.clone(), - r: PhantomData, + wrap_fn: self.wrap_fn.clone(), + _phantom: PhantomData, } } } -impl Service for Apply +impl Service for Apply where - T: Service, - F: FnMut(In, &mut T) -> R, - R: Future>, + S: Service, + F: FnMut(Req, &mut S) -> Fut, + Fut: Future>, { - type Request = In; - type Response = Out; + type Response = Res; type Error = Err; - type Future = R; + type Future = Fut; fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { - Poll::Ready(futures_util::ready!(self.service.poll_ready(cx))) + Poll::Ready(ready!(self.service.poll_ready(cx))) } - fn call(&mut self, req: In) -> Self::Future { - (self.f)(req, &mut self.service) + fn call(&mut self, req: Req) -> Self::Future { + (self.wrap_fn)(req, &mut self.service) } } -/// `apply()` service factory -pub struct ApplyServiceFactory -where - T: ServiceFactory, - F: FnMut(In, &mut T::Service) -> R + Clone, - R: Future>, -{ - service: T, - f: F, - r: PhantomData<(R, In, Out)>, +/// `ApplyFactory` service factory combinator. +pub struct ApplyFactory { + factory: SF, + wrap_fn: F, + _phantom: PhantomData<(Req, In, Res, Err)>, } -impl ApplyServiceFactory +impl ApplyFactory where - T: ServiceFactory, - F: FnMut(In, &mut T::Service) -> R + Clone, - R: Future>, + SF: ServiceFactory, + F: FnMut(Req, &mut SF::Service) -> Fut + Clone, + Fut: Future>, { - /// Create new `ApplyNewService` new service instance - fn new(service: T, f: F) -> Self { + /// Create new `ApplyFactory` new service instance + fn new(factory: SF, wrap_fn: F) -> Self { Self { - f, - service, - r: PhantomData, + factory, + wrap_fn, + _phantom: PhantomData, } } } -impl Clone for ApplyServiceFactory +impl Clone for ApplyFactory where - T: ServiceFactory + Clone, - F: FnMut(In, &mut T::Service) -> R + Clone, - R: Future>, + SF: ServiceFactory + Clone, + F: FnMut(Req, &mut SF::Service) -> Fut + Clone, + Fut: Future>, { fn clone(&self) -> Self { Self { - service: self.service.clone(), - f: self.f.clone(), - r: PhantomData, + factory: self.factory.clone(), + wrap_fn: self.wrap_fn.clone(), + _phantom: PhantomData, } } } -impl ServiceFactory for ApplyServiceFactory +impl ServiceFactory + for ApplyFactory where - T: ServiceFactory, - F: FnMut(In, &mut T::Service) -> R + Clone, - R: Future>, + SF: ServiceFactory, + F: FnMut(Req, &mut SF::Service) -> Fut + Clone, + Fut: Future>, { - type Request = In; - type Response = Out; + type Response = Res; type Error = Err; - type Config = T::Config; - type Service = Apply; - type InitError = T::InitError; - type Future = ApplyServiceFactoryResponse; + type Config = SF::Config; + type Service = Apply; + type InitError = SF::InitError; + type Future = ApplyServiceFactoryResponse; - fn new_service(&self, cfg: T::Config) -> Self::Future { - ApplyServiceFactoryResponse::new(self.service.new_service(cfg), self.f.clone()) + fn new_service(&self, cfg: SF::Config) -> Self::Future { + let svc = self.factory.new_service(cfg); + ApplyServiceFactoryResponse::new(svc, self.wrap_fn.clone()) } } #[pin_project::pin_project] -pub struct ApplyServiceFactoryResponse +pub struct ApplyServiceFactoryResponse where - T: ServiceFactory, - F: FnMut(In, &mut T::Service) -> R, - R: Future>, + SF: ServiceFactory, + F: FnMut(Req, &mut SF::Service) -> Fut, + Fut: Future>, { #[pin] - fut: T::Future, - f: Option, - r: PhantomData<(In, Out)>, + fut: SF::Future, + wrap_fn: Option, + _phantom: PhantomData<(Req, Res)>, } -impl ApplyServiceFactoryResponse +impl ApplyServiceFactoryResponse where - T: ServiceFactory, - F: FnMut(In, &mut T::Service) -> R, - R: Future>, + SF: ServiceFactory, + F: FnMut(Req, &mut SF::Service) -> Fut, + Fut: Future>, { - fn new(fut: T::Future, f: F) -> Self { + fn new(fut: SF::Future, wrap_fn: F) -> Self { Self { - f: Some(f), fut, - r: PhantomData, + wrap_fn: Some(wrap_fn), + _phantom: PhantomData, } } } -impl Future for ApplyServiceFactoryResponse +impl Future + for ApplyServiceFactoryResponse where - T: ServiceFactory, - F: FnMut(In, &mut T::Service) -> R, - R: Future>, + SF: ServiceFactory, + F: FnMut(Req, &mut SF::Service) -> Fut, + Fut: Future>, { - type Output = Result, T::InitError>; + type Output = Result, SF::InitError>; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let this = self.project(); - if let Poll::Ready(svc) = this.fut.poll(cx)? { - Poll::Ready(Ok(Apply::new(svc, this.f.take().unwrap()))) - } else { - Poll::Pending - } + let svc = ready!(this.fut.poll(cx))?; + Poll::Ready(Ok(Apply::new(svc, Option::take(this.wrap_fn).unwrap()))) } } @@ -213,8 +219,7 @@ mod tests { #[derive(Clone)] struct Srv; - impl Service for Srv { - type Request = (); + impl Service<()> for Srv { type Response = (); type Error = (); type Future = Ready>; diff --git a/actix-service/src/apply_cfg.rs b/actix-service/src/apply_cfg.rs index 0d984309..da24e87d 100644 --- a/actix-service/src/apply_cfg.rs +++ b/actix-service/src/apply_cfg.rs @@ -7,152 +7,152 @@ use std::task::{Context, Poll}; use crate::{Service, ServiceFactory}; -/// Convert `Fn(Config, &mut Service1) -> Future` fn to a service factory -pub fn apply_cfg( - srv: T, +/// Convert `Fn(Config, &mut Service1) -> Future` fn to a service factory. +pub fn apply_cfg( + srv: S1, f: F, ) -> impl ServiceFactory< - Config = C, - Request = S::Request, - Response = S::Response, - Error = S::Error, - Service = S, - InitError = E, - Future = R, + Req, + Config = Cfg, + Response = S2::Response, + Error = S2::Error, + Service = S2, + InitError = Err, + Future = Fut, > + Clone where - F: FnMut(C, &mut T) -> R, - T: Service, - R: Future>, - S: Service, + S1: Service, + F: FnMut(Cfg, &mut S1) -> Fut, + Fut: Future>, + S2: Service, { ApplyConfigService { srv: Rc::new(RefCell::new((srv, f))), - _t: PhantomData, + _phantom: PhantomData, } } -/// Convert `Fn(Config, &mut Service1) -> Future` fn to a service factory +/// Convert `Fn(Config, &mut ServiceFactory1) -> Future` fn to a service factory. /// /// Service1 get constructed from `T` factory. -pub fn apply_cfg_factory( - factory: T, +pub fn apply_cfg_factory( + factory: SF, f: F, ) -> impl ServiceFactory< - Config = C, - Request = S::Request, + Req, + Config = Cfg, Response = S::Response, Error = S::Error, Service = S, - InitError = T::InitError, + InitError = SF::InitError, > + Clone where - F: FnMut(C, &mut T::Service) -> R, - T: ServiceFactory, - T::InitError: From, - R: Future>, - S: Service, + SF: ServiceFactory, + F: FnMut(Cfg, &mut SF::Service) -> Fut, + SF::InitError: From, + Fut: Future>, + S: Service, { ApplyConfigServiceFactory { srv: Rc::new(RefCell::new((factory, f))), - _t: PhantomData, + _phantom: PhantomData, } } /// Convert `Fn(Config, &mut Server) -> Future` fn to NewService\ -struct ApplyConfigService +struct ApplyConfigService where - F: FnMut(C, &mut T) -> R, - T: Service, - R: Future>, - S: Service, + S1: Service, + F: FnMut(Cfg, &mut S1) -> Fut, + Fut: Future>, + S2: Service, { - srv: Rc>, - _t: PhantomData<(C, R, S)>, + srv: Rc>, + _phantom: PhantomData<(Cfg, Req, Fut, S2)>, } -impl Clone for ApplyConfigService +impl Clone for ApplyConfigService where - F: FnMut(C, &mut T) -> R, - T: Service, - R: Future>, - S: Service, + S1: Service, + F: FnMut(Cfg, &mut S1) -> Fut, + Fut: Future>, + S2: Service, { fn clone(&self) -> Self { ApplyConfigService { srv: self.srv.clone(), - _t: PhantomData, + _phantom: PhantomData, } } } -impl ServiceFactory for ApplyConfigService +impl ServiceFactory + for ApplyConfigService where - F: FnMut(C, &mut T) -> R, - T: Service, - R: Future>, - S: Service, + S1: Service, + F: FnMut(Cfg, &mut S1) -> Fut, + Fut: Future>, + S2: Service, { - type Config = C; - type Request = S::Request; - type Response = S::Response; - type Error = S::Error; - type Service = S; + type Config = Cfg; + type Response = S2::Response; + type Error = S2::Error; + type Service = S2; - type InitError = E; - type Future = R; + type InitError = Err; + type Future = Fut; - fn new_service(&self, cfg: C) -> Self::Future { + fn new_service(&self, cfg: Cfg) -> Self::Future { let (t, f) = &mut *self.srv.borrow_mut(); f(cfg, t) } } /// Convert `Fn(&Config) -> Future` fn to NewService -struct ApplyConfigServiceFactory +struct ApplyConfigServiceFactory where - F: FnMut(C, &mut T::Service) -> R, - T: ServiceFactory, - R: Future>, - S: Service, + SF: ServiceFactory, + F: FnMut(Cfg, &mut SF::Service) -> Fut, + Fut: Future>, + S: Service, { - srv: Rc>, - _t: PhantomData<(C, R, S)>, + srv: Rc>, + _phantom: PhantomData<(Cfg, Req, Fut, S)>, } -impl Clone for ApplyConfigServiceFactory +impl Clone for ApplyConfigServiceFactory where - F: FnMut(C, &mut T::Service) -> R, - T: ServiceFactory, - R: Future>, - S: Service, + SF: ServiceFactory, + F: FnMut(Cfg, &mut SF::Service) -> Fut, + Fut: Future>, + S: Service, { fn clone(&self) -> Self { Self { srv: self.srv.clone(), - _t: PhantomData, + _phantom: PhantomData, } } } -impl ServiceFactory for ApplyConfigServiceFactory +impl ServiceFactory + for ApplyConfigServiceFactory where - F: FnMut(C, &mut T::Service) -> R, - T: ServiceFactory, - T::InitError: From, - R: Future>, - S: Service, + SF: ServiceFactory, + SF::InitError: From, + F: FnMut(Cfg, &mut SF::Service) -> Fut, + Fut: Future>, + S: Service, { - type Config = C; - type Request = S::Request; + type Config = Cfg; type Response = S::Response; type Error = S::Error; type Service = S; - type InitError = T::InitError; - type Future = ApplyConfigServiceFactoryResponse; + type InitError = SF::InitError; + type Future = ApplyConfigServiceFactoryResponse; - fn new_service(&self, cfg: C) -> Self::Future { + fn new_service(&self, cfg: Cfg) -> Self::Future { ApplyConfigServiceFactoryResponse { cfg: Some(cfg), store: self.srv.clone(), @@ -162,42 +162,43 @@ where } #[pin_project::pin_project] -struct ApplyConfigServiceFactoryResponse +struct ApplyConfigServiceFactoryResponse where - F: FnMut(C, &mut T::Service) -> R, - T: ServiceFactory, - T::InitError: From, - R: Future>, - S: Service, + SF: ServiceFactory, + SF::InitError: From, + F: FnMut(Cfg, &mut SF::Service) -> Fut, + Fut: Future>, + S: Service, { - cfg: Option, - store: Rc>, + cfg: Option, + store: Rc>, #[pin] - state: State, + state: State, } #[pin_project::pin_project(project = StateProj)] -enum State +enum State where - T: ServiceFactory, - T::InitError: From, - R: Future>, - S: Service, + SF: ServiceFactory, + SF::InitError: From, + Fut: Future>, + S: Service, { - A(#[pin] T::Future), - B(T::Service), - C(#[pin] R), + A(#[pin] SF::Future), + B(SF::Service), + C(#[pin] Fut), } -impl Future for ApplyConfigServiceFactoryResponse +impl Future + for ApplyConfigServiceFactoryResponse where - F: FnMut(C, &mut T::Service) -> R, - T: ServiceFactory, - T::InitError: From, - R: Future>, - S: Service, + SF: ServiceFactory, + SF::InitError: From, + F: FnMut(Cfg, &mut SF::Service) -> Fut, + Fut: Future>, + S: Service, { - type Output = Result; + type Output = Result; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let mut this = self.as_mut().project(); diff --git a/actix-service/src/boxed.rs b/actix-service/src/boxed.rs index e630f81c..35a10dac 100644 --- a/actix-service/src/boxed.rs +++ b/actix-service/src/boxed.rs @@ -1,30 +1,30 @@ -use std::future::Future; use std::pin::Pin; use std::task::{Context, Poll}; +use std::{future::Future, marker::PhantomData}; use futures_util::future::FutureExt; use crate::{Service, ServiceFactory}; -pub type BoxFuture = Pin>>>; +pub type BoxFuture = Pin>>; pub type BoxService = - Box>>; + Box>>>; -pub struct BoxServiceFactory(Inner); +pub struct BoxServiceFactory(Inner); /// Create boxed service factory -pub fn factory( - factory: T, -) -> BoxServiceFactory +pub fn factory( + factory: SF, +) -> BoxServiceFactory where - T: ServiceFactory + 'static, - T::Request: 'static, - T::Response: 'static, - T::Service: 'static, - T::Future: 'static, - T::Error: 'static, - T::InitError: 'static, + SF: ServiceFactory + 'static, + Req: 'static, + SF::Response: 'static, + SF::Service: 'static, + SF::Future: 'static, + SF::Error: 'static, + SF::InitError: 'static, { BoxServiceFactory(Box::new(FactoryWrapper { factory, @@ -33,78 +33,75 @@ where } /// Create boxed service -pub fn service(service: T) -> BoxService +pub fn service(service: S) -> BoxService where - T: Service + 'static, - T::Future: 'static, + S: Service + 'static, + Req: 'static, + S::Future: 'static, { - Box::new(ServiceWrapper(service)) + Box::new(ServiceWrapper(service, PhantomData)) } type Inner = Box< dyn ServiceFactory< + Req, Config = C, - Request = Req, Response = Res, Error = Err, InitError = InitErr, Service = BoxService, - Future = BoxFuture, InitErr>, + Future = BoxFuture, InitErr>>, >, >; -impl ServiceFactory for BoxServiceFactory +impl ServiceFactory + for BoxServiceFactory where Req: 'static, Res: 'static, Err: 'static, InitErr: 'static, { - type Request = Req; type Response = Res; type Error = Err; type InitError = InitErr; type Config = C; type Service = BoxService; - type Future = BoxFuture; + type Future = BoxFuture>; fn new_service(&self, cfg: C) -> Self::Future { self.0.new_service(cfg) } } -struct FactoryWrapper { - factory: T, - _t: std::marker::PhantomData, +struct FactoryWrapper +where + SF: ServiceFactory, +{ + factory: SF, + _t: PhantomData<(C, Req)>, } -impl ServiceFactory for FactoryWrapper +impl ServiceFactory for FactoryWrapper where Req: 'static, Res: 'static, Err: 'static, InitErr: 'static, - T: ServiceFactory< - Config = C, - Request = Req, - Response = Res, - Error = Err, - InitError = InitErr, - >, - T::Future: 'static, - T::Service: 'static, - ::Future: 'static, + SF: ServiceFactory, + SF::Future: 'static, + SF::Service: 'static, + >::Future: 'static, { - type Request = Req; type Response = Res; type Error = Err; type InitError = InitErr; - type Config = C; + type Config = Cfg; type Service = BoxService; - type Future = BoxFuture; + type Future = BoxFuture>; - fn new_service(&self, cfg: C) -> Self::Future { + fn new_service(&self, cfg: Cfg) -> Self::Future { Box::pin( self.factory .new_service(cfg) @@ -113,33 +110,33 @@ where } } -struct ServiceWrapper(T); +struct ServiceWrapper, Req>(S, PhantomData); -impl ServiceWrapper +impl ServiceWrapper where - T: Service + 'static, - T::Future: 'static, + S: Service + 'static, + Req: 'static, + S::Future: 'static, { - fn boxed(service: T) -> BoxService { - Box::new(ServiceWrapper(service)) + fn boxed(service: S) -> BoxService { + Box::new(ServiceWrapper(service, PhantomData)) } } -impl Service for ServiceWrapper +impl Service for ServiceWrapper where - T: Service, - T::Future: 'static, + S: Service, + S::Future: 'static, { - type Request = Req; type Response = Res; type Error = Err; - type Future = BoxFuture; + type Future = BoxFuture>; fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll> { self.0.poll_ready(ctx) } - fn call(&mut self, req: Self::Request) -> Self::Future { + fn call(&mut self, req: Req) -> Self::Future { Box::pin(self.0.call(req)) } } diff --git a/actix-service/src/fn_service.rs b/actix-service/src/fn_service.rs index 08690a5d..7d15304d 100644 --- a/actix-service/src/fn_service.rs +++ b/actix-service/src/fn_service.rs @@ -53,9 +53,11 @@ where /// Ok(()) /// } /// ``` -pub fn fn_factory(f: F) -> FnServiceNoConfig +pub fn fn_factory( + f: F, +) -> FnServiceNoConfig where - Srv: Service, + Srv: Service, F: Fn() -> Fut, Fut: Future>, { @@ -92,13 +94,13 @@ where /// Ok(()) /// } /// ``` -pub fn fn_factory_with_config( +pub fn fn_factory_with_config( f: F, -) -> FnServiceConfig +) -> FnServiceConfig where F: Fn(Cfg) -> Fut, Fut: Future>, - Srv: Service, + Srv: Service, { FnServiceConfig::new(f) } @@ -132,12 +134,11 @@ where } } -impl Service for FnService +impl Service for FnService where F: FnMut(Req) -> Fut, Fut: Future>, { - type Request = Req; type Response = Res; type Error = Err; type Future = Fut; @@ -151,7 +152,7 @@ where } } -impl IntoService> for F +impl IntoService, Req> for F where F: FnMut(Req) -> Fut, Fut: Future>, @@ -190,12 +191,11 @@ where } } -impl Service for FnServiceFactory +impl Service for FnServiceFactory where F: FnMut(Req) -> Fut + Clone, Fut: Future>, { - type Request = Req; type Response = Res; type Error = Err; type Future = Fut; @@ -204,17 +204,17 @@ where Poll::Ready(Ok(())) } - fn call(&mut self, req: Self::Request) -> Self::Future { + fn call(&mut self, req: Req) -> Self::Future { (self.f)(req) } } -impl ServiceFactory for FnServiceFactory +impl ServiceFactory + for FnServiceFactory where F: FnMut(Req) -> Fut + Clone, Fut: Future>, { - type Request = Req; type Response = Res; type Error = Err; @@ -229,7 +229,7 @@ where } impl - IntoServiceFactory> for F + IntoServiceFactory, Req> for F where F: Fn(Req) -> Fut + Clone, Fut: Future>, @@ -240,32 +240,32 @@ where } /// Convert `Fn(&Config) -> Future` fn to NewService -pub struct FnServiceConfig +pub struct FnServiceConfig where F: Fn(Cfg) -> Fut, Fut: Future>, - Srv: Service, + Srv: Service, { f: F, - _t: PhantomData<(Fut, Cfg, Srv, Err)>, + _t: PhantomData<(Fut, Cfg, Req, Srv, Err)>, } -impl FnServiceConfig +impl FnServiceConfig where F: Fn(Cfg) -> Fut, Fut: Future>, - Srv: Service, + Srv: Service, { fn new(f: F) -> Self { FnServiceConfig { f, _t: PhantomData } } } -impl Clone for FnServiceConfig +impl Clone for FnServiceConfig where F: Fn(Cfg) -> Fut + Clone, Fut: Future>, - Srv: Service, + Srv: Service, { fn clone(&self) -> Self { FnServiceConfig { @@ -275,13 +275,13 @@ where } } -impl ServiceFactory for FnServiceConfig +impl ServiceFactory + for FnServiceConfig where F: Fn(Cfg) -> Fut, Fut: Future>, - Srv: Service, + Srv: Service, { - type Request = Srv::Request; type Response = Srv::Response; type Error = Srv::Error; @@ -296,64 +296,65 @@ where } /// Converter for `Fn() -> Future` fn -pub struct FnServiceNoConfig +pub struct FnServiceNoConfig where - F: Fn() -> R, - S: Service, - R: Future>, + F: Fn() -> Fut, + Srv: Service, + Fut: Future>, { f: F, - _t: PhantomData, + _t: PhantomData<(Cfg, Req)>, } -impl FnServiceNoConfig +impl FnServiceNoConfig where - F: Fn() -> R, - R: Future>, - S: Service, + F: Fn() -> Fut, + Fut: Future>, + Srv: Service, { fn new(f: F) -> Self { Self { f, _t: PhantomData } } } -impl ServiceFactory for FnServiceNoConfig +impl ServiceFactory + for FnServiceNoConfig where - F: Fn() -> R, - R: Future>, - S: Service, + F: Fn() -> Fut, + Fut: Future>, + Srv: Service, { - type Request = S::Request; - type Response = S::Response; - type Error = S::Error; - type Service = S; - type Config = C; - type InitError = E; - type Future = R; + type Response = Srv::Response; + type Error = Srv::Error; + type Service = Srv; + type Config = Cfg; + type InitError = Err; + type Future = Fut; - fn new_service(&self, _: C) -> Self::Future { + fn new_service(&self, _: Cfg) -> Self::Future { (self.f)() } } -impl Clone for FnServiceNoConfig +impl Clone for FnServiceNoConfig where - F: Fn() -> R + Clone, - R: Future>, - S: Service, + F: Fn() -> Fut + Clone, + Fut: Future>, + Srv: Service, { fn clone(&self) -> Self { Self::new(self.f.clone()) } } -impl IntoServiceFactory> for F +impl + IntoServiceFactory, Req> for F where - F: Fn() -> R, - R: Future>, - S: Service, + F: Fn() -> Fut, + Fut: Future>, + Srv: Service, { - fn into_factory(self) -> FnServiceNoConfig { + fn into_factory(self) -> FnServiceNoConfig { FnServiceNoConfig::new(self) } } diff --git a/actix-service/src/lib.rs b/actix-service/src/lib.rs index 96c878e3..2dfa0dd7 100644 --- a/actix-service/src/lib.rs +++ b/actix-service/src/lib.rs @@ -72,14 +72,11 @@ pub use self::transform::{apply, Transform}; /// ```rust,ignore /// async fn my_service(req: u8) -> Result; /// ``` -pub trait Service { - /// Requests handled by the service. - type Request; - +pub trait Service { /// Responses given by the service. type Response; - /// Errors produced by the service. + /// Errors produced by the service when polling readiness or executing call. type Error; /// The future response value. @@ -109,7 +106,7 @@ pub trait Service { /// /// Calling `call` without calling `poll_ready` is permitted. The /// implementation must be resilient to this fact. - fn call(&mut self, req: Self::Request) -> Self::Future; + fn call(&mut self, req: Req) -> Self::Future; /// Map this service's output to a different type, returning a new service /// of the resulting type. @@ -120,7 +117,7 @@ pub trait Service { /// Note that this function consumes the receiving service and returns a /// wrapped version of it, similar to the existing `map` methods in the /// standard library. - fn map(self, f: F) -> crate::dev::Map + fn map(self, f: F) -> crate::dev::Map where Self: Sized, F: FnMut(Self::Response) -> R, @@ -136,7 +133,7 @@ pub trait Service { /// /// Note that this function consumes the receiving service and returns a /// wrapped version of it. - fn map_err(self, f: F) -> crate::dev::MapErr + fn map_err(self, f: F) -> crate::dev::MapErr where Self: Sized, F: Fn(Self::Error) -> E, @@ -154,10 +151,7 @@ pub trait Service { /// requests on that new TCP stream. /// /// `Config` is a service factory configuration type. -pub trait ServiceFactory { - /// Requests handled by the created services. - type Request; - +pub trait ServiceFactory { /// Responses given by the created services. type Response; @@ -168,11 +162,7 @@ pub trait ServiceFactory { type Config; /// The kind of `Service` created by this factory. - type Service: Service< - Request = Self::Request, - Response = Self::Response, - Error = Self::Error, - >; + type Service: Service; /// Errors potentially raised while building a service. type InitError; @@ -185,7 +175,7 @@ pub trait ServiceFactory { /// Map this service's output to a different type, returning a new service /// of the resulting type. - fn map(self, f: F) -> crate::map::MapServiceFactory + fn map(self, f: F) -> crate::map::MapServiceFactory where Self: Sized, F: FnMut(Self::Response) -> R + Clone, @@ -194,7 +184,7 @@ pub trait ServiceFactory { } /// Map this service's error to a different error, returning a new service. - fn map_err(self, f: F) -> crate::map_err::MapErrServiceFactory + fn map_err(self, f: F) -> crate::map_err::MapErrServiceFactory where Self: Sized, F: Fn(Self::Error) -> E + Clone, @@ -203,7 +193,7 @@ pub trait ServiceFactory { } /// Map this factory's init error to a different error, returning a new service. - fn map_init_err(self, f: F) -> crate::map_init_err::MapInitErr + fn map_init_err(self, f: F) -> crate::map_init_err::MapInitErr where Self: Sized, F: Fn(Self::InitError) -> E + Clone, @@ -212,11 +202,10 @@ pub trait ServiceFactory { } } -impl<'a, S> Service for &'a mut S +impl<'a, S, Req> Service for &'a mut S where - S: Service + 'a, + S: Service + 'a, { - type Request = S::Request; type Response = S::Response; type Error = S::Error; type Future = S::Future; @@ -225,16 +214,15 @@ where (**self).poll_ready(ctx) } - fn call(&mut self, request: Self::Request) -> S::Future { + fn call(&mut self, request: Req) -> S::Future { (**self).call(request) } } -impl Service for Box +impl Service for Box where - S: Service + ?Sized, + S: Service + ?Sized, { - type Request = S::Request; type Response = S::Response; type Error = S::Error; type Future = S::Future; @@ -243,16 +231,15 @@ where (**self).poll_ready(ctx) } - fn call(&mut self, request: Self::Request) -> S::Future { + fn call(&mut self, request: Req) -> S::Future { (**self).call(request) } } -impl Service for RefCell +impl Service for RefCell where - S: Service, + S: Service, { - type Request = S::Request; type Response = S::Response; type Error = S::Error; type Future = S::Future; @@ -261,16 +248,15 @@ where self.borrow_mut().poll_ready(ctx) } - fn call(&mut self, request: Self::Request) -> S::Future { + fn call(&mut self, request: Req) -> S::Future { self.borrow_mut().call(request) } } -impl Service for Rc> +impl Service for Rc> where - S: Service, + S: Service, { - type Request = S::Request; type Response = S::Response; type Error = S::Error; type Future = S::Future; @@ -279,16 +265,15 @@ where self.borrow_mut().poll_ready(ctx) } - fn call(&mut self, request: Self::Request) -> S::Future { + fn call(&mut self, request: Req) -> S::Future { (&mut (**self).borrow_mut()).call(request) } } -impl ServiceFactory for Rc +impl ServiceFactory for Rc where - S: ServiceFactory, + S: ServiceFactory, { - type Request = S::Request; type Response = S::Response; type Error = S::Error; type Config = S::Config; @@ -301,11 +286,10 @@ where } } -impl ServiceFactory for Arc +impl ServiceFactory for Arc where - S: ServiceFactory, + S: ServiceFactory, { - type Request = S::Request; type Response = S::Response; type Error = S::Error; type Config = S::Config; @@ -319,52 +303,52 @@ where } /// Trait for types that can be converted to a `Service` -pub trait IntoService +pub trait IntoService where - T: Service, + S: Service, { /// Convert to a `Service` - fn into_service(self) -> T; + fn into_service(self) -> S; } /// Trait for types that can be converted to a `ServiceFactory` -pub trait IntoServiceFactory +pub trait IntoServiceFactory where - T: ServiceFactory, + SF: ServiceFactory, { /// Convert `Self` to a `ServiceFactory` - fn into_factory(self) -> T; + fn into_factory(self) -> SF; } -impl IntoService for T +impl IntoService for S where - T: Service, + S: Service, { - fn into_service(self) -> T { + fn into_service(self) -> S { self } } -impl IntoServiceFactory for T +impl IntoServiceFactory for SF where - T: ServiceFactory, + SF: ServiceFactory, { - fn into_factory(self) -> T { + fn into_factory(self) -> SF { self } } -/// Convert object of type `T` to a service `S` -pub fn into_service(tp: T) -> S +/// Convert object of type `U` to a service `S` +pub fn into_service(tp: I) -> S where - S: Service, - T: IntoService, + I: IntoService, + S: Service, { tp.into_service() } pub mod dev { - pub use crate::apply::{Apply, ApplyServiceFactory}; + pub use crate::apply::{Apply, ApplyFactory}; pub use crate::fn_service::{ FnService, FnServiceConfig, FnServiceFactory, FnServiceNoConfig, }; diff --git a/actix-service/src/map.rs b/actix-service/src/map.rs index ec3520a1..04ef8c5f 100644 --- a/actix-service/src/map.rs +++ b/actix-service/src/map.rs @@ -8,18 +8,18 @@ use super::{Service, ServiceFactory}; /// Service for the `map` combinator, changing the type of a service's response. /// /// This is created by the `ServiceExt::map` method. -pub struct Map { +pub struct Map { service: A, f: F, - _t: PhantomData, + _t: PhantomData<(Req, Res)>, } -impl Map { +impl Map { /// Create new `Map` combinator pub(crate) fn new(service: A, f: F) -> Self where - A: Service, - F: FnMut(A::Response) -> Response, + A: Service, + F: FnMut(A::Response) -> Res, { Self { service, @@ -29,7 +29,7 @@ impl Map { } } -impl Clone for Map +impl Clone for Map where A: Clone, F: Clone, @@ -43,52 +43,51 @@ where } } -impl Service for Map +impl Service for Map where - A: Service, - F: FnMut(A::Response) -> Response + Clone, + A: Service, + F: FnMut(A::Response) -> Res + Clone, { - type Request = A::Request; - type Response = Response; + type Response = Res; type Error = A::Error; - type Future = MapFuture; + type Future = MapFuture; fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll> { self.service.poll_ready(ctx) } - fn call(&mut self, req: A::Request) -> Self::Future { + fn call(&mut self, req: Req) -> Self::Future { MapFuture::new(self.service.call(req), self.f.clone()) } } #[pin_project::pin_project] -pub struct MapFuture +pub struct MapFuture where - A: Service, - F: FnMut(A::Response) -> Response, + A: Service, + F: FnMut(A::Response) -> Res, { f: F, #[pin] fut: A::Future, } -impl MapFuture +impl MapFuture where - A: Service, - F: FnMut(A::Response) -> Response, + A: Service, + F: FnMut(A::Response) -> Res, { fn new(fut: A::Future, f: F) -> Self { MapFuture { f, fut } } } -impl Future for MapFuture +impl Future for MapFuture where - A: Service, - F: FnMut(A::Response) -> Response, + A: Service, + F: FnMut(A::Response) -> Res, { - type Output = Result; + type Output = Result; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let this = self.project(); @@ -102,17 +101,17 @@ where } /// `MapNewService` new service combinator -pub struct MapServiceFactory { +pub struct MapServiceFactory { a: A, f: F, - r: PhantomData, + r: PhantomData<(Res, Req)>, } -impl MapServiceFactory { +impl MapServiceFactory { /// Create new `Map` new service instance pub(crate) fn new(a: A, f: F) -> Self where - A: ServiceFactory, + A: ServiceFactory, F: FnMut(A::Response) -> Res, { Self { @@ -123,7 +122,7 @@ impl MapServiceFactory { } } -impl Clone for MapServiceFactory +impl Clone for MapServiceFactory where A: Clone, F: Clone, @@ -137,19 +136,18 @@ where } } -impl ServiceFactory for MapServiceFactory +impl ServiceFactory for MapServiceFactory where - A: ServiceFactory, + A: ServiceFactory, F: FnMut(A::Response) -> Res + Clone, { - type Request = A::Request; type Response = Res; type Error = A::Error; type Config = A::Config; - type Service = Map; + type Service = Map; type InitError = A::InitError; - type Future = MapServiceFuture; + type Future = MapServiceFuture; fn new_service(&self, cfg: A::Config) -> Self::Future { MapServiceFuture::new(self.a.new_service(cfg), self.f.clone()) @@ -157,9 +155,9 @@ where } #[pin_project::pin_project] -pub struct MapServiceFuture +pub struct MapServiceFuture where - A: ServiceFactory, + A: ServiceFactory, F: FnMut(A::Response) -> Res, { #[pin] @@ -167,9 +165,9 @@ where f: Option, } -impl MapServiceFuture +impl MapServiceFuture where - A: ServiceFactory, + A: ServiceFactory, F: FnMut(A::Response) -> Res, { fn new(fut: A::Future, f: F) -> Self { @@ -177,12 +175,12 @@ where } } -impl Future for MapServiceFuture +impl Future for MapServiceFuture where - A: ServiceFactory, + A: ServiceFactory, F: FnMut(A::Response) -> Res, { - type Output = Result, A::InitError>; + type Output = Result, A::InitError>; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let this = self.project(); @@ -204,8 +202,7 @@ mod tests { struct Srv; - impl Service for Srv { - type Request = (); + impl Service<()> for Srv { type Response = (); type Error = (); type Future = Ready>; diff --git a/actix-service/src/map_config.rs b/actix-service/src/map_config.rs index 7a1a1b89..82b1789b 100644 --- a/actix-service/src/map_config.rs +++ b/actix-service/src/map_config.rs @@ -6,121 +6,123 @@ use super::{IntoServiceFactory, ServiceFactory}; /// /// Note that this function consumes the receiving service factory and returns /// a wrapped version of it. -pub fn map_config(factory: U, f: F) -> MapConfig +pub fn map_config(factory: I, f: F) -> MapConfig where - T: ServiceFactory, - U: IntoServiceFactory, - F: Fn(C) -> T::Config, + I: IntoServiceFactory, + SF: ServiceFactory, + F: Fn(Cfg) -> SF::Config, { MapConfig::new(factory.into_factory(), f) } -/// Replace config with unit -pub fn unit_config(factory: U) -> UnitConfig +/// Replace config with unit. +pub fn unit_config(factory: I) -> UnitConfig where - T: ServiceFactory, - U: IntoServiceFactory, + I: IntoServiceFactory, + SF: ServiceFactory, { UnitConfig::new(factory.into_factory()) } /// `map_config()` adapter service factory -pub struct MapConfig { - a: A, - f: F, - e: PhantomData, +pub struct MapConfig { + factory: SF, + cfg_mapper: F, + e: PhantomData<(Cfg, Req)>, } -impl MapConfig { +impl MapConfig { /// Create new `MapConfig` combinator - pub(crate) fn new(a: A, f: F) -> Self + pub(crate) fn new(factory: SF, cfg_mapper: F) -> Self where - A: ServiceFactory, - F: Fn(C) -> A::Config, + SF: ServiceFactory, + F: Fn(Cfg) -> SF::Config, { Self { - a, - f, + factory, + cfg_mapper, e: PhantomData, } } } -impl Clone for MapConfig +impl Clone for MapConfig where - A: Clone, + SF: Clone, F: Clone, { fn clone(&self) -> Self { Self { - a: self.a.clone(), - f: self.f.clone(), + factory: self.factory.clone(), + cfg_mapper: self.cfg_mapper.clone(), e: PhantomData, } } } -impl ServiceFactory for MapConfig +impl ServiceFactory for MapConfig where - A: ServiceFactory, - F: Fn(C) -> A::Config, + SF: ServiceFactory, + F: Fn(Cfg) -> SF::Config, { - type Request = A::Request; - type Response = A::Response; - type Error = A::Error; + type Response = SF::Response; + type Error = SF::Error; - type Config = C; - type Service = A::Service; - type InitError = A::InitError; - type Future = A::Future; + type Config = Cfg; + type Service = SF::Service; + type InitError = SF::InitError; + type Future = SF::Future; - fn new_service(&self, cfg: C) -> Self::Future { - self.a.new_service((self.f)(cfg)) + fn new_service(&self, cfg: Self::Config) -> Self::Future { + let mapped_cfg = (self.cfg_mapper)(cfg); + self.factory.new_service(mapped_cfg) } } /// `unit_config()` config combinator -pub struct UnitConfig { - a: A, - e: PhantomData, +pub struct UnitConfig { + factory: SF, + _phantom: PhantomData<(Cfg, Req)>, } -impl UnitConfig +impl UnitConfig where - A: ServiceFactory, + SF: ServiceFactory, { /// Create new `UnitConfig` combinator - pub(crate) fn new(a: A) -> Self { - Self { a, e: PhantomData } - } -} - -impl Clone for UnitConfig -where - A: Clone, -{ - fn clone(&self) -> Self { + pub(crate) fn new(factory: SF) -> Self { Self { - a: self.a.clone(), - e: PhantomData, + factory, + _phantom: PhantomData, } } } -impl ServiceFactory for UnitConfig +impl Clone for UnitConfig where - A: ServiceFactory, + SF: Clone, { - type Request = A::Request; - type Response = A::Response; - type Error = A::Error; - - type Config = C; - type Service = A::Service; - type InitError = A::InitError; - type Future = A::Future; - - fn new_service(&self, _: C) -> Self::Future { - self.a.new_service(()) + fn clone(&self) -> Self { + Self { + factory: self.factory.clone(), + _phantom: PhantomData, + } + } +} + +impl ServiceFactory for UnitConfig +where + SF: ServiceFactory, +{ + type Response = SF::Response; + type Error = SF::Error; + + type Config = Cfg; + type Service = SF::Service; + type InitError = SF::InitError; + type Future = SF::Future; + + fn new_service(&self, _: Cfg) -> Self::Future { + self.factory.new_service(()) } } diff --git a/actix-service/src/map_err.rs b/actix-service/src/map_err.rs index ee7145c3..ae7442cc 100644 --- a/actix-service/src/map_err.rs +++ b/actix-service/src/map_err.rs @@ -9,18 +9,18 @@ use super::{Service, ServiceFactory}; /// error. /// /// This is created by the `ServiceExt::map_err` method. -pub struct MapErr { - service: A, +pub struct MapErr { + service: S, f: F, - _t: PhantomData, + _t: PhantomData<(E, Req)>, } -impl MapErr { +impl MapErr { /// Create new `MapErr` combinator - pub(crate) fn new(service: A, f: F) -> Self + pub(crate) fn new(service: S, f: F) -> Self where - A: Service, - F: Fn(A::Error) -> E, + S: Service, + F: Fn(S::Error) -> E, { Self { service, @@ -30,9 +30,9 @@ impl MapErr { } } -impl Clone for MapErr +impl Clone for MapErr where - A: Clone, + S: Clone, F: Clone, { fn clone(&self) -> Self { @@ -44,29 +44,28 @@ where } } -impl Service for MapErr +impl Service for MapErr where - A: Service, + A: Service, F: Fn(A::Error) -> E + Clone, { - type Request = A::Request; type Response = A::Response; type Error = E; - type Future = MapErrFuture; + type Future = MapErrFuture; fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll> { self.service.poll_ready(ctx).map_err(&self.f) } - fn call(&mut self, req: A::Request) -> Self::Future { + fn call(&mut self, req: Req) -> Self::Future { MapErrFuture::new(self.service.call(req), self.f.clone()) } } #[pin_project::pin_project] -pub struct MapErrFuture +pub struct MapErrFuture where - A: Service, + A: Service, F: Fn(A::Error) -> E, { f: F, @@ -74,9 +73,9 @@ where fut: A::Future, } -impl MapErrFuture +impl MapErrFuture where - A: Service, + A: Service, F: Fn(A::Error) -> E, { fn new(fut: A::Future, f: F) -> Self { @@ -84,9 +83,9 @@ where } } -impl Future for MapErrFuture +impl Future for MapErrFuture where - A: Service, + A: Service, F: Fn(A::Error) -> E, { type Output = Result; @@ -101,19 +100,19 @@ where /// service's error. /// /// This is created by the `NewServiceExt::map_err` method. -pub struct MapErrServiceFactory +pub struct MapErrServiceFactory where - A: ServiceFactory, + A: ServiceFactory, F: Fn(A::Error) -> E + Clone, { a: A, f: F, - e: PhantomData, + e: PhantomData<(E, Req)>, } -impl MapErrServiceFactory +impl MapErrServiceFactory where - A: ServiceFactory, + A: ServiceFactory, F: Fn(A::Error) -> E + Clone, { /// Create new `MapErr` new service instance @@ -126,9 +125,9 @@ where } } -impl Clone for MapErrServiceFactory +impl Clone for MapErrServiceFactory where - A: ServiceFactory + Clone, + A: ServiceFactory + Clone, F: Fn(A::Error) -> E + Clone, { fn clone(&self) -> Self { @@ -140,19 +139,18 @@ where } } -impl ServiceFactory for MapErrServiceFactory +impl ServiceFactory for MapErrServiceFactory where - A: ServiceFactory, + A: ServiceFactory, F: Fn(A::Error) -> E + Clone, { - type Request = A::Request; type Response = A::Response; type Error = E; type Config = A::Config; - type Service = MapErr; + type Service = MapErr; type InitError = A::InitError; - type Future = MapErrServiceFuture; + type Future = MapErrServiceFuture; fn new_service(&self, cfg: A::Config) -> Self::Future { MapErrServiceFuture::new(self.a.new_service(cfg), self.f.clone()) @@ -160,9 +158,9 @@ where } #[pin_project::pin_project] -pub struct MapErrServiceFuture +pub struct MapErrServiceFuture where - A: ServiceFactory, + A: ServiceFactory, F: Fn(A::Error) -> E, { #[pin] @@ -170,9 +168,9 @@ where f: F, } -impl MapErrServiceFuture +impl MapErrServiceFuture where - A: ServiceFactory, + A: ServiceFactory, F: Fn(A::Error) -> E, { fn new(fut: A::Future, f: F) -> Self { @@ -180,12 +178,12 @@ where } } -impl Future for MapErrServiceFuture +impl Future for MapErrServiceFuture where - A: ServiceFactory, + A: ServiceFactory, F: Fn(A::Error) -> E + Clone, { - type Output = Result, A::InitError>; + type Output = Result, A::InitError>; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let this = self.project(); @@ -206,8 +204,7 @@ mod tests { struct Srv; - impl Service for Srv { - type Request = (); + impl Service<()> for Srv { type Response = (); type Error = (); type Future = Ready>; diff --git a/actix-service/src/map_init_err.rs b/actix-service/src/map_init_err.rs index b1eec072..518daaf6 100644 --- a/actix-service/src/map_init_err.rs +++ b/actix-service/src/map_init_err.rs @@ -6,16 +6,16 @@ use std::task::{Context, Poll}; use super::ServiceFactory; /// `MapInitErr` service combinator -pub struct MapInitErr { +pub struct MapInitErr { a: A, f: F, - e: PhantomData, + e: PhantomData<(Req, Err)>, } -impl MapInitErr +impl MapInitErr where - A: ServiceFactory, - F: Fn(A::InitError) -> E, + A: ServiceFactory, + F: Fn(A::InitError) -> Err, { /// Create new `MapInitErr` combinator pub(crate) fn new(a: A, f: F) -> Self { @@ -27,7 +27,7 @@ where } } -impl Clone for MapInitErr +impl Clone for MapInitErr where A: Clone, F: Clone, @@ -41,19 +41,18 @@ where } } -impl ServiceFactory for MapInitErr +impl ServiceFactory for MapInitErr where - A: ServiceFactory, + A: ServiceFactory, F: Fn(A::InitError) -> E + Clone, { - type Request = A::Request; type Response = A::Response; type Error = A::Error; type Config = A::Config; type Service = A::Service; type InitError = E; - type Future = MapInitErrFuture; + type Future = MapInitErrFuture; fn new_service(&self, cfg: A::Config) -> Self::Future { MapInitErrFuture::new(self.a.new_service(cfg), self.f.clone()) @@ -61,9 +60,9 @@ where } #[pin_project::pin_project] -pub struct MapInitErrFuture +pub struct MapInitErrFuture where - A: ServiceFactory, + A: ServiceFactory, F: Fn(A::InitError) -> E, { f: F, @@ -71,9 +70,9 @@ where fut: A::Future, } -impl MapInitErrFuture +impl MapInitErrFuture where - A: ServiceFactory, + A: ServiceFactory, F: Fn(A::InitError) -> E, { fn new(fut: A::Future, f: F) -> Self { @@ -81,9 +80,9 @@ where } } -impl Future for MapInitErrFuture +impl Future for MapInitErrFuture where - A: ServiceFactory, + A: ServiceFactory, F: Fn(A::InitError) -> E, { type Output = Result; diff --git a/actix-service/src/pipeline.rs b/actix-service/src/pipeline.rs index 75cd6af9..cba7ce78 100644 --- a/actix-service/src/pipeline.rs +++ b/actix-service/src/pipeline.rs @@ -1,5 +1,5 @@ -use std::future::Future; use std::task::{Context, Poll}; +use std::{future::Future, marker::PhantomData}; use crate::and_then::{AndThenService, AndThenServiceFactory}; use crate::and_then_apply_fn::{AndThenApplyFn, AndThenApplyFnFactory}; @@ -10,33 +10,39 @@ use crate::then::{ThenService, ThenServiceFactory}; use crate::{IntoService, IntoServiceFactory, Service, ServiceFactory}; /// Construct new pipeline with one service in pipeline chain. -pub fn pipeline(service: F) -> Pipeline +pub fn pipeline(service: I) -> Pipeline where - F: IntoService, - T: Service, + I: IntoService, + S: Service, { Pipeline { service: service.into_service(), + _phantom: PhantomData, } } /// Construct new pipeline factory with one service factory. -pub fn pipeline_factory(factory: F) -> PipelineFactory +pub fn pipeline_factory(factory: I) -> PipelineFactory where - T: ServiceFactory, - F: IntoServiceFactory, + I: IntoServiceFactory, + SF: ServiceFactory, { PipelineFactory { factory: factory.into_factory(), + _phantom: PhantomData, } } /// Pipeline service - pipeline allows to compose multiple service into one service. -pub struct Pipeline { - service: T, +pub struct Pipeline { + service: S, + _phantom: PhantomData, } -impl Pipeline { +impl Pipeline +where + S: Service, +{ /// Call another service after call to this one has resolved successfully. /// /// This function can be used to chain two services together and ensure that @@ -46,41 +52,40 @@ impl Pipeline { /// /// Note that this function consumes the receiving service and returns a /// wrapped version of it. - pub fn and_then( + pub fn and_then( self, - service: F, - ) -> Pipeline< - impl Service + Clone, - > + service: I, + ) -> Pipeline + Clone, Req> where Self: Sized, - F: IntoService, - U: Service, + I: IntoService, + S1: Service, { Pipeline { service: AndThenService::new(self.service, service.into_service()), + _phantom: PhantomData, } } - /// Apply function to specified service and use it as a next service in - /// chain. + /// Apply function to specified service and use it as a next service in chain. /// - /// Short version of `pipeline_factory(...).and_then(apply_fn_factory(...))` - pub fn and_then_apply_fn( + /// Short version of `pipeline_factory(...).and_then(apply_fn(...))` + pub fn and_then_apply_fn( self, service: I, - f: F, - ) -> Pipeline + Clone> + wrap_fn: F, + ) -> Pipeline + Clone, Req> where Self: Sized, - I: IntoService, - U: Service, - F: FnMut(T::Response, &mut U) -> Fut, + I: IntoService, + S1: Service, + F: FnMut(S::Response, &mut S1) -> Fut, Fut: Future>, - Err: From + From, + Err: From + From, { Pipeline { - service: AndThenApplyFn::new(self.service, service.into_service(), f), + service: AndThenApplyFn::new(self.service, service.into_service(), wrap_fn), + _phantom: PhantomData, } } @@ -89,19 +94,18 @@ impl Pipeline { /// /// Note that this function consumes the receiving pipeline and returns a /// wrapped version of it. - pub fn then( + pub fn then( self, service: F, - ) -> Pipeline< - impl Service + Clone, - > + ) -> Pipeline + Clone, Req> where Self: Sized, - F: IntoService, - U: Service, Error = T::Error>, + F: IntoService>, + S1: Service, Error = S::Error>, { Pipeline { service: ThenService::new(self.service, service.into_service()), + _phantom: PhantomData, } } @@ -114,13 +118,14 @@ impl Pipeline { /// Note that this function consumes the receiving service and returns a /// wrapped version of it, similar to the existing `map` methods in the /// standard library. - pub fn map(self, f: F) -> Pipeline> + pub fn map(self, f: F) -> Pipeline, Req> where Self: Sized, - F: FnMut(T::Response) -> R, + F: FnMut(S::Response) -> R, { Pipeline { service: Map::new(self.service, f), + _phantom: PhantomData, } } @@ -132,114 +137,118 @@ impl Pipeline { /// /// Note that this function consumes the receiving service and returns a /// wrapped version of it. - pub fn map_err(self, f: F) -> Pipeline> + pub fn map_err(self, f: F) -> Pipeline, Req> where Self: Sized, - F: Fn(T::Error) -> E, + F: Fn(S::Error) -> E, { Pipeline { service: MapErr::new(self.service, f), + _phantom: PhantomData, } } } -impl Clone for Pipeline +impl Clone for Pipeline where T: Clone, { fn clone(&self) -> Self { Pipeline { service: self.service.clone(), + _phantom: PhantomData, } } } -impl Service for Pipeline { - type Request = T::Request; - type Response = T::Response; - type Error = T::Error; - type Future = T::Future; +impl, Req> Service for Pipeline { + type Response = S::Response; + type Error = S::Error; + type Future = S::Future; #[inline] - fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll> { + fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll> { self.service.poll_ready(ctx) } #[inline] - fn call(&mut self, req: T::Request) -> Self::Future { + fn call(&mut self, req: Req) -> Self::Future { self.service.call(req) } } /// Pipeline factory -pub struct PipelineFactory { - factory: T, +pub struct PipelineFactory { + factory: SF, + _phantom: PhantomData, } -impl PipelineFactory { +impl PipelineFactory +where + SF: ServiceFactory, +{ /// Call another service after call to this one has resolved successfully. - pub fn and_then( + pub fn and_then( self, - factory: F, + factory: I, ) -> PipelineFactory< impl ServiceFactory< - Request = T::Request, - Response = U::Response, - Error = T::Error, - Config = T::Config, - InitError = T::InitError, - Service = impl Service< - Request = T::Request, - Response = U::Response, - Error = T::Error, - > + Clone, + Req, + Response = SF1::Response, + Error = SF::Error, + Config = SF::Config, + InitError = SF::InitError, + Service = impl Service + Clone, > + Clone, + Req, > where Self: Sized, - T::Config: Clone, - F: IntoServiceFactory, - U: ServiceFactory< - Config = T::Config, - Request = T::Response, - Error = T::Error, - InitError = T::InitError, + SF::Config: Clone, + I: IntoServiceFactory, + SF1: ServiceFactory< + SF::Response, + Config = SF::Config, + Error = SF::Error, + InitError = SF::InitError, >, { PipelineFactory { factory: AndThenServiceFactory::new(self.factory, factory.into_factory()), + _phantom: PhantomData, } } - /// Apply function to specified service and use it as a next service in - /// chain. + /// Apply function to specified service and use it as a next service in chain. /// /// Short version of `pipeline_factory(...).and_then(apply_fn_factory(...))` - pub fn and_then_apply_fn( + pub fn and_then_apply_fn( self, factory: I, - f: F, + wrap_fn: F, ) -> PipelineFactory< impl ServiceFactory< - Request = T::Request, + Req, Response = Res, Error = Err, - Config = T::Config, - InitError = T::InitError, - Service = impl Service + Clone, + Config = SF::Config, + InitError = SF::InitError, + Service = impl Service + Clone, > + Clone, + Req, > where Self: Sized, - T::Config: Clone, - I: IntoServiceFactory, - U: ServiceFactory, - F: FnMut(T::Response, &mut U::Service) -> Fut + Clone, + SF::Config: Clone, + I: IntoServiceFactory, + SF1: ServiceFactory, + F: FnMut(SF::Response, &mut SF1::Service) -> Fut + Clone, Fut: Future>, - Err: From + From, + Err: From + From, { PipelineFactory { - factory: AndThenApplyFnFactory::new(self.factory, factory.into_factory(), f), + factory: AndThenApplyFnFactory::new(self.factory, factory.into_factory(), wrap_fn), + _phantom: PhantomData, } } @@ -249,96 +258,103 @@ impl PipelineFactory { /// /// Note that this function consumes the receiving pipeline and returns a /// wrapped version of it. - pub fn then( + pub fn then( self, - factory: F, + factory: I, ) -> PipelineFactory< impl ServiceFactory< - Request = T::Request, - Response = U::Response, - Error = T::Error, - Config = T::Config, - InitError = T::InitError, - Service = impl Service< - Request = T::Request, - Response = U::Response, - Error = T::Error, - > + Clone, + Req, + Response = SF1::Response, + Error = SF::Error, + Config = SF::Config, + InitError = SF::InitError, + Service = impl Service + Clone, > + Clone, + Req, > where Self: Sized, - T::Config: Clone, - F: IntoServiceFactory, - U: ServiceFactory< - Config = T::Config, - Request = Result, - Error = T::Error, - InitError = T::InitError, + SF::Config: Clone, + I: IntoServiceFactory>, + SF1: ServiceFactory< + Result, + Config = SF::Config, + Error = SF::Error, + InitError = SF::InitError, >, { PipelineFactory { factory: ThenServiceFactory::new(self.factory, factory.into_factory()), + _phantom: PhantomData, } } /// Map this service's output to a different type, returning a new service /// of the resulting type. - pub fn map(self, f: F) -> PipelineFactory> + pub fn map(self, f: F) -> PipelineFactory, Req> where Self: Sized, - F: FnMut(T::Response) -> R + Clone, + F: FnMut(SF::Response) -> R + Clone, { PipelineFactory { factory: MapServiceFactory::new(self.factory, f), + _phantom: PhantomData, } } /// Map this service's error to a different error, returning a new service. - pub fn map_err(self, f: F) -> PipelineFactory> + pub fn map_err( + self, + f: F, + ) -> PipelineFactory, Req> where Self: Sized, - F: Fn(T::Error) -> E + Clone, + F: Fn(SF::Error) -> E + Clone, { PipelineFactory { factory: MapErrServiceFactory::new(self.factory, f), + _phantom: PhantomData, } } /// Map this factory's init error to a different error, returning a new service. - pub fn map_init_err(self, f: F) -> PipelineFactory> + pub fn map_init_err(self, f: F) -> PipelineFactory, Req> where Self: Sized, - F: Fn(T::InitError) -> E + Clone, + F: Fn(SF::InitError) -> E + Clone, { PipelineFactory { factory: MapInitErr::new(self.factory, f), + _phantom: PhantomData, } } } -impl Clone for PipelineFactory +impl Clone for PipelineFactory where T: Clone, { fn clone(&self) -> Self { PipelineFactory { factory: self.factory.clone(), + _phantom: PhantomData, } } } -impl ServiceFactory for PipelineFactory { - type Config = T::Config; - type Request = T::Request; - type Response = T::Response; - type Error = T::Error; - type Service = T::Service; - type InitError = T::InitError; - type Future = T::Future; +impl ServiceFactory for PipelineFactory +where + SF: ServiceFactory, +{ + type Config = SF::Config; + type Response = SF::Response; + type Error = SF::Error; + type Service = SF::Service; + type InitError = SF::InitError; + type Future = SF::Future; #[inline] - fn new_service(&self, cfg: T::Config) -> Self::Future { + fn new_service(&self, cfg: SF::Config) -> Self::Future { self.factory.new_service(cfg) } } diff --git a/actix-service/src/then.rs b/actix-service/src/then.rs index 3da46cbb..021e5484 100644 --- a/actix-service/src/then.rs +++ b/actix-service/src/then.rs @@ -1,8 +1,8 @@ -use std::cell::RefCell; use std::future::Future; use std::pin::Pin; use std::rc::Rc; use std::task::{Context, Poll}; +use std::{cell::RefCell, marker::PhantomData}; use super::{Service, ServiceFactory}; @@ -10,34 +10,33 @@ use super::{Service, ServiceFactory}; /// another service. /// /// This is created by the `Pipeline::then` method. -pub(crate) struct ThenService(Rc>); +pub(crate) struct ThenService(Rc>, PhantomData); -impl ThenService { +impl ThenService { /// Create new `.then()` combinator - pub(crate) fn new(a: A, b: B) -> ThenService + pub(crate) fn new(a: A, b: B) -> ThenService where - A: Service, - B: Service, Error = A::Error>, + A: Service, + B: Service, Error = A::Error>, { - Self(Rc::new(RefCell::new((a, b)))) + Self(Rc::new(RefCell::new((a, b))), PhantomData) } } -impl Clone for ThenService { +impl Clone for ThenService { fn clone(&self) -> Self { - ThenService(self.0.clone()) + ThenService(self.0.clone(), PhantomData) } } -impl Service for ThenService +impl Service for ThenService where - A: Service, - B: Service, Error = A::Error>, + A: Service, + B: Service, Error = A::Error>, { - type Request = A::Request; type Response = B::Response; type Error = B::Error; - type Future = ThenServiceResponse; + type Future = ThenServiceResponse; fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { let mut srv = self.0.borrow_mut(); @@ -49,7 +48,7 @@ where } } - fn call(&mut self, req: A::Request) -> Self::Future { + fn call(&mut self, req: Req) -> Self::Future { ThenServiceResponse { state: State::A(self.0.borrow_mut().0.call(req), Some(self.0.clone())), } @@ -57,30 +56,30 @@ where } #[pin_project::pin_project] -pub(crate) struct ThenServiceResponse +pub(crate) struct ThenServiceResponse where - A: Service, - B: Service>, + A: Service, + B: Service>, { #[pin] - state: State, + state: State, } #[pin_project::pin_project(project = StateProj)] -enum State +enum State where - A: Service, - B: Service>, + A: Service, + B: Service>, { A(#[pin] A::Future, Option>>), B(#[pin] B::Future), Empty, } -impl Future for ThenServiceResponse +impl Future for ThenServiceResponse where - A: Service, - B: Service>, + A: Service, + B: Service>, { type Output = Result; @@ -110,44 +109,43 @@ where } /// `.then()` service factory combinator -pub(crate) struct ThenServiceFactory(Rc<(A, B)>); +pub(crate) struct ThenServiceFactory(Rc<(A, B)>, PhantomData); -impl ThenServiceFactory +impl ThenServiceFactory where - A: ServiceFactory, + A: ServiceFactory, A::Config: Clone, B: ServiceFactory< + Result, Config = A::Config, - Request = Result, Error = A::Error, InitError = A::InitError, >, { /// Create new `AndThen` combinator pub(crate) fn new(a: A, b: B) -> Self { - Self(Rc::new((a, b))) + Self(Rc::new((a, b)), PhantomData) } } -impl ServiceFactory for ThenServiceFactory +impl ServiceFactory for ThenServiceFactory where - A: ServiceFactory, + A: ServiceFactory, A::Config: Clone, B: ServiceFactory< + Result, Config = A::Config, - Request = Result, Error = A::Error, InitError = A::InitError, >, { - type Request = A::Request; type Response = B::Response; type Error = A::Error; type Config = A::Config; - type Service = ThenService; + type Service = ThenService; type InitError = A::InitError; - type Future = ThenServiceFactoryResponse; + type Future = ThenServiceFactoryResponse; fn new_service(&self, cfg: A::Config) -> Self::Future { let srv = &*self.0; @@ -155,19 +153,19 @@ where } } -impl Clone for ThenServiceFactory { +impl Clone for ThenServiceFactory { fn clone(&self) -> Self { - Self(self.0.clone()) + Self(self.0.clone(), PhantomData) } } #[pin_project::pin_project] -pub(crate) struct ThenServiceFactoryResponse +pub(crate) struct ThenServiceFactoryResponse where - A: ServiceFactory, + A: ServiceFactory, B: ServiceFactory< + Result, Config = A::Config, - Request = Result, Error = A::Error, InitError = A::InitError, >, @@ -180,12 +178,12 @@ where b: Option, } -impl ThenServiceFactoryResponse +impl ThenServiceFactoryResponse where - A: ServiceFactory, + A: ServiceFactory, B: ServiceFactory< + Result, Config = A::Config, - Request = Result, Error = A::Error, InitError = A::InitError, >, @@ -200,17 +198,17 @@ where } } -impl Future for ThenServiceFactoryResponse +impl Future for ThenServiceFactoryResponse where - A: ServiceFactory, + A: ServiceFactory, B: ServiceFactory< + Result, Config = A::Config, - Request = Result, Error = A::Error, InitError = A::InitError, >, { - type Output = Result, A::InitError>; + type Output = Result, A::InitError>; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let this = self.project(); @@ -249,8 +247,7 @@ mod tests { #[derive(Clone)] struct Srv1(Rc>); - impl Service for Srv1 { - type Request = Result<&'static str, &'static str>; + impl Service> for Srv1 { type Response = &'static str; type Error = (); type Future = Ready>; @@ -270,8 +267,7 @@ mod tests { struct Srv2(Rc>); - impl Service for Srv2 { - type Request = Result<&'static str, ()>; + impl Service> for Srv2 { type Response = (&'static str, &'static str); type Error = (); type Future = Ready>; diff --git a/actix-service/src/transform.rs b/actix-service/src/transform.rs index 0ebfc5b7..d4d49417 100644 --- a/actix-service/src/transform.rs +++ b/actix-service/src/transform.rs @@ -1,18 +1,18 @@ -use std::future::Future; use std::pin::Pin; use std::rc::Rc; use std::sync::Arc; use std::task::{Context, Poll}; +use std::{future::Future, marker::PhantomData}; use crate::transform_err::TransformMapInitErr; use crate::{IntoServiceFactory, Service, ServiceFactory}; /// Apply transform to a service. -pub fn apply(t: T, factory: U) -> ApplyTransform +pub fn apply(t: T, factory: I) -> ApplyTransform where - S: ServiceFactory, - T: Transform, - U: IntoServiceFactory, + I: IntoServiceFactory, + S: ServiceFactory, + T: Transform, { ApplyTransform::new(t, factory.into_factory()) } @@ -89,10 +89,7 @@ where /// } /// } /// ``` -pub trait Transform { - /// Requests handled by the service. - type Request; - +pub trait Transform { /// Responses given by the service. type Response; @@ -100,11 +97,7 @@ pub trait Transform { type Error; /// The `TransformService` value created by this factory - type Transform: Service< - Request = Self::Request, - Response = Self::Response, - Error = Self::Error, - >; + type Transform: Service; /// Errors produced while building a transform service. type InitError; @@ -117,7 +110,7 @@ pub trait Transform { /// Map this transform's factory error to a different error, /// returning a new transform service factory. - fn map_init_err(self, f: F) -> TransformMapInitErr + fn map_init_err(self, f: F) -> TransformMapInitErr where Self: Sized, F: Fn(Self::InitError) -> E + Clone, @@ -126,11 +119,10 @@ pub trait Transform { } } -impl Transform for Rc +impl Transform for Rc where - T: Transform, + T: Transform, { - type Request = T::Request; type Response = T::Response; type Error = T::Error; type InitError = T::InitError; @@ -142,11 +134,10 @@ where } } -impl Transform for Arc +impl Transform for Arc where - T: Transform, + T: Transform, { - type Request = T::Request; type Response = T::Response; type Error = T::Error; type InitError = T::InitError; @@ -159,38 +150,37 @@ where } /// `Apply` transform to new service -pub struct ApplyTransform(Rc<(T, S)>); +pub struct ApplyTransform(Rc<(T, S)>, PhantomData); -impl ApplyTransform +impl ApplyTransform where - S: ServiceFactory, - T: Transform, + S: ServiceFactory, + T: Transform, { /// Create new `ApplyTransform` new service instance fn new(t: T, service: S) -> Self { - Self(Rc::new((t, service))) + Self(Rc::new((t, service)), PhantomData) } } -impl Clone for ApplyTransform { +impl Clone for ApplyTransform { fn clone(&self) -> Self { - ApplyTransform(self.0.clone()) + ApplyTransform(self.0.clone(), PhantomData) } } -impl ServiceFactory for ApplyTransform +impl ServiceFactory for ApplyTransform where - S: ServiceFactory, - T: Transform, + S: ServiceFactory, + T: Transform, { - type Request = T::Request; type Response = T::Response; type Error = T::Error; type Config = S::Config; type Service = T::Transform; type InitError = T::InitError; - type Future = ApplyTransformFuture; + type Future = ApplyTransformFuture; fn new_service(&self, cfg: S::Config) -> Self::Future { ApplyTransformFuture { @@ -201,30 +191,30 @@ where } #[pin_project::pin_project] -pub struct ApplyTransformFuture +pub struct ApplyTransformFuture where - S: ServiceFactory, - T: Transform, + S: ServiceFactory, + T: Transform, { store: Rc<(T, S)>, #[pin] - state: ApplyTransformFutureState, + state: ApplyTransformFutureState, } #[pin_project::pin_project(project = ApplyTransformFutureStateProj)] -pub enum ApplyTransformFutureState +pub enum ApplyTransformFutureState where - S: ServiceFactory, - T: Transform, + S: ServiceFactory, + T: Transform, { A(#[pin] S::Future), B(#[pin] T::Future), } -impl Future for ApplyTransformFuture +impl Future for ApplyTransformFuture where - S: ServiceFactory, - T: Transform, + S: ServiceFactory, + T: Transform, { type Output = Result; diff --git a/actix-service/src/transform_err.rs b/actix-service/src/transform_err.rs index 9d306f0c..1d1b9576 100644 --- a/actix-service/src/transform_err.rs +++ b/actix-service/src/transform_err.rs @@ -9,65 +9,64 @@ use super::Transform; /// transform's init error. /// /// This is created by the `Transform::map_init_err` method. -pub struct TransformMapInitErr { - t: T, - f: F, - e: PhantomData<(S, E)>, +pub struct TransformMapInitErr { + transform: T, + mapper: F, + _phantom: PhantomData<(S, Req, E)>, } -impl TransformMapInitErr { +impl TransformMapInitErr { pub(crate) fn new(t: T, f: F) -> Self where - T: Transform, + T: Transform, F: Fn(T::InitError) -> E, { Self { - t, - f, - e: PhantomData, + transform: t, + mapper: f, + _phantom: PhantomData, } } } -impl Clone for TransformMapInitErr +impl Clone for TransformMapInitErr where T: Clone, F: Clone, { fn clone(&self) -> Self { Self { - t: self.t.clone(), - f: self.f.clone(), - e: PhantomData, + transform: self.transform.clone(), + mapper: self.mapper.clone(), + _phantom: PhantomData, } } } -impl Transform for TransformMapInitErr +impl Transform for TransformMapInitErr where - T: Transform, + T: Transform, F: Fn(T::InitError) -> E + Clone, { - type Request = T::Request; type Response = T::Response; type Error = T::Error; type Transform = T::Transform; type InitError = E; - type Future = TransformMapInitErrFuture; + type Future = TransformMapInitErrFuture; fn new_transform(&self, service: S) -> Self::Future { TransformMapInitErrFuture { - fut: self.t.new_transform(service), - f: self.f.clone(), + fut: self.transform.new_transform(service), + f: self.mapper.clone(), } } } #[pin_project::pin_project] -pub struct TransformMapInitErrFuture +pub struct TransformMapInitErrFuture where - T: Transform, + T: Transform, F: Fn(T::InitError) -> E, { #[pin] @@ -75,9 +74,9 @@ where f: F, } -impl Future for TransformMapInitErrFuture +impl Future for TransformMapInitErrFuture where - T: Transform, + T: Transform, F: Fn(T::InitError) -> E + Clone, { type Output = Result; diff --git a/actix-tracing/src/lib.rs b/actix-tracing/src/lib.rs index b61ffac8..36aa21f2 100644 --- a/actix-tracing/src/lib.rs +++ b/actix-tracing/src/lib.rs @@ -27,12 +27,11 @@ impl TracingService { } } -impl Service for TracingService +impl Service for TracingService where - S: Service, - F: Fn(&S::Request) -> Option, + S: Service, + F: Fn(&Req) -> Option, { - type Request = S::Request; type Response = S::Response; type Error = S::Error; type Future = Either>; @@ -41,7 +40,7 @@ where self.inner.poll_ready(ctx) } - fn call(&mut self, req: Self::Request) -> Self::Future { + fn call(&mut self, req: Req) -> Self::Future { let span = (self.make_span)(&req); let _enter = span.as_ref().map(|s| s.enter()); @@ -74,18 +73,12 @@ impl TracingTransform { } } -impl Transform for TracingTransform +impl Transform for TracingTransform where - S: Service, - U: ServiceFactory< - Request = S::Request, - Response = S::Response, - Error = S::Error, - Service = S, - >, - F: Fn(&S::Request) -> Option + Clone, + S: Service, + U: ServiceFactory, + F: Fn(&Req) -> Option + Clone, { - type Request = S::Request; type Response = S::Response; type Error = S::Error; type Transform = TracingService; @@ -110,14 +103,14 @@ where /// |req: &Request| Some(span!(Level::INFO, "request", req.id)) /// ); /// ``` -pub fn trace( - service_factory: U, +pub fn trace( + service_factory: I, make_span: F, -) -> ApplyTransform, S> +) -> ApplyTransform, S, Req> where - S: ServiceFactory, - F: Fn(&S::Request) -> Option + Clone, - U: IntoServiceFactory, + I: IntoServiceFactory, + S: ServiceFactory, + F: Fn(&Req) -> Option + Clone, { apply( TracingTransform::new(make_span), diff --git a/actix-utils/Cargo.toml b/actix-utils/Cargo.toml index f5bd5793..fb7ed151 100644 --- a/actix-utils/Cargo.toml +++ b/actix-utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-utils" -version = "3.0.0" +version = "2.0.0" authors = ["Nikolay Kim "] description = "Various network related services and utilities for the Actix ecosystem." keywords = ["network", "framework", "async", "futures"] diff --git a/actix-utils/src/dispatcher.rs b/actix-utils/src/dispatcher.rs index c3cb4f16..1e55aa2c 100644 --- a/actix-utils/src/dispatcher.rs +++ b/actix-utils/src/dispatcher.rs @@ -1,4 +1,4 @@ -//! Framed dispatcher service and related utilities +//! Framed dispatcher service and related utilities. #![allow(type_alias_bounds)] @@ -11,6 +11,7 @@ use actix_codec::{AsyncRead, AsyncWrite, Decoder, Encoder, Framed}; use actix_service::{IntoService, Service}; use futures_core::stream::Stream; use log::debug; +use pin_project_lite::pin_project; use crate::mpsc; @@ -62,12 +63,12 @@ pub enum Message { Close, } -pin_project_lite::pin_project! { +pin_project! { /// Dispatcher is a future that reads frames from Framed object /// and passes them to the service. pub struct Dispatcher where - S: Service::Item, Response = I>, + S: Service<::Item, Response = I>, S::Error: 'static, S::Future: 'static, T: AsyncRead, @@ -86,7 +87,11 @@ pin_project_lite::pin_project! { } } -enum State + Decoder, I> { +enum State +where + S: Service<::Item>, + U: Encoder + Decoder, +{ Processing, Error(DispatcherError), FramedError(DispatcherError), @@ -94,7 +99,11 @@ enum State + Decoder, I> { Stopping, } -impl + Decoder, I> State { +impl State +where + S: Service<::Item>, + U: Encoder + Decoder, +{ fn take_error(&mut self) -> DispatcherError { match mem::replace(self, State::Processing) { State::Error(err) => err, @@ -112,7 +121,7 @@ impl + Decoder, I> State { impl Dispatcher where - S: Service::Item, Response = I>, + S: Service<::Item, Response = I>, S::Error: 'static, S::Future: 'static, T: AsyncRead + AsyncWrite, @@ -121,7 +130,10 @@ where ::Error: fmt::Debug, >::Error: fmt::Debug, { - pub fn new>(framed: Framed, service: F) -> Self { + pub fn new(framed: Framed, service: F) -> Self + where + F: IntoService::Item>, + { let (tx, rx) = mpsc::channel(); Dispatcher { framed, @@ -133,11 +145,14 @@ where } /// Construct new `Dispatcher` instance with customer `mpsc::Receiver` - pub fn with_rx>( + pub fn with_rx( framed: Framed, service: F, rx: mpsc::Receiver, S::Error>>, - ) -> Self { + ) -> Self + where + F: IntoService::Item>, + { let tx = rx.sender(); Dispatcher { framed, @@ -176,7 +191,7 @@ where fn poll_read(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> bool where - S: Service::Item, Response = I>, + S: Service<::Item, Response = I>, S::Error: 'static, S::Future: 'static, T: AsyncRead + AsyncWrite, @@ -220,7 +235,7 @@ where /// write to framed object fn poll_write(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> bool where - S: Service::Item, Response = I>, + S: Service<::Item, Response = I>, S::Error: 'static, S::Future: 'static, T: AsyncRead + AsyncWrite, @@ -271,7 +286,7 @@ where impl Future for Dispatcher where - S: Service::Item, Response = I>, + S: Service<::Item, Response = I>, S::Error: 'static, S::Future: 'static, T: AsyncRead + AsyncWrite, diff --git a/actix-utils/src/timeout.rs b/actix-utils/src/timeout.rs index f3489b85..17647206 100644 --- a/actix-utils/src/timeout.rs +++ b/actix-utils/src/timeout.rs @@ -2,6 +2,7 @@ //! //! If the response does not complete within the specified timeout, the response //! will be aborted. + use core::future::Future; use core::marker::PhantomData; use core::pin::Pin; @@ -10,6 +11,7 @@ use core::{fmt, time}; use actix_rt::time::{delay_for, Delay}; use actix_service::{IntoService, Service, Transform}; +use pin_project_lite::pin_project; /// Applies a timeout to requests. #[derive(Debug)] @@ -77,21 +79,21 @@ impl Clone for Timeout { } } -impl Transform for Timeout +impl Transform for Timeout where - S: Service, + S: Service, { - type Request = S::Request; type Response = S::Response; type Error = TimeoutError; - type Transform = TimeoutService; type InitError = E; + type Transform = TimeoutService; type Future = TimeoutFuture; fn new_transform(&self, service: S) -> Self::Future { let service = TimeoutService { service, timeout: self.timeout, + _phantom: PhantomData, }; TimeoutFuture { @@ -118,40 +120,41 @@ impl Future for TimeoutFuture { /// Applies a timeout to requests. #[derive(Debug, Clone)] -pub struct TimeoutService { +pub struct TimeoutService { service: S, timeout: time::Duration, + _phantom: PhantomData, } -impl TimeoutService +impl TimeoutService where - S: Service, + S: Service, { pub fn new(timeout: time::Duration, service: U) -> Self where - U: IntoService, + U: IntoService, { TimeoutService { timeout, service: service.into_service(), + _phantom: PhantomData, } } } -impl Service for TimeoutService +impl Service for TimeoutService where - S: Service, + S: Service, { - type Request = S::Request; type Response = S::Response; type Error = TimeoutError; - type Future = TimeoutServiceResponse; + type Future = TimeoutServiceResponse; fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { self.service.poll_ready(cx).map_err(TimeoutError::Service) } - fn call(&mut self, request: S::Request) -> Self::Future { + fn call(&mut self, request: Req) -> Self::Future { TimeoutServiceResponse { fut: self.service.call(request), sleep: delay_for(self.timeout), @@ -159,21 +162,24 @@ where } } -pin_project_lite::pin_project! { +pin_project! { /// `TimeoutService` response future #[derive(Debug)] - pub struct TimeoutServiceResponse { + pub struct TimeoutServiceResponse + where + S: Service + { #[pin] - fut: T::Future, + fut: S::Future, sleep: Delay, } } -impl Future for TimeoutServiceResponse +impl Future for TimeoutServiceResponse where - T: Service, + S: Service, { - type Output = Result>; + type Output = Result>; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let this = self.project(); @@ -204,8 +210,7 @@ mod tests { struct SleepService(Duration); - impl Service for SleepService { - type Request = (); + impl Service<()> for SleepService { type Response = (); type Error = (); type Future = LocalBoxFuture<'static, Result<(), ()>>; From 33c9aa6988c5dc80ed064ca4bb720be5c5cff11e Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sun, 27 Dec 2020 04:36:08 +0000 Subject: [PATCH 04/73] bump msrv to 1.46 --- .github/workflows/linux.yml | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 23efaa8a..8ea7823d 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -14,7 +14,7 @@ jobs: fail-fast: false matrix: version: - - 1.42.0 + - 1.46.0 - stable - nightly diff --git a/README.md b/README.md index 827f937f..1f7f063a 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Actix net - framework for composable network services ## Documentation & community resources * [Chat on Gitter](https://gitter.im/actix/actix) -* Minimum supported Rust version: 1.42 or later +* Minimum supported Rust version: 1.46 or later ## Example From 8a58a341a464db928e60309a4b5a66081ee7ecea Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sun, 27 Dec 2020 14:15:42 +0000 Subject: [PATCH 05/73] service improvements (#233) --- .github/workflows/bench.yml | 29 -- actix-connect/src/connector.rs | 4 +- actix-connect/src/resolve.rs | 4 +- actix-connect/src/service.rs | 8 +- actix-connect/src/ssl/openssl.rs | 8 +- actix-connect/src/ssl/rustls.rs | 4 +- actix-service/CHANGES.md | 8 +- actix-service/Cargo.toml | 13 +- actix-service/benches/and_then.rs | 334 ------------------ .../benches/unsafecell_vs_refcell.rs | 110 ------ actix-service/src/and_then.rs | 103 +++--- actix-service/src/and_then_apply_fn.rs | 334 ------------------ actix-service/src/apply.rs | 40 +-- actix-service/src/apply_cfg.rs | 85 +++-- actix-service/src/boxed.rs | 19 +- actix-service/src/fn_service.rs | 14 +- actix-service/src/lib.rs | 39 +- actix-service/src/map.rs | 58 +-- actix-service/src/map_config.rs | 2 +- actix-service/src/map_err.rs | 50 +-- actix-service/src/map_init_err.rs | 31 +- actix-service/src/pipeline.rs | 62 +--- actix-service/src/then.rs | 106 +++--- actix-service/src/transform.rs | 61 ++-- actix-service/src/transform_err.rs | 27 +- actix-tracing/src/lib.rs | 7 +- actix-utils/src/timeout.rs | 6 +- 27 files changed, 387 insertions(+), 1179 deletions(-) delete mode 100644 .github/workflows/bench.yml delete mode 100644 actix-service/benches/and_then.rs delete mode 100644 actix-service/benches/unsafecell_vs_refcell.rs delete mode 100644 actix-service/src/and_then_apply_fn.rs diff --git a/.github/workflows/bench.yml b/.github/workflows/bench.yml deleted file mode 100644 index 7c76e171..00000000 --- a/.github/workflows/bench.yml +++ /dev/null @@ -1,29 +0,0 @@ -name: Benchmark (Linux) - -on: - pull_request: - types: [opened, synchronize, reopened] - push: - branches: - - master - - '1.0' - -jobs: - check_benchmark: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - - name: Install Rust - uses: actions-rs/toolchain@v1 - with: - toolchain: nightly - profile: minimal - override: true - - - name: Check benchmark - uses: actions-rs/cargo@v1 - with: - command: bench - args: --package=actix-service diff --git a/actix-connect/src/connector.rs b/actix-connect/src/connector.rs index e4c86d91..d3ef9813 100644 --- a/actix-connect/src/connector.rs +++ b/actix-connect/src/connector.rs @@ -75,9 +75,7 @@ impl Service> for TcpConnector { #[allow(clippy::type_complexity)] type Future = Either, Ready>>; - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } + actix_service::always_ready!(); fn call(&mut self, req: Connect) -> Self::Future { let port = req.port(); diff --git a/actix-connect/src/resolve.rs b/actix-connect/src/resolve.rs index 85edf0d8..2c75cc0d 100644 --- a/actix-connect/src/resolve.rs +++ b/actix-connect/src/resolve.rs @@ -110,9 +110,7 @@ impl Service> for Resolver { Ready, Self::Error>>, >; - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } + actix_service::always_ready!(); fn call(&mut self, mut req: Connect) -> Self::Future { if req.addr.is_some() { diff --git a/actix-connect/src/service.rs b/actix-connect/src/service.rs index ef5d04da..b942d230 100644 --- a/actix-connect/src/service.rs +++ b/actix-connect/src/service.rs @@ -94,9 +94,7 @@ impl Service> for ConnectService { type Error = ConnectError; type Future = ConnectServiceResponse; - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } + actix_service::always_ready!(); fn call(&mut self, req: Connect) -> Self::Future { ConnectServiceResponse { @@ -163,9 +161,7 @@ impl Service> for TcpConnectService { type Error = ConnectError; type Future = TcpConnectServiceResponse; - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } + actix_service::always_ready!(); fn call(&mut self, req: Connect) -> Self::Future { TcpConnectServiceResponse { diff --git a/actix-connect/src/ssl/openssl.rs b/actix-connect/src/ssl/openssl.rs index e1c6b6fb..a9bcc3c7 100644 --- a/actix-connect/src/ssl/openssl.rs +++ b/actix-connect/src/ssl/openssl.rs @@ -100,9 +100,7 @@ where #[allow(clippy::type_complexity)] type Future = Either, Ready>>; - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } + actix_service::always_ready!(); fn call(&mut self, stream: Connection) -> Self::Future { trace!("SSL Handshake start for: {:?}", stream.host()); @@ -220,9 +218,7 @@ impl Service for OpensslConnectService { type Error = ConnectError; type Future = OpensslConnectServiceResponse; - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } + actix_service::always_ready!(); fn call(&mut self, req: Connect) -> Self::Future { OpensslConnectServiceResponse { diff --git a/actix-connect/src/ssl/rustls.rs b/actix-connect/src/ssl/rustls.rs index 3e646082..984fbe49 100644 --- a/actix-connect/src/ssl/rustls.rs +++ b/actix-connect/src/ssl/rustls.rs @@ -96,9 +96,7 @@ where type Error = std::io::Error; type Future = ConnectAsyncExt; - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } + actix_service::always_ready!(); fn call(&mut self, stream: Connection) -> Self::Future { trace!("SSL Handshake start for: {:?}", stream.host()); diff --git a/actix-service/CHANGES.md b/actix-service/CHANGES.md index 971741e8..3b9a9cc0 100644 --- a/actix-service/CHANGES.md +++ b/actix-service/CHANGES.md @@ -3,10 +3,14 @@ ## Unreleased - 2020-xx-xx * `Service`, other traits, and many type signatures now take the the request type as a type parameter instead of an associated type. [#232] -* Upgrade `pin-project` to `1.0`. - +* Add `always_ready!` and `forward_ready!` macros. [#233] +* Crate is now `no_std`. [#233] +* Migrate pin projections to `pin-project-lite`. [#233] +* Remove `AndThenApplyFn` and Pipeline `and_then_apply_fn`. Use the + `.and_then(apply_fn(...))` construction. [#233] [#232]: https://github.com/actix/actix-net/pull/232 +[#233]: https://github.com/actix/actix-net/pull/233 ## 1.0.6 - 2020-08-09 diff --git a/actix-service/Cargo.toml b/actix-service/Cargo.toml index 1505873b..60818968 100644 --- a/actix-service/Cargo.toml +++ b/actix-service/Cargo.toml @@ -17,17 +17,10 @@ name = "actix_service" path = "src/lib.rs" [dependencies] -futures-util = "0.3.1" -pin-project = "1.0.0" +pin-project-lite = "0.2" +futures-util = { version = "0.3.7", default-features = false } +futures-core = { version = "0.3.7", default-features = false } [dev-dependencies] actix-rt = "1.0.0" criterion = "0.3" - -[[bench]] -name = "unsafecell_vs_refcell" -harness = false - -[[bench]] -name = "and_then" -harness = false diff --git a/actix-service/benches/and_then.rs b/actix-service/benches/and_then.rs deleted file mode 100644 index c8aa315d..00000000 --- a/actix-service/benches/and_then.rs +++ /dev/null @@ -1,334 +0,0 @@ -use actix_service::boxed::BoxFuture; -use actix_service::IntoService; -use actix_service::Service; -/// Benchmark various implementations of and_then -use criterion::{criterion_main, Criterion}; -use futures_util::future::join_all; -use futures_util::future::TryFutureExt; -use std::future::Future; -use std::pin::Pin; -use std::rc::Rc; -use std::task::{Context, Poll}; -use std::{ - cell::{RefCell, UnsafeCell}, - marker::PhantomData, -}; - -/* - * Test services A,B for AndThen service implementations - */ - -async fn svc1(_: ()) -> Result { - Ok(1) -} - -async fn svc2(req: usize) -> Result { - Ok(req + 1) -} - -/* - * AndThenUC - original AndThen service based on UnsafeCell - * Cut down version of actix_service::AndThenService based on actix-service::Cell - */ - -struct AndThenUC(Rc>, PhantomData); - -impl AndThenUC { - fn new(a: A, b: B) -> Self - where - A: Service, - B: Service, - { - Self(Rc::new(UnsafeCell::new((a, b))), PhantomData) - } -} - -impl Clone for AndThenUC { - fn clone(&self) -> Self { - Self(self.0.clone(), PhantomData) - } -} - -impl Service for AndThenUC -where - A: Service, - B: Service, -{ - type Response = B::Response; - type Error = A::Error; - type Future = AndThenServiceResponse; - - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } - - fn call(&mut self, req: Req) -> Self::Future { - let fut = unsafe { &mut *(*self.0).get() }.0.call(req); - AndThenServiceResponse { - state: State::A(fut, Some(self.0.clone())), - _phantom: PhantomData, - } - } -} - -#[pin_project::pin_project] -pub(crate) struct AndThenServiceResponse -where - A: Service, - B: Service, -{ - #[pin] - state: State, - _phantom: PhantomData, -} - -#[pin_project::pin_project(project = StateProj)] -enum State -where - A: Service, - B: Service, -{ - A(#[pin] A::Future, Option>>), - B(#[pin] B::Future), - Empty(PhantomData), -} - -impl Future for AndThenServiceResponse -where - A: Service, - B: Service, -{ - type Output = Result; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let mut this = self.as_mut().project(); - - match this.state.as_mut().project() { - StateProj::A(fut, b) => match fut.poll(cx)? { - Poll::Ready(res) => { - let b = b.take().unwrap(); - this.state.set(State::Empty(PhantomData)); // drop fut A - let fut = unsafe { &mut (*b.get()).1 }.call(res); - this.state.set(State::B(fut)); - self.poll(cx) - } - Poll::Pending => Poll::Pending, - }, - StateProj::B(fut) => fut.poll(cx).map(|r| { - this.state.set(State::Empty(PhantomData)); - r - }), - StateProj::Empty(_) => { - panic!("future must not be polled after it returned `Poll::Ready`") - } - } - } -} - -/* - * AndThenRC - AndThen service based on RefCell - */ - -struct AndThenRC(Rc>, PhantomData); - -impl AndThenRC { - fn new(a: A, b: B) -> Self - where - A: Service, - B: Service, - { - Self(Rc::new(RefCell::new((a, b))), PhantomData) - } -} - -impl Clone for AndThenRC { - fn clone(&self) -> Self { - Self(self.0.clone(), PhantomData) - } -} - -impl Service for AndThenRC -where - A: Service, - B: Service, -{ - type Response = B::Response; - type Error = A::Error; - type Future = AndThenServiceResponseRC; - - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } - - fn call(&mut self, req: Req) -> Self::Future { - let fut = self.0.borrow_mut().0.call(req); - AndThenServiceResponseRC { - state: StateRC::A(fut, Some(self.0.clone())), - } - } -} - -#[pin_project::pin_project] -pub(crate) struct AndThenServiceResponseRC -where - A: Service, - B: Service, -{ - #[pin] - state: StateRC, -} - -#[pin_project::pin_project(project = StateRCProj)] -enum StateRC -where - A: Service, - B: Service, -{ - A(#[pin] A::Future, Option>>), - B(#[pin] B::Future), - Empty(PhantomData), -} - -impl Future for AndThenServiceResponseRC -where - A: Service, - B: Service, -{ - type Output = Result; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let mut this = self.as_mut().project(); - - match this.state.as_mut().project() { - StateRCProj::A(fut, b) => match fut.poll(cx)? { - Poll::Ready(res) => { - let b = b.take().unwrap(); - this.state.set(StateRC::Empty(PhantomData)); // drop fut A - let fut = b.borrow_mut().1.call(res); - this.state.set(StateRC::B(fut)); - self.poll(cx) - } - Poll::Pending => Poll::Pending, - }, - StateRCProj::B(fut) => fut.poll(cx).map(|r| { - this.state.set(StateRC::Empty(PhantomData)); - r - }), - StateRCProj::Empty(_) => { - panic!("future must not be polled after it returned `Poll::Ready`") - } - } - } -} - -/* - * AndThenRCFuture - AndThen service based on RefCell - * and standard futures::future::and_then combinator in a Box - */ - -struct AndThenRCFuture(Rc>, PhantomData); - -impl AndThenRCFuture { - fn new(a: A, b: B) -> Self - where - A: Service, - B: Service, - { - Self(Rc::new(RefCell::new((a, b))), PhantomData) - } -} - -impl Clone for AndThenRCFuture { - fn clone(&self) -> Self { - Self(self.0.clone(), PhantomData) - } -} - -impl Service for AndThenRCFuture -where - A: Service + 'static, - A::Future: 'static, - B: Service + 'static, - B::Future: 'static, -{ - type Response = B::Response; - type Error = A::Error; - type Future = BoxFuture; - - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } - - fn call(&mut self, req: Req) -> Self::Future { - let fut = self.0.borrow_mut().0.call(req); - let core = self.0.clone(); - let fut2 = move |res| (*core).borrow_mut().1.call(res); - Box::pin(fut.and_then(fut2)) - } -} - -/// Criterion Benchmark for async Service -/// Should be used from within criterion group: -/// ```rust,ignore -/// let mut criterion: ::criterion::Criterion<_> = -/// ::criterion::Criterion::default().configure_from_args(); -/// bench_async_service(&mut criterion, ok_service(), "async_service_direct"); -/// ``` -/// -/// Usable for benching Service wrappers: -/// Using minimum service code implementation we first measure -/// time to run minimum service, then measure time with wrapper. -/// -/// Sample output -/// async_service_direct time: [1.0908 us 1.1656 us 1.2613 us] -pub fn bench_async_service(c: &mut Criterion, srv: S, name: &str) -where - S: Service<(), Response = usize, Error = ()> + Clone + 'static, -{ - let mut rt = actix_rt::System::new("test"); - - // start benchmark loops - c.bench_function(name, move |b| { - b.iter_custom(|iters| { - let mut srvs: Vec<_> = (1..iters).map(|_| srv.clone()).collect(); - // exclude request generation, it appears it takes significant time vs call (3us vs 1us) - let start = std::time::Instant::now(); - // benchmark body - rt.block_on(async move { join_all(srvs.iter_mut().map(|srv| srv.call(()))).await }); - // check that at least first request succeeded - start.elapsed() - }) - }); -} - -pub fn service_benches() { - let mut criterion: ::criterion::Criterion<_> = - ::criterion::Criterion::default().configure_from_args(); - bench_async_service( - &mut criterion, - AndThenUC::new(svc1.into_service(), svc2.into_service()), - "AndThen with UnsafeCell", - ); - bench_async_service( - &mut criterion, - AndThenRC::new(svc1.into_service(), svc2.into_service()), - "AndThen with RefCell", - ); - bench_async_service( - &mut criterion, - AndThenUC::new(svc1.into_service(), svc2.into_service()), - "AndThen with UnsafeCell", - ); - bench_async_service( - &mut criterion, - AndThenRC::new(svc1.into_service(), svc2.into_service()), - "AndThen with RefCell", - ); - bench_async_service( - &mut criterion, - AndThenRCFuture::new(svc1.into_service(), svc2.into_service()), - "AndThen with RefCell via future::and_then", - ); -} - -criterion_main!(service_benches); diff --git a/actix-service/benches/unsafecell_vs_refcell.rs b/actix-service/benches/unsafecell_vs_refcell.rs deleted file mode 100644 index cdf91233..00000000 --- a/actix-service/benches/unsafecell_vs_refcell.rs +++ /dev/null @@ -1,110 +0,0 @@ -use actix_service::Service; -use criterion::{criterion_main, Criterion}; -use futures_util::future::join_all; -use futures_util::future::{ok, Ready}; -use std::cell::{RefCell, UnsafeCell}; -use std::rc::Rc; -use std::task::{Context, Poll}; - -struct SrvUC(Rc>); - -impl Default for SrvUC { - fn default() -> Self { - Self(Rc::new(UnsafeCell::new(0))) - } -} - -impl Clone for SrvUC { - fn clone(&self) -> Self { - Self(self.0.clone()) - } -} - -impl Service<()> for SrvUC { - type Response = usize; - type Error = (); - type Future = Ready>; - - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } - - fn call(&mut self, _: ()) -> Self::Future { - unsafe { *(*self.0).get() = *(*self.0).get() + 1 }; - ok(unsafe { *self.0.get() }) - } -} - -struct SrvRC(Rc>); - -impl Default for SrvRC { - fn default() -> Self { - Self(Rc::new(RefCell::new(0))) - } -} - -impl Clone for SrvRC { - fn clone(&self) -> Self { - Self(self.0.clone()) - } -} - -impl Service<()> for SrvRC { - type Response = usize; - type Error = (); - type Future = Ready>; - - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } - - fn call(&mut self, _: ()) -> Self::Future { - let prev = *self.0.borrow(); - *(*self.0).borrow_mut() = prev + 1; - ok(*self.0.borrow()) - } -} - -/// Criterion Benchmark for async Service -/// Should be used from within criterion group: -/// ```rust,ignore -/// let mut criterion: ::criterion::Criterion<_> = -/// ::criterion::Criterion::default().configure_from_args(); -/// bench_async_service(&mut criterion, ok_service(), "async_service_direct"); -/// ``` -/// -/// Usable for benching Service wrappers: -/// Using minimum service code implementation we first measure -/// time to run minimum service, then measure time with wrapper. -/// -/// Sample output -/// async_service_direct time: [1.0908 us 1.1656 us 1.2613 us] -pub fn bench_async_service(c: &mut Criterion, srv: S, name: &str) -where - S: Service<(), Response = usize, Error = ()> + Clone + 'static, -{ - let mut rt = actix_rt::System::new("test"); - - // start benchmark loops - c.bench_function(name, move |b| { - b.iter_custom(|iters| { - let mut srvs: Vec<_> = (1..iters).map(|_| srv.clone()).collect(); - // exclude request generation, it appears it takes significant time vs call (3us vs 1us) - let start = std::time::Instant::now(); - // benchmark body - rt.block_on(async move { join_all(srvs.iter_mut().map(|srv| srv.call(()))).await }); - // check that at least first request succeeded - start.elapsed() - }) - }); -} - -pub fn service_benches() { - let mut criterion: ::criterion::Criterion<_> = - ::criterion::Criterion::default().configure_from_args(); - bench_async_service(&mut criterion, SrvUC::default(), "Service with UnsafeCell"); - bench_async_service(&mut criterion, SrvRC::default(), "Service with RefCell"); - bench_async_service(&mut criterion, SrvUC::default(), "Service with UnsafeCell"); - bench_async_service(&mut criterion, SrvRC::default(), "Service with RefCell"); -} -criterion_main!(service_benches); diff --git a/actix-service/src/and_then.rs b/actix-service/src/and_then.rs index 04caf79d..17d62e8f 100644 --- a/actix-service/src/and_then.rs +++ b/actix-service/src/and_then.rs @@ -1,8 +1,13 @@ -use std::future::Future; -use std::pin::Pin; -use std::rc::Rc; -use std::task::{Context, Poll}; -use std::{cell::RefCell, marker::PhantomData}; +use alloc::rc::Rc; +use core::{ + cell::RefCell, + future::Future, + marker::PhantomData, + pin::Pin, + task::{Context, Poll}, +}; + +use pin_project_lite::pin_project; use super::{Service, ServiceFactory}; @@ -50,30 +55,43 @@ where fn call(&mut self, req: Req) -> Self::Future { AndThenServiceResponse { - state: State::A(self.0.borrow_mut().0.call(req), Some(self.0.clone())), + state: State::A { + fut: self.0.borrow_mut().0.call(req), + b: Some(self.0.clone()), + }, } } } -#[pin_project::pin_project] -pub(crate) struct AndThenServiceResponse -where - A: Service, - B: Service, -{ - #[pin] - state: State, +pin_project! { + pub(crate) struct AndThenServiceResponse + where + A: Service, + B: Service, + { + #[pin] + state: State, + } } -#[pin_project::pin_project(project = StateProj)] -enum State -where - A: Service, - B: Service, -{ - A(#[pin] A::Future, Option>>), - B(#[pin] B::Future), - Empty, +pin_project! { + #[project = StateProj] + enum State + where + A: Service, + B: Service, + { + A { + #[pin] + fut: A::Future, + b: Option>>, + }, + B { + #[pin] + fut: B::Future, + }, + Empty, + } } impl Future for AndThenServiceResponse @@ -87,17 +105,17 @@ where let mut this = self.as_mut().project(); match this.state.as_mut().project() { - StateProj::A(fut, b) => match fut.poll(cx)? { + StateProj::A { fut, b } => match fut.poll(cx)? { Poll::Ready(res) => { let b = b.take().unwrap(); this.state.set(State::Empty); // drop fut A let fut = b.borrow_mut().1.call(res); - this.state.set(State::B(fut)); + this.state.set(State::B { fut }); self.poll(cx) } Poll::Pending => Poll::Pending, }, - StateProj::B(fut) => fut.poll(cx).map(|r| { + StateProj::B { fut } => fut.poll(cx).map(|r| { this.state.set(State::Empty); r }), @@ -191,19 +209,20 @@ where } } -#[pin_project::pin_project] -pub(crate) struct AndThenServiceFactoryResponse -where - A: ServiceFactory, - B: ServiceFactory, -{ - #[pin] - fut_a: A::Future, - #[pin] - fut_b: B::Future, +pin_project! { + pub(crate) struct AndThenServiceFactoryResponse + where + A: ServiceFactory, + B: ServiceFactory, + { + #[pin] + fut_a: A::Future, + #[pin] + fut_b: B::Future, - a: Option, - b: Option, + a: Option, + b: Option, + } } impl AndThenServiceFactoryResponse @@ -254,9 +273,11 @@ where #[cfg(test)] mod tests { - use std::cell::Cell; - use std::rc::Rc; - use std::task::{Context, Poll}; + use alloc::rc::Rc; + use core::{ + cell::Cell, + task::{Context, Poll}, + }; use futures_util::future::{lazy, ok, ready, Ready}; diff --git a/actix-service/src/and_then_apply_fn.rs b/actix-service/src/and_then_apply_fn.rs deleted file mode 100644 index c7bd098c..00000000 --- a/actix-service/src/and_then_apply_fn.rs +++ /dev/null @@ -1,334 +0,0 @@ -use std::cell::RefCell; -use std::future::Future; -use std::marker::PhantomData; -use std::pin::Pin; -use std::rc::Rc; -use std::task::{Context, Poll}; - -use crate::{Service, ServiceFactory}; - -/// `Apply` service combinator -pub(crate) struct AndThenApplyFn -where - S1: Service, - S2: Service, - F: FnMut(S1::Response, &mut S2) -> Fut, - Fut: Future>, - Err: From + From, -{ - svc: Rc>, - _phantom: PhantomData<(Fut, Req, In, Res, Err)>, -} - -impl AndThenApplyFn -where - S1: Service, - S2: Service, - F: FnMut(S1::Response, &mut S2) -> Fut, - Fut: Future>, - Err: From + From, -{ - /// Create new `Apply` combinator - pub(crate) fn new(a: S1, b: S2, wrap_fn: F) -> Self { - Self { - svc: Rc::new(RefCell::new((a, b, wrap_fn))), - _phantom: PhantomData, - } - } -} - -impl Clone - for AndThenApplyFn -where - S1: Service, - S2: Service, - F: FnMut(S1::Response, &mut S2) -> Fut, - Fut: Future>, - Err: From + From, -{ - fn clone(&self) -> Self { - AndThenApplyFn { - svc: self.svc.clone(), - _phantom: PhantomData, - } - } -} - -impl Service - for AndThenApplyFn -where - S1: Service, - S2: Service, - F: FnMut(S1::Response, &mut S2) -> Fut, - Fut: Future>, - Err: From + From, -{ - type Response = Res; - type Error = Err; - type Future = AndThenApplyFnFuture; - - fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { - let mut inner = self.svc.borrow_mut(); - let not_ready = inner.0.poll_ready(cx)?.is_pending(); - if inner.1.poll_ready(cx)?.is_pending() || not_ready { - Poll::Pending - } else { - Poll::Ready(Ok(())) - } - } - - fn call(&mut self, req: Req) -> Self::Future { - let fut = self.svc.borrow_mut().0.call(req); - AndThenApplyFnFuture { - state: State::A(fut, Some(self.svc.clone())), - } - } -} - -#[pin_project::pin_project] -pub(crate) struct AndThenApplyFnFuture -where - S1: Service, - S2: Service, - F: FnMut(S1::Response, &mut S2) -> Fut, - Fut: Future>, - Err: From + From, -{ - #[pin] - state: State, -} - -#[pin_project::pin_project(project = StateProj)] -enum State -where - S1: Service, - S2: Service, - F: FnMut(S1::Response, &mut S2) -> Fut, - Fut: Future>, - Err: From + From, -{ - A(#[pin] S1::Future, Option>>), - B(#[pin] Fut), - Empty(PhantomData), -} - -impl Future - for AndThenApplyFnFuture -where - S1: Service, - S2: Service, - F: FnMut(S1::Response, &mut S2) -> Fut, - Fut: Future>, - Err: From + From, -{ - type Output = Result; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let mut this = self.as_mut().project(); - - match this.state.as_mut().project() { - StateProj::A(fut, b) => match fut.poll(cx)? { - Poll::Ready(res) => { - let b = Option::take(b).unwrap(); - this.state.set(State::Empty(PhantomData)); - let (_, b, f) = &mut *b.borrow_mut(); - let fut = f(res, b); - this.state.set(State::B(fut)); - self.poll(cx) - } - Poll::Pending => Poll::Pending, - }, - StateProj::B(fut) => fut.poll(cx).map(|r| { - this.state.set(State::Empty(PhantomData)); - r - }), - StateProj::Empty(_) => { - panic!("future must not be polled after it returned `Poll::Ready`") - } - } - } -} - -/// `AndThenApplyFn` service factory -pub(crate) struct AndThenApplyFnFactory { - srv: Rc<(SF1, SF2, F)>, - _phantom: PhantomData<(Fut, Req, In, Res, Err)>, -} - -impl - AndThenApplyFnFactory -where - SF1: ServiceFactory, - SF2: ServiceFactory, - F: FnMut(SF1::Response, &mut SF2::Service) -> Fut + Clone, - Fut: Future>, - Err: From + From, -{ - /// Create new `ApplyNewService` new service instance - pub(crate) fn new(a: SF1, b: SF2, wrap_fn: F) -> Self { - Self { - srv: Rc::new((a, b, wrap_fn)), - _phantom: PhantomData, - } - } -} - -impl Clone - for AndThenApplyFnFactory -{ - fn clone(&self) -> Self { - Self { - srv: self.srv.clone(), - _phantom: PhantomData, - } - } -} - -impl ServiceFactory - for AndThenApplyFnFactory -where - SF1: ServiceFactory, - SF1::Config: Clone, - SF2: ServiceFactory, - F: FnMut(SF1::Response, &mut SF2::Service) -> Fut + Clone, - Fut: Future>, - Err: From + From, -{ - type Response = Res; - type Error = Err; - type Service = AndThenApplyFn; - type Config = SF1::Config; - type InitError = SF1::InitError; - type Future = AndThenApplyFnFactoryResponse; - - fn new_service(&self, cfg: SF1::Config) -> Self::Future { - let srv = &*self.srv; - AndThenApplyFnFactoryResponse { - s1: None, - s2: None, - wrap_fn: srv.2.clone(), - fut_s1: srv.0.new_service(cfg.clone()), - fut_s2: srv.1.new_service(cfg), - _phantom: PhantomData, - } - } -} - -#[pin_project::pin_project] -pub(crate) struct AndThenApplyFnFactoryResponse -where - SF1: ServiceFactory, - SF2: ServiceFactory, - F: FnMut(SF1::Response, &mut SF2::Service) -> Fut + Clone, - Fut: Future>, - Err: From, - Err: From, -{ - #[pin] - fut_s1: SF1::Future, - #[pin] - fut_s2: SF2::Future, - wrap_fn: F, - s1: Option, - s2: Option, - _phantom: PhantomData, -} - -impl Future - for AndThenApplyFnFactoryResponse -where - SF1: ServiceFactory, - SF2: ServiceFactory, - F: FnMut(SF1::Response, &mut SF2::Service) -> Fut + Clone, - Fut: Future>, - Err: From + From, -{ - type Output = Result< - AndThenApplyFn, - SF1::InitError, - >; - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let this = self.project(); - - if this.s1.is_none() { - if let Poll::Ready(service) = this.fut_s1.poll(cx)? { - *this.s1 = Some(service); - } - } - - if this.s2.is_none() { - if let Poll::Ready(service) = this.fut_s2.poll(cx)? { - *this.s2 = Some(service); - } - } - - if this.s1.is_some() && this.s2.is_some() { - Poll::Ready(Ok(AndThenApplyFn { - svc: Rc::new(RefCell::new(( - Option::take(this.s1).unwrap(), - Option::take(this.s2).unwrap(), - this.wrap_fn.clone(), - ))), - _phantom: PhantomData, - })) - } else { - Poll::Pending - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - use futures_util::future::{lazy, ok, Ready, TryFutureExt}; - - use crate::{fn_service, pipeline, pipeline_factory, Service, ServiceFactory}; - - #[derive(Clone)] - struct Srv; - - impl Service for Srv { - type Response = (); - type Error = (); - type Future = Ready>; - - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } - - fn call(&mut self, req: u8) -> Self::Future { - let _ = req; - ok(()) - } - } - - #[actix_rt::test] - async fn test_service() { - let mut srv = pipeline(ok).and_then_apply_fn(Srv, |req: &'static str, s| { - s.call(1).map_ok(move |res| (req, res)) - }); - let res = lazy(|cx| srv.poll_ready(cx)).await; - assert!(res.is_ready()); - - let res = srv.call("srv").await; - assert!(res.is_ok()); - assert_eq!(res.unwrap(), ("srv", ())); - } - - #[actix_rt::test] - async fn test_service_factory() { - let new_srv = pipeline_factory(|| ok::<_, ()>(fn_service(ok))).and_then_apply_fn( - || ok(Srv), - |req: &'static str, s| s.call(1).map_ok(move |res| (req, res)), - ); - let mut srv = new_srv.new_service(()).await.unwrap(); - let res = lazy(|cx| srv.poll_ready(cx)).await; - assert!(res.is_ready()); - - let res = srv.call("srv").await; - assert!(res.is_ok()); - assert_eq!(res.unwrap(), ("srv", ())); - } -} diff --git a/actix-service/src/apply.rs b/actix-service/src/apply.rs index 27a09684..8db6018f 100644 --- a/actix-service/src/apply.rs +++ b/actix-service/src/apply.rs @@ -1,11 +1,12 @@ -use std::{ +use core::{ future::Future, marker::PhantomData, pin::Pin, task::{Context, Poll}, }; -use futures_util::ready; +use futures_core::ready; +use pin_project_lite::pin_project; use super::{IntoService, IntoServiceFactory, Service, ServiceFactory}; @@ -94,9 +95,7 @@ where type Error = Err; type Future = Fut; - fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { - Poll::Ready(ready!(self.service.poll_ready(cx))) - } + crate::forward_ready!(service); fn call(&mut self, req: Req) -> Self::Future { (self.wrap_fn)(req, &mut self.service) @@ -162,17 +161,18 @@ where } } -#[pin_project::pin_project] -pub struct ApplyServiceFactoryResponse -where - SF: ServiceFactory, - F: FnMut(Req, &mut SF::Service) -> Fut, - Fut: Future>, -{ - #[pin] - fut: SF::Future, - wrap_fn: Option, - _phantom: PhantomData<(Req, Res)>, +pin_project! { + pub struct ApplyServiceFactoryResponse + where + SF: ServiceFactory, + F: FnMut(Req, &mut SF::Service) -> Fut, + Fut: Future>, + { + #[pin] + fut: SF::Future, + wrap_fn: Option, + _phantom: PhantomData<(Req, Res)>, + } } impl ApplyServiceFactoryResponse @@ -203,13 +203,13 @@ where let this = self.project(); let svc = ready!(this.fut.poll(cx))?; - Poll::Ready(Ok(Apply::new(svc, Option::take(this.wrap_fn).unwrap()))) + Poll::Ready(Ok(Apply::new(svc, this.wrap_fn.take().unwrap()))) } } #[cfg(test)] mod tests { - use std::task::{Context, Poll}; + use core::task::Poll; use futures_util::future::{lazy, ok, Ready}; @@ -224,9 +224,7 @@ mod tests { type Error = (); type Future = Ready>; - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } + crate::always_ready!(); fn call(&mut self, _: ()) -> Self::Future { ok(()) diff --git a/actix-service/src/apply_cfg.rs b/actix-service/src/apply_cfg.rs index da24e87d..3e111231 100644 --- a/actix-service/src/apply_cfg.rs +++ b/actix-service/src/apply_cfg.rs @@ -1,9 +1,13 @@ -use std::cell::RefCell; -use std::future::Future; -use std::marker::PhantomData; -use std::pin::Pin; -use std::rc::Rc; -use std::task::{Context, Poll}; +use alloc::rc::Rc; +use core::{ + cell::RefCell, + future::Future, + marker::PhantomData, + pin::Pin, + task::{Context, Poll}, +}; + +use pin_project_lite::pin_project; use crate::{Service, ServiceFactory}; @@ -156,37 +160,42 @@ where ApplyConfigServiceFactoryResponse { cfg: Some(cfg), store: self.srv.clone(), - state: State::A(self.srv.borrow().0.new_service(())), + state: State::A { + fut: self.srv.borrow().0.new_service(()), + }, } } } -#[pin_project::pin_project] -struct ApplyConfigServiceFactoryResponse -where - SF: ServiceFactory, - SF::InitError: From, - F: FnMut(Cfg, &mut SF::Service) -> Fut, - Fut: Future>, - S: Service, -{ - cfg: Option, - store: Rc>, - #[pin] - state: State, +pin_project! { + struct ApplyConfigServiceFactoryResponse + where + SF: ServiceFactory, + SF::InitError: From, + F: FnMut(Cfg, &mut SF::Service) -> Fut, + Fut: Future>, + S: Service, + { + cfg: Option, + store: Rc>, + #[pin] + state: State, + } } -#[pin_project::pin_project(project = StateProj)] -enum State -where - SF: ServiceFactory, - SF::InitError: From, - Fut: Future>, - S: Service, -{ - A(#[pin] SF::Future), - B(SF::Service), - C(#[pin] Fut), +pin_project! { + #[project = StateProj] + enum State + where + SF: ServiceFactory, + SF::InitError: From, + Fut: Future>, + S: Service, + { + A { #[pin] fut: SF::Future }, + B { svc: SF::Service }, + C { #[pin] fut: Fut }, + } } impl Future @@ -204,25 +213,25 @@ where let mut this = self.as_mut().project(); match this.state.as_mut().project() { - StateProj::A(fut) => match fut.poll(cx)? { + StateProj::A { fut } => match fut.poll(cx)? { Poll::Pending => Poll::Pending, - Poll::Ready(srv) => { - this.state.set(State::B(srv)); + Poll::Ready(svc) => { + this.state.set(State::B { svc }); self.poll(cx) } }, - StateProj::B(srv) => match srv.poll_ready(cx)? { + StateProj::B { svc } => match svc.poll_ready(cx)? { Poll::Ready(_) => { { let (_, f) = &mut *this.store.borrow_mut(); - let fut = f(this.cfg.take().unwrap(), srv); - this.state.set(State::C(fut)); + let fut = f(this.cfg.take().unwrap(), svc); + this.state.set(State::C { fut }); } self.poll(cx) } Poll::Pending => Poll::Pending, }, - StateProj::C(fut) => fut.poll(cx), + StateProj::C { fut } => fut.poll(cx), } } } diff --git a/actix-service/src/boxed.rs b/actix-service/src/boxed.rs index 35a10dac..203d575c 100644 --- a/actix-service/src/boxed.rs +++ b/actix-service/src/boxed.rs @@ -1,6 +1,10 @@ -use std::pin::Pin; -use std::task::{Context, Poll}; -use std::{future::Future, marker::PhantomData}; +use alloc::boxed::Box; +use core::{ + future::Future, + marker::PhantomData, + pin::Pin, + task::{Context, Poll}, +}; use futures_util::future::FutureExt; @@ -28,7 +32,7 @@ where { BoxServiceFactory(Box::new(FactoryWrapper { factory, - _t: std::marker::PhantomData, + _t: PhantomData, })) } @@ -75,12 +79,9 @@ where } } -struct FactoryWrapper -where - SF: ServiceFactory, -{ +struct FactoryWrapper { factory: SF, - _t: PhantomData<(C, Req)>, + _t: PhantomData<(Req, Cfg)>, } impl ServiceFactory for FactoryWrapper diff --git a/actix-service/src/fn_service.rs b/actix-service/src/fn_service.rs index 7d15304d..59792564 100644 --- a/actix-service/src/fn_service.rs +++ b/actix-service/src/fn_service.rs @@ -1,6 +1,4 @@ -use std::future::Future; -use std::marker::PhantomData; -use std::task::{Context, Poll}; +use core::{future::Future, marker::PhantomData, task::Poll}; use futures_util::future::{ok, Ready}; @@ -143,9 +141,7 @@ where type Error = Err; type Future = Fut; - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } + crate::always_ready!(); fn call(&mut self, req: Req) -> Self::Future { (self.f)(req) @@ -200,9 +196,7 @@ where type Error = Err; type Future = Fut; - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } + crate::always_ready!(); fn call(&mut self, req: Req) -> Self::Future { (self.f)(req) @@ -361,7 +355,7 @@ where #[cfg(test)] mod tests { - use std::task::Poll; + use core::task::Poll; use futures_util::future::{lazy, ok}; diff --git a/actix-service/src/lib.rs b/actix-service/src/lib.rs index 2dfa0dd7..d66d5221 100644 --- a/actix-service/src/lib.rs +++ b/actix-service/src/lib.rs @@ -1,18 +1,21 @@ //! See [`Service`] docs for information on this crate's foundational trait. +#![no_std] #![deny(rust_2018_idioms, nonstandard_style)] #![allow(clippy::type_complexity)] #![doc(html_logo_url = "https://actix.rs/img/logo.png")] #![doc(html_favicon_url = "https://actix.rs/favicon.ico")] -use std::cell::RefCell; -use std::future::Future; -use std::rc::Rc; -use std::sync::Arc; -use std::task::{self, Context, Poll}; +extern crate alloc; + +use alloc::{boxed::Box, rc::Rc, sync::Arc}; +use core::{ + cell::RefCell, + future::Future, + task::{self, Context, Poll}, +}; mod and_then; -mod and_then_apply_fn; mod apply; mod apply_cfg; pub mod boxed; @@ -359,3 +362,27 @@ pub mod dev { pub use crate::transform::ApplyTransform; pub use crate::transform_err::TransformMapInitErr; } + +#[macro_export] +macro_rules! always_ready { + () => { + fn poll_ready( + &mut self, + _: &mut ::core::task::Context<'_>, + ) -> ::core::task::Poll> { + Poll::Ready(Ok(())) + } + }; +} + +#[macro_export] +macro_rules! forward_ready { + ($field:ident) => { + fn poll_ready( + &mut self, + cx: &mut ::core::task::Context<'_>, + ) -> ::core::task::Poll> { + self.$field.poll_ready(cx) + } + }; +} diff --git a/actix-service/src/map.rs b/actix-service/src/map.rs index 04ef8c5f..a8afa25f 100644 --- a/actix-service/src/map.rs +++ b/actix-service/src/map.rs @@ -1,7 +1,11 @@ -use std::future::Future; -use std::marker::PhantomData; -use std::pin::Pin; -use std::task::{Context, Poll}; +use core::{ + future::Future, + marker::PhantomData, + pin::Pin, + task::{Context, Poll}, +}; + +use pin_project_lite::pin_project; use super::{Service, ServiceFactory}; @@ -52,24 +56,23 @@ where type Error = A::Error; type Future = MapFuture; - fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll> { - self.service.poll_ready(ctx) - } + crate::forward_ready!(service); fn call(&mut self, req: Req) -> Self::Future { MapFuture::new(self.service.call(req), self.f.clone()) } } -#[pin_project::pin_project] -pub struct MapFuture -where - A: Service, - F: FnMut(A::Response) -> Res, -{ - f: F, - #[pin] - fut: A::Future, +pin_project! { + pub struct MapFuture + where + A: Service, + F: FnMut(A::Response) -> Res, + { + f: F, + #[pin] + fut: A::Future, + } } impl MapFuture @@ -154,15 +157,16 @@ where } } -#[pin_project::pin_project] -pub struct MapServiceFuture -where - A: ServiceFactory, - F: FnMut(A::Response) -> Res, -{ - #[pin] - fut: A::Future, - f: Option, +pin_project! { + pub struct MapServiceFuture + where + A: ServiceFactory, + F: FnMut(A::Response) -> Res, + { + #[pin] + fut: A::Future, + f: Option, + } } impl MapServiceFuture @@ -207,9 +211,7 @@ mod tests { type Error = (); type Future = Ready>; - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } + crate::always_ready!(); fn call(&mut self, _: ()) -> Self::Future { ok(()) diff --git a/actix-service/src/map_config.rs b/actix-service/src/map_config.rs index 82b1789b..d6d6f6b2 100644 --- a/actix-service/src/map_config.rs +++ b/actix-service/src/map_config.rs @@ -1,4 +1,4 @@ -use std::marker::PhantomData; +use core::marker::PhantomData; use super::{IntoServiceFactory, ServiceFactory}; diff --git a/actix-service/src/map_err.rs b/actix-service/src/map_err.rs index ae7442cc..f0bf134b 100644 --- a/actix-service/src/map_err.rs +++ b/actix-service/src/map_err.rs @@ -1,7 +1,11 @@ -use std::future::Future; -use std::marker::PhantomData; -use std::pin::Pin; -use std::task::{Context, Poll}; +use core::{ + future::Future, + marker::PhantomData, + pin::Pin, + task::{Context, Poll}, +}; + +use pin_project_lite::pin_project; use super::{Service, ServiceFactory}; @@ -62,15 +66,16 @@ where } } -#[pin_project::pin_project] -pub struct MapErrFuture -where - A: Service, - F: Fn(A::Error) -> E, -{ - f: F, - #[pin] - fut: A::Future, +pin_project! { + pub struct MapErrFuture + where + A: Service, + F: Fn(A::Error) -> E, + { + f: F, + #[pin] + fut: A::Future, + } } impl MapErrFuture @@ -157,15 +162,16 @@ where } } -#[pin_project::pin_project] -pub struct MapErrServiceFuture -where - A: ServiceFactory, - F: Fn(A::Error) -> E, -{ - #[pin] - fut: A::Future, - f: F, +pin_project! { + pub struct MapErrServiceFuture + where + A: ServiceFactory, + F: Fn(A::Error) -> E, + { + #[pin] + fut: A::Future, + f: F, + } } impl MapErrServiceFuture diff --git a/actix-service/src/map_init_err.rs b/actix-service/src/map_init_err.rs index 518daaf6..9fc383aa 100644 --- a/actix-service/src/map_init_err.rs +++ b/actix-service/src/map_init_err.rs @@ -1,7 +1,11 @@ -use std::future::Future; -use std::marker::PhantomData; -use std::pin::Pin; -use std::task::{Context, Poll}; +use core::{ + future::Future, + marker::PhantomData, + pin::Pin, + task::{Context, Poll}, +}; + +use pin_project_lite::pin_project; use super::ServiceFactory; @@ -59,15 +63,16 @@ where } } -#[pin_project::pin_project] -pub struct MapInitErrFuture -where - A: ServiceFactory, - F: Fn(A::InitError) -> E, -{ - f: F, - #[pin] - fut: A::Future, +pin_project! { + pub struct MapInitErrFuture + where + A: ServiceFactory, + F: Fn(A::InitError) -> E, + { + f: F, + #[pin] + fut: A::Future, + } } impl MapInitErrFuture diff --git a/actix-service/src/pipeline.rs b/actix-service/src/pipeline.rs index cba7ce78..580d7b4c 100644 --- a/actix-service/src/pipeline.rs +++ b/actix-service/src/pipeline.rs @@ -1,8 +1,9 @@ -use std::task::{Context, Poll}; -use std::{future::Future, marker::PhantomData}; +use core::{ + marker::PhantomData, + task::{Context, Poll}, +}; use crate::and_then::{AndThenService, AndThenServiceFactory}; -use crate::and_then_apply_fn::{AndThenApplyFn, AndThenApplyFnFactory}; use crate::map::{Map, MapServiceFactory}; use crate::map_err::{MapErr, MapErrServiceFactory}; use crate::map_init_err::MapInitErr; @@ -67,28 +68,6 @@ where } } - /// Apply function to specified service and use it as a next service in chain. - /// - /// Short version of `pipeline_factory(...).and_then(apply_fn(...))` - pub fn and_then_apply_fn( - self, - service: I, - wrap_fn: F, - ) -> Pipeline + Clone, Req> - where - Self: Sized, - I: IntoService, - S1: Service, - F: FnMut(S::Response, &mut S1) -> Fut, - Fut: Future>, - Err: From + From, - { - Pipeline { - service: AndThenApplyFn::new(self.service, service.into_service(), wrap_fn), - _phantom: PhantomData, - } - } - /// Chain on a computation for when a call to the service finished, /// passing the result of the call to the next service `U`. /// @@ -219,39 +198,6 @@ where } } - /// Apply function to specified service and use it as a next service in chain. - /// - /// Short version of `pipeline_factory(...).and_then(apply_fn_factory(...))` - pub fn and_then_apply_fn( - self, - factory: I, - wrap_fn: F, - ) -> PipelineFactory< - impl ServiceFactory< - Req, - Response = Res, - Error = Err, - Config = SF::Config, - InitError = SF::InitError, - Service = impl Service + Clone, - > + Clone, - Req, - > - where - Self: Sized, - SF::Config: Clone, - I: IntoServiceFactory, - SF1: ServiceFactory, - F: FnMut(SF::Response, &mut SF1::Service) -> Fut + Clone, - Fut: Future>, - Err: From + From, - { - PipelineFactory { - factory: AndThenApplyFnFactory::new(self.factory, factory.into_factory(), wrap_fn), - _phantom: PhantomData, - } - } - /// Create `NewService` to chain on a computation for when a call to the /// service finished, passing the result of the call to the next /// service `U`. diff --git a/actix-service/src/then.rs b/actix-service/src/then.rs index 021e5484..179713ac 100644 --- a/actix-service/src/then.rs +++ b/actix-service/src/then.rs @@ -1,8 +1,13 @@ -use std::future::Future; -use std::pin::Pin; -use std::rc::Rc; -use std::task::{Context, Poll}; -use std::{cell::RefCell, marker::PhantomData}; +use alloc::rc::Rc; +use core::{ + cell::RefCell, + future::Future, + marker::PhantomData, + pin::Pin, + task::{Context, Poll}, +}; + +use pin_project_lite::pin_project; use super::{Service, ServiceFactory}; @@ -50,30 +55,36 @@ where fn call(&mut self, req: Req) -> Self::Future { ThenServiceResponse { - state: State::A(self.0.borrow_mut().0.call(req), Some(self.0.clone())), + state: State::A { + fut: self.0.borrow_mut().0.call(req), + b: Some(self.0.clone()), + }, } } } -#[pin_project::pin_project] -pub(crate) struct ThenServiceResponse -where - A: Service, - B: Service>, -{ - #[pin] - state: State, +pin_project! { + pub(crate) struct ThenServiceResponse + where + A: Service, + B: Service>, + { + #[pin] + state: State, + } } -#[pin_project::pin_project(project = StateProj)] -enum State -where - A: Service, - B: Service>, -{ - A(#[pin] A::Future, Option>>), - B(#[pin] B::Future), - Empty, +pin_project! { + #[project = StateProj] + enum State + where + A: Service, + B: Service>, + { + A { #[pin] fut: A::Future, b: Option>> }, + B { #[pin] fut: B::Future }, + Empty, + } } impl Future for ThenServiceResponse @@ -87,17 +98,17 @@ where let mut this = self.as_mut().project(); match this.state.as_mut().project() { - StateProj::A(fut, b) => match fut.poll(cx) { + StateProj::A { fut, b } => match fut.poll(cx) { Poll::Ready(res) => { let b = b.take().unwrap(); this.state.set(State::Empty); // drop fut A let fut = b.borrow_mut().1.call(res); - this.state.set(State::B(fut)); + this.state.set(State::B { fut }); self.poll(cx) } Poll::Pending => Poll::Pending, }, - StateProj::B(fut) => fut.poll(cx).map(|r| { + StateProj::B { fut } => fut.poll(cx).map(|r| { this.state.set(State::Empty); r }), @@ -159,23 +170,24 @@ impl Clone for ThenServiceFactory { } } -#[pin_project::pin_project] -pub(crate) struct ThenServiceFactoryResponse -where - A: ServiceFactory, - B: ServiceFactory< - Result, - Config = A::Config, - Error = A::Error, - InitError = A::InitError, - >, -{ - #[pin] - fut_b: B::Future, - #[pin] - fut_a: A::Future, - a: Option, - b: Option, +pin_project! { + pub(crate) struct ThenServiceFactoryResponse + where + A: ServiceFactory, + B: ServiceFactory< + Result, + Config = A::Config, + Error = A::Error, + InitError = A::InitError, + >, + { + #[pin] + fut_b: B::Future, + #[pin] + fut_a: A::Future, + a: Option, + b: Option, + } } impl ThenServiceFactoryResponse @@ -236,9 +248,11 @@ where #[cfg(test)] mod tests { - use std::cell::Cell; - use std::rc::Rc; - use std::task::{Context, Poll}; + use alloc::rc::Rc; + use core::{ + cell::Cell, + task::{Context, Poll}, + }; use futures_util::future::{err, lazy, ok, ready, Ready}; diff --git a/actix-service/src/transform.rs b/actix-service/src/transform.rs index d4d49417..76e4547a 100644 --- a/actix-service/src/transform.rs +++ b/actix-service/src/transform.rs @@ -1,8 +1,12 @@ -use std::pin::Pin; -use std::rc::Rc; -use std::sync::Arc; -use std::task::{Context, Poll}; -use std::{future::Future, marker::PhantomData}; +use alloc::{rc::Rc, sync::Arc}; +use core::{ + future::Future, + marker::PhantomData, + pin::Pin, + task::{Context, Poll}, +}; + +use pin_project_lite::pin_project; use crate::transform_err::TransformMapInitErr; use crate::{IntoServiceFactory, Service, ServiceFactory}; @@ -185,30 +189,35 @@ where fn new_service(&self, cfg: S::Config) -> Self::Future { ApplyTransformFuture { store: self.0.clone(), - state: ApplyTransformFutureState::A(self.0.as_ref().1.new_service(cfg)), + state: ApplyTransformFutureState::A { + fut: self.0.as_ref().1.new_service(cfg), + }, } } } -#[pin_project::pin_project] -pub struct ApplyTransformFuture -where - S: ServiceFactory, - T: Transform, -{ - store: Rc<(T, S)>, - #[pin] - state: ApplyTransformFutureState, +pin_project! { + pub struct ApplyTransformFuture + where + S: ServiceFactory, + T: Transform, + { + store: Rc<(T, S)>, + #[pin] + state: ApplyTransformFutureState, + } } -#[pin_project::pin_project(project = ApplyTransformFutureStateProj)] -pub enum ApplyTransformFutureState -where - S: ServiceFactory, - T: Transform, -{ - A(#[pin] S::Future), - B(#[pin] T::Future), +pin_project! { + #[project = ApplyTransformFutureStateProj] + pub enum ApplyTransformFutureState + where + S: ServiceFactory, + T: Transform, + { + A { #[pin] fut: S::Future }, + B { #[pin] fut: T::Future }, + } } impl Future for ApplyTransformFuture @@ -222,15 +231,15 @@ where let mut this = self.as_mut().project(); match this.state.as_mut().project() { - ApplyTransformFutureStateProj::A(fut) => match fut.poll(cx)? { + ApplyTransformFutureStateProj::A { fut } => match fut.poll(cx)? { Poll::Ready(srv) => { let fut = this.store.0.new_transform(srv); - this.state.set(ApplyTransformFutureState::B(fut)); + this.state.set(ApplyTransformFutureState::B { fut }); self.poll(cx) } Poll::Pending => Poll::Pending, }, - ApplyTransformFutureStateProj::B(fut) => fut.poll(cx), + ApplyTransformFutureStateProj::B { fut } => fut.poll(cx), } } } diff --git a/actix-service/src/transform_err.rs b/actix-service/src/transform_err.rs index 1d1b9576..cbf5fe3b 100644 --- a/actix-service/src/transform_err.rs +++ b/actix-service/src/transform_err.rs @@ -1,7 +1,11 @@ -use std::future::Future; -use std::marker::PhantomData; -use std::pin::Pin; -use std::task::{Context, Poll}; +use core::{ + future::Future, + marker::PhantomData, + pin::Pin, + task::{Context, Poll}, +}; + +use pin_project_lite::pin_project; use super::Transform; @@ -63,15 +67,16 @@ where } } -#[pin_project::pin_project] -pub struct TransformMapInitErrFuture -where +pin_project! { + pub struct TransformMapInitErrFuture + where T: Transform, F: Fn(T::InitError) -> E, -{ - #[pin] - fut: T::Future, - f: F, + { + #[pin] + fut: T::Future, + f: F, + } } impl Future for TransformMapInitErrFuture diff --git a/actix-tracing/src/lib.rs b/actix-tracing/src/lib.rs index 36aa21f2..6d37d9b3 100644 --- a/actix-tracing/src/lib.rs +++ b/actix-tracing/src/lib.rs @@ -4,8 +4,7 @@ #![doc(html_logo_url = "https://actix.rs/img/logo.png")] #![doc(html_favicon_url = "https://actix.rs/favicon.ico")] -use std::marker::PhantomData; -use std::task::{Context, Poll}; +use core::marker::PhantomData; use actix_service::{ apply, dev::ApplyTransform, IntoServiceFactory, Service, ServiceFactory, Transform, @@ -36,9 +35,7 @@ where type Error = S::Error; type Future = Either>; - fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll> { - self.inner.poll_ready(ctx) - } + actix_service::forward_ready!(inner); fn call(&mut self, req: Req) -> Self::Future { let span = (self.make_span)(&req); diff --git a/actix-utils/src/timeout.rs b/actix-utils/src/timeout.rs index 17647206..a27e9ffb 100644 --- a/actix-utils/src/timeout.rs +++ b/actix-utils/src/timeout.rs @@ -201,7 +201,7 @@ where #[cfg(test)] mod tests { - use std::task::{Context, Poll}; + use std::task::Poll; use std::time::Duration; use super::*; @@ -215,9 +215,7 @@ mod tests { type Error = (); type Future = LocalBoxFuture<'static, Result<(), ()>>; - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } + actix_service::always_ready!(); fn call(&mut self, _: ()) -> Self::Future { actix_rt::time::delay_for(self.0) From ba44ea7d0bafaf5fccb9a34003d503e1910943ee Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sun, 27 Dec 2020 18:24:57 +0000 Subject: [PATCH 06/73] remove futures-util from service deps (#235) --- actix-service/CHANGES.md | 2 + actix-service/Cargo.toml | 5 +-- actix-service/src/and_then.rs | 6 ++- actix-service/src/apply.rs | 4 +- actix-service/src/boxed.rs | 12 +++--- actix-service/src/ext.rs | 70 +++++++++++++++++++++++++++++++++ actix-service/src/fn_service.rs | 8 ++-- actix-service/src/lib.rs | 67 +++---------------------------- actix-service/src/map.rs | 6 ++- actix-service/src/map_err.rs | 7 +++- actix-service/src/ready.rs | 54 +++++++++++++++++++++++++ actix-service/src/then.rs | 4 +- 12 files changed, 159 insertions(+), 86 deletions(-) create mode 100644 actix-service/src/ext.rs create mode 100644 actix-service/src/ready.rs diff --git a/actix-service/CHANGES.md b/actix-service/CHANGES.md index 3b9a9cc0..82c5adb3 100644 --- a/actix-service/CHANGES.md +++ b/actix-service/CHANGES.md @@ -8,9 +8,11 @@ * Migrate pin projections to `pin-project-lite`. [#233] * Remove `AndThenApplyFn` and Pipeline `and_then_apply_fn`. Use the `.and_then(apply_fn(...))` construction. [#233] +* Move non-vital methods to `ServiceExt` and `ServiceFactoryExt` extension traits. [#235] [#232]: https://github.com/actix/actix-net/pull/232 [#233]: https://github.com/actix/actix-net/pull/233 +[#235]: https://github.com/actix/actix-net/pull/235 ## 1.0.6 - 2020-08-09 diff --git a/actix-service/Cargo.toml b/actix-service/Cargo.toml index 60818968..c08bb169 100644 --- a/actix-service/Cargo.toml +++ b/actix-service/Cargo.toml @@ -17,10 +17,9 @@ name = "actix_service" path = "src/lib.rs" [dependencies] -pin-project-lite = "0.2" -futures-util = { version = "0.3.7", default-features = false } futures-core = { version = "0.3.7", default-features = false } +pin-project-lite = "0.2" [dev-dependencies] actix-rt = "1.0.0" -criterion = "0.3" +futures-util = { version = "0.3.7", default-features = false } diff --git a/actix-service/src/and_then.rs b/actix-service/src/and_then.rs index 17d62e8f..fd24cb56 100644 --- a/actix-service/src/and_then.rs +++ b/actix-service/src/and_then.rs @@ -279,9 +279,11 @@ mod tests { task::{Context, Poll}, }; - use futures_util::future::{lazy, ok, ready, Ready}; + use futures_util::future::lazy; - use crate::{fn_factory, pipeline, pipeline_factory, Service, ServiceFactory}; + use crate::{ + fn_factory, ok, pipeline, pipeline_factory, ready, Ready, Service, ServiceFactory, + }; struct Srv1(Rc>); diff --git a/actix-service/src/apply.rs b/actix-service/src/apply.rs index 8db6018f..9b0c4025 100644 --- a/actix-service/src/apply.rs +++ b/actix-service/src/apply.rs @@ -211,10 +211,10 @@ where mod tests { use core::task::Poll; - use futures_util::future::{lazy, ok, Ready}; + use futures_util::future::lazy; use super::*; - use crate::{pipeline, pipeline_factory, Service, ServiceFactory}; + use crate::{ok, pipeline, pipeline_factory, Ready, Service, ServiceFactory}; #[derive(Clone)] struct Srv; diff --git a/actix-service/src/boxed.rs b/actix-service/src/boxed.rs index 203d575c..5c4557df 100644 --- a/actix-service/src/boxed.rs +++ b/actix-service/src/boxed.rs @@ -6,8 +6,6 @@ use core::{ task::{Context, Poll}, }; -use futures_util::future::FutureExt; - use crate::{Service, ServiceFactory}; pub type BoxFuture = Pin>>; @@ -103,11 +101,11 @@ where type Future = BoxFuture>; fn new_service(&self, cfg: Cfg) -> Self::Future { - Box::pin( - self.factory - .new_service(cfg) - .map(|res| res.map(ServiceWrapper::boxed)), - ) + let fut = self.factory.new_service(cfg); + Box::pin(async { + let res = fut.await; + res.map(ServiceWrapper::boxed) + }) } } diff --git a/actix-service/src/ext.rs b/actix-service/src/ext.rs new file mode 100644 index 00000000..e778d11e --- /dev/null +++ b/actix-service/src/ext.rs @@ -0,0 +1,70 @@ +use crate::{dev, Service, ServiceFactory}; + +pub trait ServiceExt: Service { + /// Map this service's output to a different type, returning a new service + /// of the resulting type. + /// + /// This function is similar to the `Option::map` or `Iterator::map` where + /// it will change the type of the underlying service. + /// + /// Note that this function consumes the receiving service and returns a + /// wrapped version of it, similar to the existing `map` methods in the + /// standard library. + fn map(self, f: F) -> dev::Map + where + Self: Sized, + F: FnMut(Self::Response) -> R, + { + dev::Map::new(self, f) + } + + /// Map this service's error to a different error, returning a new service. + /// + /// This function is similar to the `Result::map_err` where it will change + /// the error type of the underlying service. For example, this can be useful to + /// ensure that services have the same error type. + /// + /// Note that this function consumes the receiving service and returns a + /// wrapped version of it. + fn map_err(self, f: F) -> dev::MapErr + where + Self: Sized, + F: Fn(Self::Error) -> E, + { + dev::MapErr::new(self, f) + } +} + +impl ServiceExt for S where S: Service {} + +pub trait ServiceFactoryExt: ServiceFactory { + /// Map this service's output to a different type, returning a new service + /// of the resulting type. + fn map(self, f: F) -> crate::map::MapServiceFactory + where + Self: Sized, + F: FnMut(Self::Response) -> R + Clone, + { + crate::map::MapServiceFactory::new(self, f) + } + + /// Map this service's error to a different error, returning a new service. + fn map_err(self, f: F) -> crate::map_err::MapErrServiceFactory + where + Self: Sized, + F: Fn(Self::Error) -> E + Clone, + { + crate::map_err::MapErrServiceFactory::new(self, f) + } + + /// Map this factory's init error to a different error, returning a new service. + fn map_init_err(self, f: F) -> crate::map_init_err::MapInitErr + where + Self: Sized, + F: Fn(Self::InitError) -> E + Clone, + { + crate::map_init_err::MapInitErr::new(self, f) + } +} + +impl ServiceFactoryExt for S where S: ServiceFactory {} diff --git a/actix-service/src/fn_service.rs b/actix-service/src/fn_service.rs index 59792564..9f7d1eb7 100644 --- a/actix-service/src/fn_service.rs +++ b/actix-service/src/fn_service.rs @@ -1,8 +1,6 @@ use core::{future::Future, marker::PhantomData, task::Poll}; -use futures_util::future::{ok, Ready}; - -use crate::{IntoService, IntoServiceFactory, Service, ServiceFactory}; +use crate::{ok, IntoService, IntoServiceFactory, Ready, Service, ServiceFactory}; /// Create `ServiceFactory` for function that can act as a `Service` pub fn fn_service( @@ -357,10 +355,10 @@ where mod tests { use core::task::Poll; - use futures_util::future::{lazy, ok}; + use futures_util::future::lazy; use super::*; - use crate::{Service, ServiceFactory}; + use crate::{ok, Service, ServiceFactory}; #[actix_rt::test] async fn test_fn_service() { diff --git a/actix-service/src/lib.rs b/actix-service/src/lib.rs index d66d5221..7bf979e5 100644 --- a/actix-service/src/lib.rs +++ b/actix-service/src/lib.rs @@ -19,23 +19,29 @@ mod and_then; mod apply; mod apply_cfg; pub mod boxed; +mod ext; mod fn_service; mod map; mod map_config; mod map_err; mod map_init_err; mod pipeline; +mod ready; mod then; mod transform; mod transform_err; pub use self::apply::{apply_fn, apply_fn_factory}; pub use self::apply_cfg::{apply_cfg, apply_cfg_factory}; +pub use self::ext::{ServiceExt, ServiceFactoryExt}; pub use self::fn_service::{fn_factory, fn_factory_with_config, fn_service}; pub use self::map_config::{map_config, unit_config}; pub use self::pipeline::{pipeline, pipeline_factory, Pipeline, PipelineFactory}; pub use self::transform::{apply, Transform}; +#[allow(unused_imports)] +use self::ready::{err, ok, ready, Ready}; + /// An asynchronous operation from `Request` to a `Response`. /// /// The `Service` trait models a request/response interaction, receiving requests and returning @@ -110,39 +116,6 @@ pub trait Service { /// Calling `call` without calling `poll_ready` is permitted. The /// implementation must be resilient to this fact. fn call(&mut self, req: Req) -> Self::Future; - - /// Map this service's output to a different type, returning a new service - /// of the resulting type. - /// - /// This function is similar to the `Option::map` or `Iterator::map` where - /// it will change the type of the underlying service. - /// - /// Note that this function consumes the receiving service and returns a - /// wrapped version of it, similar to the existing `map` methods in the - /// standard library. - fn map(self, f: F) -> crate::dev::Map - where - Self: Sized, - F: FnMut(Self::Response) -> R, - { - crate::dev::Map::new(self, f) - } - - /// Map this service's error to a different error, returning a new service. - /// - /// This function is similar to the `Result::map_err` where it will change - /// the error type of the underlying service. For example, this can be useful to - /// ensure that services have the same error type. - /// - /// Note that this function consumes the receiving service and returns a - /// wrapped version of it. - fn map_err(self, f: F) -> crate::dev::MapErr - where - Self: Sized, - F: Fn(Self::Error) -> E, - { - crate::dev::MapErr::new(self, f) - } } /// Factory for creating `Service`s. @@ -175,34 +148,6 @@ pub trait ServiceFactory { /// Create and return a new service asynchronously. fn new_service(&self, cfg: Self::Config) -> Self::Future; - - /// Map this service's output to a different type, returning a new service - /// of the resulting type. - fn map(self, f: F) -> crate::map::MapServiceFactory - where - Self: Sized, - F: FnMut(Self::Response) -> R + Clone, - { - crate::map::MapServiceFactory::new(self, f) - } - - /// Map this service's error to a different error, returning a new service. - fn map_err(self, f: F) -> crate::map_err::MapErrServiceFactory - where - Self: Sized, - F: Fn(Self::Error) -> E + Clone, - { - crate::map_err::MapErrServiceFactory::new(self, f) - } - - /// Map this factory's init error to a different error, returning a new service. - fn map_init_err(self, f: F) -> crate::map_init_err::MapInitErr - where - Self: Sized, - F: Fn(Self::InitError) -> E + Clone, - { - crate::map_init_err::MapInitErr::new(self, f) - } } impl<'a, S, Req> Service for &'a mut S diff --git a/actix-service/src/map.rs b/actix-service/src/map.rs index a8afa25f..0599a1d8 100644 --- a/actix-service/src/map.rs +++ b/actix-service/src/map.rs @@ -199,10 +199,12 @@ where #[cfg(test)] mod tests { - use futures_util::future::{lazy, ok, Ready}; + use futures_util::future::lazy; use super::*; - use crate::{IntoServiceFactory, Service, ServiceFactory}; + use crate::{ + ok, IntoServiceFactory, Ready, Service, ServiceExt, ServiceFactory, ServiceFactoryExt, + }; struct Srv; diff --git a/actix-service/src/map_err.rs b/actix-service/src/map_err.rs index f0bf134b..944056c2 100644 --- a/actix-service/src/map_err.rs +++ b/actix-service/src/map_err.rs @@ -203,10 +203,13 @@ where #[cfg(test)] mod tests { - use futures_util::future::{err, lazy, ok, Ready}; + use futures_util::future::lazy; use super::*; - use crate::{IntoServiceFactory, Service, ServiceFactory}; + use crate::{ + err, ok, IntoServiceFactory, Ready, Service, ServiceExt, ServiceFactory, + ServiceFactoryExt, + }; struct Srv; diff --git a/actix-service/src/ready.rs b/actix-service/src/ready.rs new file mode 100644 index 00000000..8b0c2ea7 --- /dev/null +++ b/actix-service/src/ready.rs @@ -0,0 +1,54 @@ +//! When MSRV is 1.48, replace with `core::future::Ready` and `core::future::ready()`. + +use core::{ + future::Future, + pin::Pin, + task::{Context, Poll}, +}; + +/// Future for the [`ready`](ready()) function. +#[derive(Debug, Clone)] +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct Ready { + val: Option, +} + +impl Ready { + /// Unwraps the value from this immediately ready future. + #[inline] + pub fn into_inner(mut self) -> T { + self.val.take().unwrap() + } +} + +impl Unpin for Ready {} + +impl Future for Ready { + type Output = T; + + #[inline] + fn poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { + let val = self.val.take().expect("Ready can not be polled twice."); + Poll::Ready(val) + } +} + +/// Creates a future that is immediately ready with a value. +#[allow(dead_code)] +pub(crate) fn ready(val: T) -> Ready { + Ready { val: Some(val) } +} + +/// Create a future that is immediately ready with a success value. +#[allow(dead_code)] +pub(crate) fn ok(val: T) -> Ready> { + Ready { val: Some(Ok(val)) } +} + +/// Create a future that is immediately ready with an error value. +#[allow(dead_code)] +pub(crate) fn err(err: E) -> Ready> { + Ready { + val: Some(Err(err)), + } +} diff --git a/actix-service/src/then.rs b/actix-service/src/then.rs index 179713ac..060ca9c7 100644 --- a/actix-service/src/then.rs +++ b/actix-service/src/then.rs @@ -254,9 +254,9 @@ mod tests { task::{Context, Poll}, }; - use futures_util::future::{err, lazy, ok, ready, Ready}; + use futures_util::future::lazy; - use crate::{pipeline, pipeline_factory, Service, ServiceFactory}; + use crate::{err, ok, pipeline, pipeline_factory, ready, Ready, Service, ServiceFactory}; #[derive(Clone)] struct Srv1(Rc>); From 0c1293079605b3d8e8a73db36ca9c1452f9ca829 Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Mon, 28 Dec 2020 09:40:22 +0800 Subject: [PATCH 07/73] update to tokio 1.0 for actix-rt (#236) --- Cargo.toml | 2 +- actix-rt/CHANGES.md | 6 ++++-- actix-rt/Cargo.toml | 7 +------ actix-rt/src/arbiter.rs | 31 +++++++++++++---------------- actix-rt/src/builder.rs | 17 +++++++--------- actix-rt/src/lib.rs | 2 +- actix-rt/src/runtime.rs | 9 ++++----- actix-rt/src/system.rs | 20 +++++++++---------- actix-rt/tests/integration_tests.rs | 16 +++++++-------- 9 files changed, 49 insertions(+), 61 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f032478a..cea3ee3a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,7 @@ members = [ [patch.crates-io] actix-codec = { path = "actix-codec" } actix-connect = { path = "actix-connect" } -actix-rt = { path = "actix-rt" } +actix-rt = { git = "https://github.com/actix/actix-net.git", ref = "ba44ea7d0bafaf5fccb9a34003d503e1910943eepath" } actix-macros = { path = "actix-macros" } actix-server = { path = "actix-server" } actix-service = { path = "actix-service" } diff --git a/actix-rt/CHANGES.md b/actix-rt/CHANGES.md index c3479db1..8e73cef2 100644 --- a/actix-rt/CHANGES.md +++ b/actix-rt/CHANGES.md @@ -7,8 +7,10 @@ * Add `System::attach_to_tokio` method. [#173] ### Changed - -* Remove `'static` lifetime requirement for `Runtime::block_on` and `SystemRunner::block_on`. +* Update `tokio` dependency to `1` +* Rename `time` module `delay_for` to `sleep`, `delay_until` to `sleep_until`, `Delay` to `Sleep` to keep inline with tokio. +* Remove `'static` lifetime requirement for `Runtime::block_on` and `SystemRunner::block_on`. + These methods would accept &Self when calling. Remove `'static` lifetime requirement for `System::run` and `Builder::run`. `Arbiter::spawn` would panic when `System` is not in scope. [#207] diff --git a/actix-rt/Cargo.toml b/actix-rt/Cargo.toml index 57710a7f..eff206f2 100644 --- a/actix-rt/Cargo.toml +++ b/actix-rt/Cargo.toml @@ -18,9 +18,4 @@ path = "src/lib.rs" [dependencies] actix-macros = "0.1.0" -futures-channel = "0.3.7" -tokio = { version = "0.2.6", default-features = false, features = ["rt-core", "rt-util", "io-driver", "tcp", "uds", "udp", "time", "signal", "stream"] } - -[dev-dependencies] -futures-util = { version = "0.3.7", default-features = false, features = ["alloc"] } -tokio = { version = "0.2.6", features = ["full"] } +tokio = { version = "1", features = ["rt", "net", "signal", "sync", "time"] } diff --git a/actix-rt/src/arbiter.rs b/actix-rt/src/arbiter.rs index 3fe81b99..7aae7cd2 100644 --- a/actix-rt/src/arbiter.rs +++ b/actix-rt/src/arbiter.rs @@ -7,12 +7,11 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use std::task::{Context, Poll}; use std::{fmt, thread}; -use futures_channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender}; -use futures_channel::oneshot::{channel, Canceled, Sender}; +use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}; +use tokio::sync::oneshot::{channel, error::RecvError as Canceled, Sender}; // use futures_util::stream::FuturesUnordered; // use tokio::task::JoinHandle; // use tokio::stream::StreamExt; -use tokio::stream::Stream; use tokio::task::LocalSet; use crate::runtime::Runtime; @@ -70,7 +69,7 @@ impl Default for Arbiter { impl Arbiter { pub(crate) fn new_system(local: &LocalSet) -> Self { - let (tx, rx) = unbounded(); + let (tx, rx) = unbounded_channel(); let arb = Arbiter::with_sender(tx); ADDR.with(|cell| *cell.borrow_mut() = Some(arb.clone())); @@ -98,7 +97,7 @@ impl Arbiter { /// Stop arbiter from continuing it's event loop. pub fn stop(&self) { - let _ = self.sender.unbounded_send(ArbiterCommand::Stop); + let _ = self.sender.send(ArbiterCommand::Stop); } /// Spawn new thread and run event loop in spawned thread. @@ -107,14 +106,14 @@ impl Arbiter { let id = COUNT.fetch_add(1, Ordering::Relaxed); let name = format!("actix-rt:worker:{}", id); let sys = System::current(); - let (tx, rx) = unbounded(); + let (tx, rx) = unbounded_channel(); let handle = thread::Builder::new() .name(name.clone()) .spawn({ let tx = tx.clone(); move || { - let mut rt = Runtime::new().expect("Can not create Runtime"); + let rt = Runtime::new().expect("Can not create Runtime"); let arb = Arbiter::with_sender(tx); STORAGE.with(|cell| cell.borrow_mut().clear()); @@ -126,7 +125,7 @@ impl Arbiter { // register arbiter let _ = System::current() .sys() - .unbounded_send(SystemCommand::RegisterArbiter(id, arb)); + .send(SystemCommand::RegisterArbiter(id, arb)); // start arbiter controller // run loop @@ -135,7 +134,7 @@ impl Arbiter { // unregister arbiter let _ = System::current() .sys() - .unbounded_send(SystemCommand::UnregisterArbiter(id)); + .send(SystemCommand::UnregisterArbiter(id)); } }) .unwrap_or_else(|err| { @@ -181,9 +180,7 @@ impl Arbiter { where F: Future + Send + Unpin + 'static, { - let _ = self - .sender - .unbounded_send(ArbiterCommand::Execute(Box::new(future))); + let _ = self.sender.send(ArbiterCommand::Execute(Box::new(future))); } /// Send a function to the Arbiter's thread, and execute it. Any result from the function @@ -194,7 +191,7 @@ impl Arbiter { { let _ = self .sender - .unbounded_send(ArbiterCommand::ExecuteFn(Box::new(move || { + .send(ArbiterCommand::ExecuteFn(Box::new(move || { f(); }))); } @@ -210,8 +207,8 @@ impl Arbiter { let (tx, rx) = channel(); let _ = self .sender - .unbounded_send(ArbiterCommand::ExecuteFn(Box::new(move || { - if !tx.is_canceled() { + .send(ArbiterCommand::ExecuteFn(Box::new(move || { + if !tx.is_closed() { let _ = tx.send(f()); } }))); @@ -328,7 +325,7 @@ impl Future for ArbiterController { fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { loop { - match Pin::new(&mut self.rx).poll_next(cx) { + match Pin::new(&mut self.rx).poll_recv(cx) { Poll::Ready(None) => return Poll::Ready(()), Poll::Ready(Some(item)) => match item { ArbiterCommand::Stop => return Poll::Ready(()), @@ -393,7 +390,7 @@ impl Future for SystemArbiter { fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { loop { - match Pin::new(&mut self.commands).poll_next(cx) { + match Pin::new(&mut self.commands).poll_recv(cx) { Poll::Ready(None) => return Poll::Ready(()), Poll::Ready(Some(cmd)) => match cmd { SystemCommand::Exit(code) => { diff --git a/actix-rt/src/builder.rs b/actix-rt/src/builder.rs index 83aed064..ff7b0e06 100644 --- a/actix-rt/src/builder.rs +++ b/actix-rt/src/builder.rs @@ -2,8 +2,8 @@ use std::borrow::Cow; use std::future::Future; use std::io; -use futures_channel::mpsc::unbounded; -use futures_channel::oneshot::{channel, Receiver}; +use tokio::sync::mpsc::unbounded_channel; +use tokio::sync::oneshot::{channel, Receiver}; use tokio::task::LocalSet; use crate::arbiter::{Arbiter, SystemArbiter}; @@ -72,7 +72,7 @@ impl Builder { fn create_async_runtime(self, local: &LocalSet) -> AsyncSystemRunner { let (stop_tx, stop) = channel(); - let (sys_sender, sys_receiver) = unbounded(); + let (sys_sender, sys_receiver) = unbounded_channel(); let system = System::construct(sys_sender, Arbiter::new_system(local), self.stop_on_panic); @@ -91,9 +91,9 @@ impl Builder { F: FnOnce(), { let (stop_tx, stop) = channel(); - let (sys_sender, sys_receiver) = unbounded(); + let (sys_sender, sys_receiver) = unbounded_channel(); - let mut rt = Runtime::new().unwrap(); + let rt = Runtime::new().unwrap(); let system = System::construct( sys_sender, @@ -157,7 +157,7 @@ impl SystemRunner { /// This function will start event loop and will finish once the /// `System::stop()` function is called. pub fn run(self) -> io::Result<()> { - let SystemRunner { mut rt, stop, .. } = self; + let SystemRunner { rt, stop, .. } = self; // run loop match rt.block_on(stop) { @@ -177,10 +177,7 @@ impl SystemRunner { /// Execute a future and wait for result. #[inline] - pub fn block_on(&mut self, fut: F) -> O - where - F: Future, - { + pub fn block_on(&self, fut: F) -> F::Output { self.rt.block_on(fut) } } diff --git a/actix-rt/src/lib.rs b/actix-rt/src/lib.rs index b2e23c0f..3fd94bf9 100644 --- a/actix-rt/src/lib.rs +++ b/actix-rt/src/lib.rs @@ -58,7 +58,7 @@ pub mod net { /// Utilities for tracking time. pub mod time { pub use tokio::time::Instant; - pub use tokio::time::{delay_for, delay_until, Delay}; pub use tokio::time::{interval, interval_at, Interval}; + pub use tokio::time::{sleep, sleep_until, Sleep}; pub use tokio::time::{timeout, Timeout}; } diff --git a/actix-rt/src/runtime.rs b/actix-rt/src/runtime.rs index 7ee02b02..a72f492c 100644 --- a/actix-rt/src/runtime.rs +++ b/actix-rt/src/runtime.rs @@ -18,10 +18,9 @@ impl Runtime { #[allow(clippy::new_ret_no_self)] /// Returns a new runtime initialized with default configuration values. pub fn new() -> io::Result { - let rt = runtime::Builder::new() + let rt = runtime::Builder::new_current_thread() .enable_io() .enable_time() - .basic_scheduler() .build()?; Ok(Runtime { @@ -48,7 +47,7 @@ impl Runtime { /// /// # fn dox() { /// // Create the runtime - /// let mut rt = Runtime::new().unwrap(); + /// let rt = Runtime::new().unwrap(); /// /// // Spawn a future onto the runtime /// rt.spawn(future::lazy(|_| { @@ -86,10 +85,10 @@ impl Runtime { /// /// The caller is responsible for ensuring that other spawned futures /// complete execution by calling `block_on` or `run`. - pub fn block_on(&mut self, f: F) -> F::Output + pub fn block_on(&self, f: F) -> F::Output where F: Future, { - self.local.block_on(&mut self.rt, f) + self.local.block_on(&self.rt, f) } } diff --git a/actix-rt/src/system.rs b/actix-rt/src/system.rs index 16b96439..1fbbc0ee 100644 --- a/actix-rt/src/system.rs +++ b/actix-rt/src/system.rs @@ -3,7 +3,7 @@ use std::future::Future; use std::io; use std::sync::atomic::{AtomicUsize, Ordering}; -use futures_channel::mpsc::UnboundedSender; +use tokio::sync::mpsc::UnboundedSender; use tokio::task::LocalSet; use crate::arbiter::{Arbiter, SystemCommand}; @@ -70,7 +70,7 @@ impl System { /// /// # Examples /// - /// ``` + /// ```rust,ignore /// use tokio::{runtime::Runtime, task::LocalSet}; /// use actix_rt::System; /// use futures_util::future::try_join_all; @@ -94,10 +94,9 @@ impl System { /// } /// /// - /// let mut runtime = tokio::runtime::Builder::new() - /// .core_threads(2) + /// let runtime = tokio::runtime::Builder::new_multi_thread() + /// .worker_threads(2) /// .enable_all() - /// .threaded_scheduler() /// .build() /// .unwrap(); /// @@ -140,7 +139,7 @@ impl System { /// /// # Examples /// - /// ``` + /// ```rust,ignore /// use tokio::runtime::Runtime; /// use actix_rt::System; /// use futures_util::future::try_join_all; @@ -164,10 +163,9 @@ impl System { /// } /// /// - /// let runtime = tokio::runtime::Builder::new() - /// .core_threads(2) + /// let runtime = tokio::runtime::Builder::new_multi_thread() + /// .worker_threads(2) /// .enable_all() - /// .threaded_scheduler() /// .build() /// .unwrap(); /// @@ -176,7 +174,7 @@ impl System { /// ``` pub fn attach_to_tokio( name: impl Into, - mut runtime: tokio::runtime::Runtime, + runtime: tokio::runtime::Runtime, rest_operations: Fut, ) -> R where @@ -233,7 +231,7 @@ impl System { /// Stop the system with a particular exit code. pub fn stop_with_code(&self, code: i32) { - let _ = self.sys.unbounded_send(SystemCommand::Exit(code)); + let _ = self.sys.send(SystemCommand::Exit(code)); } pub(crate) fn sys(&self) -> &UnboundedSender { diff --git a/actix-rt/tests/integration_tests.rs b/actix-rt/tests/integration_tests.rs index b3265476..12ceb4ef 100644 --- a/actix-rt/tests/integration_tests.rs +++ b/actix-rt/tests/integration_tests.rs @@ -5,7 +5,7 @@ fn await_for_timer() { let time = Duration::from_secs(2); let instant = Instant::now(); actix_rt::System::new("test_wait_timer").block_on(async move { - tokio::time::delay_for(time).await; + tokio::time::sleep(time).await; }); assert!( instant.elapsed() >= time, @@ -20,7 +20,7 @@ fn join_another_arbiter() { actix_rt::System::new("test_join_another_arbiter").block_on(async move { let mut arbiter = actix_rt::Arbiter::new(); arbiter.send(Box::pin(async move { - tokio::time::delay_for(time).await; + tokio::time::sleep(time).await; actix_rt::Arbiter::current().stop(); })); arbiter.join().unwrap(); @@ -35,7 +35,7 @@ fn join_another_arbiter() { let mut arbiter = actix_rt::Arbiter::new(); arbiter.exec_fn(move || { actix_rt::spawn(async move { - tokio::time::delay_for(time).await; + tokio::time::sleep(time).await; actix_rt::Arbiter::current().stop(); }); }); @@ -50,7 +50,7 @@ fn join_another_arbiter() { actix_rt::System::new("test_join_another_arbiter").block_on(async move { let mut arbiter = actix_rt::Arbiter::new(); arbiter.send(Box::pin(async move { - tokio::time::delay_for(time).await; + tokio::time::sleep(time).await; actix_rt::Arbiter::current().stop(); })); arbiter.stop(); @@ -104,17 +104,17 @@ fn non_static_block_on() { let string = String::from("test_str"); let str = string.as_str(); - let mut sys = actix_rt::System::new("borrow some"); + let sys = actix_rt::System::new("borrow some"); sys.block_on(async { - actix_rt::time::delay_for(Duration::from_millis(1)).await; + actix_rt::time::sleep(Duration::from_millis(1)).await; assert_eq!("test_str", str); }); - let mut rt = actix_rt::Runtime::new().unwrap(); + let rt = actix_rt::Runtime::new().unwrap(); rt.block_on(async { - actix_rt::time::delay_for(Duration::from_millis(1)).await; + actix_rt::time::sleep(Duration::from_millis(1)).await; assert_eq!("test_str", str); }); From d684128831dbaaf86bbd42639dad34a776d91907 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Mon, 28 Dec 2020 01:48:19 +0000 Subject: [PATCH 08/73] fix rt override --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index cea3ee3a..b3274564 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,7 @@ members = [ [patch.crates-io] actix-codec = { path = "actix-codec" } actix-connect = { path = "actix-connect" } -actix-rt = { git = "https://github.com/actix/actix-net.git", ref = "ba44ea7d0bafaf5fccb9a34003d503e1910943eepath" } +actix-rt = { git = "https://github.com/actix/actix-net.git", ref = "ba44ea7d0bafaf5fccb9a34003d503e1910943ee" } actix-macros = { path = "actix-macros" } actix-server = { path = "actix-server" } actix-service = { path = "actix-service" } From 3d3bd60368f72ab09347b7f82e93b979204757d0 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Mon, 28 Dec 2020 01:53:11 +0000 Subject: [PATCH 09/73] fix rt override --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index b3274564..61280183 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,7 @@ members = [ [patch.crates-io] actix-codec = { path = "actix-codec" } actix-connect = { path = "actix-connect" } -actix-rt = { git = "https://github.com/actix/actix-net.git", ref = "ba44ea7d0bafaf5fccb9a34003d503e1910943ee" } +actix-rt = { git = "https://github.com/actix/actix-net.git", rev = "ba44ea7d0bafaf5fccb9a34003d503e1910943ee" } actix-macros = { path = "actix-macros" } actix-server = { path = "actix-server" } actix-service = { path = "actix-service" } From f48e3f4cb0cf5779b43d5ec465a646253d96057f Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Mon, 28 Dec 2020 01:58:31 +0000 Subject: [PATCH 10/73] prepare release for rt and service --- actix-rt/CHANGES.md | 18 ++++++++++-------- actix-rt/Cargo.toml | 4 ++-- actix-rt/src/lib.rs | 3 ++- actix-service/CHANGES.md | 8 +++++++- actix-service/Cargo.toml | 7 +++++-- 5 files changed, 26 insertions(+), 14 deletions(-) diff --git a/actix-rt/CHANGES.md b/actix-rt/CHANGES.md index 8e73cef2..35409616 100644 --- a/actix-rt/CHANGES.md +++ b/actix-rt/CHANGES.md @@ -2,23 +2,25 @@ ## Unreleased - 2020-xx-xx -### Added +## 2.0.0-beta.1 - 2020-12-28 +### Added * Add `System::attach_to_tokio` method. [#173] ### Changed -* Update `tokio` dependency to `1` -* Rename `time` module `delay_for` to `sleep`, `delay_until` to `sleep_until`, `Delay` to `Sleep` to keep inline with tokio. +* Update `tokio` dependency to `1.0`. [#236] +* Rename `time` module `delay_for` to `sleep`, `delay_until` to `sleep_until`, `Delay` to `Sleep` + to stay aligned with Tokio's naming. [#236] * Remove `'static` lifetime requirement for `Runtime::block_on` and `SystemRunner::block_on`. - These methods would accept &Self when calling. - Remove `'static` lifetime requirement for `System::run` and `Builder::run`. - `Arbiter::spawn` would panic when `System` is not in scope. [#207] + * These methods now accept `&self` when calling. [#236] +* Remove `'static` lifetime requirement for `System::run` and `Builder::run`. [#236] +* `Arbiter::spawn` now panics when `System` is not in scope. [#207] ### Fixed - -* Fix work load issue by removing `PENDDING` thread local. [#207] +* Fix work load issue by removing `PENDING` thread local. [#207] [#207]: https://github.com/actix/actix-net/pull/207 +[#236]: https://github.com/actix/actix-net/pull/236 ## [1.1.1] - 2020-04-30 diff --git a/actix-rt/Cargo.toml b/actix-rt/Cargo.toml index eff206f2..317345d4 100644 --- a/actix-rt/Cargo.toml +++ b/actix-rt/Cargo.toml @@ -1,8 +1,8 @@ [package] name = "actix-rt" -version = "1.1.1" +version = "2.0.0-beta.1" authors = ["Nikolay Kim "] -description = "Actix runtime" +description = "Tokio-based single-thread async runtime for the Actix ecosystem" keywords = ["network", "framework", "async", "futures"] homepage = "https://actix.rs" repository = "https://github.com/actix/actix-net.git" diff --git a/actix-rt/src/lib.rs b/actix-rt/src/lib.rs index 3fd94bf9..440fa33c 100644 --- a/actix-rt/src/lib.rs +++ b/actix-rt/src/lib.rs @@ -1,4 +1,5 @@ -//! A runtime implementation that runs everything on the current thread. +//! Tokio-based single-thread async runtime for the Actix ecosystem. + #![deny(rust_2018_idioms, nonstandard_style)] #![allow(clippy::type_complexity)] #![doc(html_logo_url = "https://actix.rs/img/logo.png")] diff --git a/actix-service/CHANGES.md b/actix-service/CHANGES.md index 82c5adb3..1cbf414e 100644 --- a/actix-service/CHANGES.md +++ b/actix-service/CHANGES.md @@ -1,6 +1,9 @@ # Changes ## Unreleased - 2020-xx-xx + + +## 2.0.0-beta.1 - 2020-12-28 * `Service`, other traits, and many type signatures now take the the request type as a type parameter instead of an associated type. [#232] * Add `always_ready!` and `forward_ready!` macros. [#233] @@ -19,7 +22,10 @@ ### Fixed -* Removed unsound custom Cell implementation that allowed obtaining several mutable references to the same data, which is undefined behavior in Rust and could lead to violations of memory safety. External code could obtain several mutable references to the same data through service combinators. Attempts to acquire several mutable references to the same data will instead result in a panic. +* Removed unsound custom Cell implementation that allowed obtaining several mutable references to + the same data, which is undefined behavior in Rust and could lead to violations of memory safety. External code could obtain several mutable references to the same data through + service combinators. Attempts to acquire several mutable references to the same data will instead + result in a panic. ## [1.0.5] - 2020-01-16 diff --git a/actix-service/Cargo.toml b/actix-service/Cargo.toml index c08bb169..708fb5e6 100644 --- a/actix-service/Cargo.toml +++ b/actix-service/Cargo.toml @@ -1,7 +1,10 @@ [package] name = "actix-service" -version = "1.0.6" -authors = ["Nikolay Kim "] +version = "2.0.0-beta.1" +authors = [ + "Nikolay Kim ", + "Rob Ede ", +] description = "Service trait and combinators for representing asynchronous request/response operations." keywords = ["network", "framework", "async", "futures", "service"] homepage = "https://actix.rs" From 2ee8f45f5dc66486d071b13b1acc17c150568823 Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Mon, 28 Dec 2020 11:16:37 +0800 Subject: [PATCH 11/73] update actix-codec and actix-utils to tokio 1.0 (#237) --- Cargo.toml | 6 +++--- actix-codec/CHANGES.md | 5 ++++- actix-codec/Cargo.toml | 12 ++++++------ actix-codec/src/bcodec.rs | 2 +- actix-codec/src/framed.rs | 37 +++++++++++++++++++------------------ actix-codec/src/lib.rs | 3 ++- actix-utils/CHANGES.md | 1 + actix-utils/Cargo.toml | 4 ++-- actix-utils/src/timeout.rs | 29 +++++++++++++++-------------- 9 files changed, 53 insertions(+), 46 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 61280183..d46b6283 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,16 +16,16 @@ members = [ ] [patch.crates-io] -actix-codec = { path = "actix-codec" } +actix-codec = { git = "https://github.com/actix/actix-net.git", rev = "ba44ea7d0bafaf5fccb9a34003d503e1910943ee" } actix-connect = { path = "actix-connect" } actix-rt = { git = "https://github.com/actix/actix-net.git", rev = "ba44ea7d0bafaf5fccb9a34003d503e1910943ee" } actix-macros = { path = "actix-macros" } actix-server = { path = "actix-server" } -actix-service = { path = "actix-service" } +actix-service = { git = "https://github.com/actix/actix-net.git", rev = "ba44ea7d0bafaf5fccb9a34003d503e1910943ee" } actix-testing = { path = "actix-testing" } actix-threadpool = { path = "actix-threadpool" } actix-tls = { path = "actix-tls" } actix-tracing = { path = "actix-tracing" } -actix-utils = { path = "actix-utils" } +actix-utils = { git = "https://github.com/actix/actix-net.git", rev = "ba44ea7d0bafaf5fccb9a34003d503e1910943ee" } actix-router = { path = "router" } bytestring = { path = "string" } diff --git a/actix-codec/CHANGES.md b/actix-codec/CHANGES.md index cd4424d0..3d82775a 100644 --- a/actix-codec/CHANGES.md +++ b/actix-codec/CHANGES.md @@ -1,7 +1,10 @@ # Changes ## Unreleased - 2020-xx-xx -* Upgrade `pin-project` to `1.0`. +* Replace `pin-project` with `pin-project-lite`. +* Upgrade `tokio` dependency to `1`. +* Upgrade `tokio-util` dependency to `0.6`. +* Upgrade `bytes` dependency to `1`. ## 0.3.0 - 2020-08-23 * No changes from beta 2. diff --git a/actix-codec/Cargo.toml b/actix-codec/Cargo.toml index 1214945a..e901efd5 100644 --- a/actix-codec/Cargo.toml +++ b/actix-codec/Cargo.toml @@ -17,10 +17,10 @@ path = "src/lib.rs" [dependencies] bitflags = "1.2.1" -bytes = "0.5.2" -futures-core = { version = "0.3.4", default-features = false } -futures-sink = { version = "0.3.4", default-features = false } +bytes = "1" +futures-core = { version = "0.3.7", default-features = false } +futures-sink = { version = "0.3.7", default-features = false } log = "0.4" -pin-project = "1.0.0" -tokio = { version = "0.2.5", default-features = false } -tokio-util = { version = "0.3.1", default-features = false, features = ["codec"] } +pin-project-lite = "0.2" +tokio = "1" +tokio-util = { version = "0.6", features = ["codec", "io"] } diff --git a/actix-codec/src/bcodec.rs b/actix-codec/src/bcodec.rs index 045b20a2..b06279ea 100644 --- a/actix-codec/src/bcodec.rs +++ b/actix-codec/src/bcodec.rs @@ -14,7 +14,7 @@ impl Encoder for BytesCodec { #[inline] fn encode(&mut self, item: Bytes, dst: &mut BytesMut) -> Result<(), Self::Error> { - dst.extend_from_slice(item.bytes()); + dst.extend_from_slice(item.chunk()); Ok(()) } } diff --git a/actix-codec/src/framed.rs b/actix-codec/src/framed.rs index 844f20d8..cf2297dc 100644 --- a/actix-codec/src/framed.rs +++ b/actix-codec/src/framed.rs @@ -5,7 +5,6 @@ use std::{fmt, io}; use bytes::{Buf, BytesMut}; use futures_core::{ready, Stream}; use futures_sink::Sink; -use pin_project::pin_project; use crate::{AsyncRead, AsyncWrite, Decoder, Encoder}; @@ -21,22 +20,23 @@ bitflags::bitflags! { } } -/// A unified `Stream` and `Sink` interface to an underlying I/O object, using -/// the `Encoder` and `Decoder` traits to encode and decode frames. -/// -/// Raw I/O objects work with byte sequences, but higher-level code usually -/// wants to batch these into meaningful chunks, called "frames". This -/// method layers framing on top of an I/O object, by using the `Encoder`/`Decoder` -/// traits to handle encoding and decoding of message frames. Note that -/// the incoming and outgoing frame types may be distinct. -#[pin_project] -pub struct Framed { - #[pin] - io: T, - codec: U, - flags: Flags, - read_buf: BytesMut, - write_buf: BytesMut, +pin_project_lite::pin_project! { + /// A unified `Stream` and `Sink` interface to an underlying I/O object, using + /// the `Encoder` and `Decoder` traits to encode and decode frames. + /// + /// Raw I/O objects work with byte sequences, but higher-level code usually + /// wants to batch these into meaningful chunks, called "frames". This + /// method layers framing on top of an I/O object, by using the `Encoder`/`Decoder` + /// traits to handle encoding and decoding of message frames. Note that + /// the incoming and outgoing frame types may be distinct. + pub struct Framed { + #[pin] + io: T, + codec: U, + flags: Flags, + read_buf: BytesMut, + write_buf: BytesMut, + } } impl Framed @@ -220,7 +220,8 @@ impl Framed { if remaining < LW { this.read_buf.reserve(HW - remaining) } - let cnt = match this.io.poll_read_buf(cx, &mut this.read_buf) { + + let cnt = match tokio_util::io::poll_read_buf(this.io, cx, this.read_buf) { Poll::Pending => return Poll::Pending, Poll::Ready(Err(e)) => return Poll::Ready(Some(Err(e.into()))), Poll::Ready(Ok(cnt)) => cnt, diff --git a/actix-codec/src/lib.rs b/actix-codec/src/lib.rs index 8c346052..9e875409 100644 --- a/actix-codec/src/lib.rs +++ b/actix-codec/src/lib.rs @@ -18,5 +18,6 @@ mod framed; pub use self::bcodec::BytesCodec; pub use self::framed::{Framed, FramedParts}; -pub use tokio::io::{AsyncRead, AsyncWrite}; +pub use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; pub use tokio_util::codec::{Decoder, Encoder}; +pub use tokio_util::io::poll_read_buf; diff --git a/actix-utils/CHANGES.md b/actix-utils/CHANGES.md index b4d59ed0..b112d8b1 100644 --- a/actix-utils/CHANGES.md +++ b/actix-utils/CHANGES.md @@ -1,6 +1,7 @@ # Changes ## Unreleased - 2020-xx-xx +* Update `bytes` dependency to `1`. * Use `pin-project-lite` to replace `pin-project`. [#229] * Remove `condition`,`either`,`inflight`,`keepalive`,`oneshot`,`order`,`stream` and `time` mods. [#229] diff --git a/actix-utils/Cargo.toml b/actix-utils/Cargo.toml index fb7ed151..3ed4a518 100644 --- a/actix-utils/Cargo.toml +++ b/actix-utils/Cargo.toml @@ -17,8 +17,8 @@ path = "src/lib.rs" [dependencies] actix-codec = "0.3.0" -actix-rt = "1.1.1" -actix-service = "1.0.6" +actix-rt = "2.0.0-beta.1" +actix-service = "2.0.0-beta.1" futures-core = { version = "0.3.7", default-features = false } futures-sink = { version = "0.3.7", default-features = false } diff --git a/actix-utils/src/timeout.rs b/actix-utils/src/timeout.rs index a27e9ffb..612c3cb4 100644 --- a/actix-utils/src/timeout.rs +++ b/actix-utils/src/timeout.rs @@ -9,7 +9,7 @@ use core::pin::Pin; use core::task::{Context, Poll}; use core::{fmt, time}; -use actix_rt::time::{delay_for, Delay}; +use actix_rt::time::{sleep, Sleep}; use actix_service::{IntoService, Service, Transform}; use pin_project_lite::pin_project; @@ -85,8 +85,8 @@ where { type Response = S::Response; type Error = TimeoutError; - type InitError = E; type Transform = TimeoutService; + type InitError = E; type Future = TimeoutFuture; fn new_transform(&self, service: S) -> Self::Future { @@ -157,7 +157,7 @@ where fn call(&mut self, request: Req) -> Self::Future { TimeoutServiceResponse { fut: self.service.call(request), - sleep: delay_for(self.timeout), + sleep: sleep(self.timeout), } } } @@ -171,7 +171,8 @@ pin_project! { { #[pin] fut: S::Future, - sleep: Delay, + #[pin] + sleep: Sleep, } } @@ -193,20 +194,18 @@ where } // Now check the sleep - Pin::new(this.sleep) - .poll(cx) - .map(|_| Err(TimeoutError::Timeout)) + this.sleep.poll(cx).map(|_| Err(TimeoutError::Timeout)) } } #[cfg(test)] mod tests { - use std::task::Poll; - use std::time::Duration; + use core::task::Poll; + use core::time::Duration; use super::*; use actix_service::{apply, fn_factory, Service, ServiceFactory}; - use futures_util::future::{ok, FutureExt, LocalBoxFuture}; + use futures_core::future::LocalBoxFuture; struct SleepService(Duration); @@ -218,9 +217,11 @@ mod tests { actix_service::always_ready!(); fn call(&mut self, _: ()) -> Self::Future { - actix_rt::time::delay_for(self.0) - .then(|_| ok::<_, ()>(())) - .boxed_local() + let sleep = actix_rt::time::sleep(self.0); + Box::pin(async move { + sleep.await; + Ok(()) + }) } } @@ -249,7 +250,7 @@ mod tests { let timeout = apply( Timeout::new(resolution), - fn_factory(|| ok::<_, ()>(SleepService(wait_time))), + fn_factory(|| async { Ok::<_, ()>(SleepService(wait_time)) }), ); let mut srv = timeout.new_service(&()).await.unwrap(); From e4a44b77e673c8fad5fcd8eed259618d28ac5c8c Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Mon, 28 Dec 2020 03:24:43 +0000 Subject: [PATCH 12/73] prepare codec release 0.4.0-beta.1 --- actix-codec/CHANGES.md | 31 +++++++++++++++++++------------ actix-codec/Cargo.toml | 4 ++-- actix-codec/src/lib.rs | 2 +- 3 files changed, 22 insertions(+), 15 deletions(-) diff --git a/actix-codec/CHANGES.md b/actix-codec/CHANGES.md index 3d82775a..0c26c085 100644 --- a/actix-codec/CHANGES.md +++ b/actix-codec/CHANGES.md @@ -1,17 +1,25 @@ # Changes ## Unreleased - 2020-xx-xx -* Replace `pin-project` with `pin-project-lite`. -* Upgrade `tokio` dependency to `1`. -* Upgrade `tokio-util` dependency to `0.6`. -* Upgrade `bytes` dependency to `1`. + + +## 0.4.0-beta.1 - 2020-12-28 +* Replace `pin-project` with `pin-project-lite`. [#237] +* Upgrade `tokio` dependency to `1`. [#237] +* Upgrade `tokio-util` dependency to `0.6`. [#237] +* Upgrade `bytes` dependency to `1`. [#237] + +[#237]: https://github.com/actix/actix-net/pull/237 + ## 0.3.0 - 2020-08-23 * No changes from beta 2. + ## 0.3.0-beta.2 - 2020-08-19 * Remove unused type parameter from `Framed::replace_codec`. + ## 0.3.0-beta.1 - 2020-08-19 * Use `.advance()` instead of `.split_to()`. * Upgrade `tokio-util` to `0.3`. @@ -21,32 +29,31 @@ * Add method on `Framed` to get a pinned reference to the underlying I/O. * Add method on `Framed` check emptiness of read buffer. -## [0.2.0] - 2019-12-10 +## 0.2.0 - 2019-12-10 * Use specific futures dependencies -## [0.2.0-alpha.4] +## 0.2.0-alpha.4 * Fix buffer remaining capacity calculation -## [0.2.0-alpha.3] +## 0.2.0-alpha.3 * Use tokio 0.2 - * Fix low/high watermark for write/read buffers -## [0.2.0-alpha.2] +## 0.2.0-alpha.2 * Migrated to `std::future` -## [0.1.2] - 2019-03-27 +## 0.1.2 - 2019-03-27 * Added `Framed::map_io()` method. -## [0.1.1] - 2019-03-06 +## 0.1.1 - 2019-03-06 * Added `FramedParts::with_read_buffer()` method. -## [0.1.0] - 2018-12-09 +## 0.1.0 - 2018-12-09 * Move codec to separate crate diff --git a/actix-codec/Cargo.toml b/actix-codec/Cargo.toml index e901efd5..d0f6646d 100644 --- a/actix-codec/Cargo.toml +++ b/actix-codec/Cargo.toml @@ -1,8 +1,8 @@ [package] name = "actix-codec" -version = "0.3.0" +version = "0.4.0-beta.1" authors = ["Nikolay Kim "] -description = "Codec utilities for working with framed protocols." +description = "Codec utilities for working with framed protocols" keywords = ["network", "framework", "async", "futures"] homepage = "https://actix.rs" repository = "https://github.com/actix/actix-net.git" diff --git a/actix-codec/src/lib.rs b/actix-codec/src/lib.rs index 9e875409..dec30ba6 100644 --- a/actix-codec/src/lib.rs +++ b/actix-codec/src/lib.rs @@ -1,4 +1,4 @@ -//! Utilities for encoding and decoding frames. +//! Codec utilities for working with framed protocols. //! //! Contains adapters to go from streams of bytes, [`AsyncRead`] and //! [`AsyncWrite`], to framed streams implementing [`Sink`] and [`Stream`]. From a09f9abfcb712ffbf61a90c80a258281a739a88a Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Mon, 28 Dec 2020 03:32:28 +0000 Subject: [PATCH 13/73] prepare utils release 3.0.0-beta.1 --- actix-utils/CHANGES.md | 213 +++++++++++++------------------------ actix-utils/Cargo.toml | 6 +- actix-utils/src/lib.rs | 2 +- actix-utils/src/mpsc.rs | 1 + actix-utils/src/timeout.rs | 3 +- 5 files changed, 77 insertions(+), 148 deletions(-) diff --git a/actix-utils/CHANGES.md b/actix-utils/CHANGES.md index b112d8b1..6c7025b5 100644 --- a/actix-utils/CHANGES.md +++ b/actix-utils/CHANGES.md @@ -1,227 +1,156 @@ # Changes ## Unreleased - 2020-xx-xx -* Update `bytes` dependency to `1`. + + +## 3.0.0-beta.1 - 2020-12-28 +* Update `bytes` dependency to `1`. [#237] * Use `pin-project-lite` to replace `pin-project`. [#229] * Remove `condition`,`either`,`inflight`,`keepalive`,`oneshot`,`order`,`stream` and `time` mods. [#229] [#229]: https://github.com/actix/actix-net/pull/229 +[#237]: https://github.com/actix/actix-net/pull/237 + ## 2.0.0 - 2020-08-23 * No changes from beta 1. + ## 2.0.0-beta.1 - 2020-08-19 * Upgrade `tokio-util` to `0.3`. * Remove unsound custom Cell and use `std::cell::RefCell` instead, as well as `actix-service`. * Rename method to correctly spelled `LocalWaker::is_registered`. -## [1.0.6] - 2020-01-08 -* Add `Clone` impl for `condition::Waiter` +## 1.0.6 - 2020-01-08 +* Add `Clone` impl for `condition::Waiter`. -## [1.0.5] - 2020-01-08 +## 1.0.5 - 2020-01-08 * Add `Condition` type. - * Add `Pool` of one-shot's. -## [1.0.4] - 2019-12-20 +## 1.0.4 - 2019-12-20 * Add methods to check `LocalWaker` registration state. -## [1.0.3] - 2019-12-11 +## 1.0.3 - 2019-12-11 * Revert InOrder service changes -## [1.0.2] - 2019-12-11 -* Allow to create `framed::Dispatcher` with custom `mpsc::Receiver` +## 1.0.2 - 2019-12-11 +* Allow to create `framed::Dispatcher` with custom `mpsc::Receiver`. +* Add `oneshot::Sender::is_canceled()` method. -* Add `oneshot::Sender::is_canceled()` method -## [1.0.1] - 2019-12-11 +## 1.0.1 - 2019-12-11 +* Optimize InOrder service. -* Optimize InOrder service -## [1.0.0] - 2019-12-11 +## 1.0.0 - 2019-12-11 +* Simplify oneshot and mpsc implementations. -* Simplify oneshot and mpsc implementations -## [1.0.0-alpha.3] - 2019-12-07 +## 1.0.0-alpha.3 - 2019-12-07 +* Migrate to tokio 0.2. +* Fix oneshot. -* Migrate to tokio 0.2 -* Fix oneshot +## 1.0.0-alpha.2 - 2019-12-02 +* Migrate to `std::future`. -## [1.0.0-alpha.2] - 2019-12-02 - -* Migrate to `std::future` - -## [0.4.7] - 2019-10-14 +## 0.4.7 - 2019-10-14 * Re-register task on every framed transport poll. -## [0.4.6] - 2019-10-08 - +## 0.4.6 - 2019-10-08 * Refactor `Counter` type. register current task in available method. -## [0.4.5] - 2019-07-19 - -### Removed - -* Deprecated `CloneableService` as it is not safe +## 0.4.5 - 2019-07-19 +* Deprecated `CloneableService` as it is not safe. -## [0.4.4] - 2019-07-17 - -### Changed - -* Undeprecate `FramedTransport` as it is actually useful +## 0.4.4 - 2019-07-17 +* Undeprecate `FramedTransport` as it is actually useful. -## [0.4.3] - 2019-07-17 - -### Deprecated - -* Deprecate `CloneableService` as it is not safe and in general not very useful - -* Deprecate `FramedTransport` in favor of `actix-ioframe` +## 0.4.3 - 2019-07-17 +* Deprecate `CloneableService` as it is not safe and in general not very useful. +* Deprecate `FramedTransport` in favor of `actix-ioframe`. -## [0.4.2] - 2019-06-26 - -### Fixed - -* Do not block on sink drop for FramedTransport +## 0.4.2 - 2019-06-26 +* Do not block on sink drop for FramedTransport. -## [0.4.1] - 2019-05-15 - -### Changed - -* Change `Either` constructor +## 0.4.1 - 2019-05-15 +* Change `Either` constructor. -## [0.4.0] - 2019-05-11 +## 0.4.0 - 2019-05-11 +* Change `Either` to handle two nexted services. +* Upgrade actix-service 0.4. +* Removed framed related services. +* Removed stream related services. -### Changed - -* Change `Either` to handle two nexted services - -* Upgrade actix-service 0.4 - -### Deleted - -* Framed related services - -* Stream related services - -## [0.3.5] - 2019-04-04 - -### Added +## 0.3.5 - 2019-04-04 * Allow to send messages to `FramedTransport` via mpsc channel. - -### Changed - -* Remove 'static constraint from Clonable service +* Remove `'static` constraint from Clonable service. -## [0.3.4] - 2019-03-12 - -### Changed - +## 0.3.4 - 2019-03-12 * `TimeoutService`, `InOrderService`, `InFlightService` accepts generic IntoService services. - -### Fixed - -* Fix `InFlightService::poll_ready()` nested service readiness check - -* Fix `InOrderService::poll_ready()` nested service readiness check +* Fix `InFlightService::poll_ready()` nested service readiness check. +* Fix `InOrderService::poll_ready()` nested service readiness check. -## [0.3.3] - 2019-03-09 - -### Changed - -* Revert IntoFuture change - -* Add generic config param for IntoFramed and TakeOne new services +## 0.3.3 - 2019-03-09 +* Revert IntoFuture change. +* Add generic config param for IntoFramed and TakeOne new services. -## [0.3.2] - 2019-03-04 +## 0.3.2 - 2019-03-04 +* Use IntoFuture for new services. -### Changed - -* Use IntoFuture for new services +## 0.3.1 - 2019-03-04 +* Use new type of transform trait. -## [0.3.1] - 2019-03-04 - -### Changed - -* Use new type of transform trait - - -## [0.3.0] - 2019-03-02 - -### Changed - +## 0.3.0 - 2019-03-02 * Use new `NewService` trait - * BoxedNewService` and `BoxedService` types moved to actix-service crate. -## [0.2.4] - 2019-02-21 - -### Changed - +## 0.2.4 - 2019-02-21 * Custom `BoxedNewService` implementation. -## [0.2.3] - 2019-02-21 - -### Added - -* Add `BoxedNewService` and `BoxedService` +## 0.2.3 - 2019-02-21 +* Add `BoxedNewService` and `BoxedService`. -## [0.2.2] - 2019-02-11 - -### Added - -* Add `Display` impl for `TimeoutError` - -* Add `Display` impl for `InOrderError` +## 0.2.2 - 2019-02-11 +* Add `Display` impl for `TimeoutError`. +* Add `Display` impl for `InOrderError`. -## [0.2.1] - 2019-02-06 - -### Added - +## 0.2.1 - 2019-02-06 * Add `InOrder` service. the service yields responses as they become available, in the order that their originating requests were submitted to the service. - -### Changed - -* Convert `Timeout` and `InFlight` services to a transforms +* Convert `Timeout` and `InFlight` services to a transforms. -## [0.2.0] - 2019-02-01 - -* Fix framed transport error handling - -* Added Clone impl for Either service - -* Added Clone impl for Timeout service factory - -* Added Service and NewService for Stream dispatcher - -* Switch to actix-service 0.2 +## 0.2.0 - 2019-02-01 +* Fix framed transport error handling. +* Added Clone impl for Either service. +* Added Clone impl for Timeout service factory. +* Added Service and NewService for Stream dispatcher. +* Switch to actix-service 0.2. -## [0.1.0] - 2018-12-09 - -* Move utils services to separate crate +## 0.1.0 - 2018-12-09 +* Move utils services to separate crate. diff --git a/actix-utils/Cargo.toml b/actix-utils/Cargo.toml index 3ed4a518..3dded4f0 100644 --- a/actix-utils/Cargo.toml +++ b/actix-utils/Cargo.toml @@ -1,8 +1,8 @@ [package] name = "actix-utils" -version = "2.0.0" +version = "3.0.0-beta.1" authors = ["Nikolay Kim "] -description = "Various network related services and utilities for the Actix ecosystem." +description = "Various network related services and utilities for the Actix ecosystem" keywords = ["network", "framework", "async", "futures"] homepage = "https://actix.rs" repository = "https://github.com/actix/actix-net.git" @@ -16,7 +16,7 @@ name = "actix_utils" path = "src/lib.rs" [dependencies] -actix-codec = "0.3.0" +actix-codec = "0.4.0-beta.1" actix-rt = "2.0.0-beta.1" actix-service = "2.0.0-beta.1" diff --git a/actix-utils/src/lib.rs b/actix-utils/src/lib.rs index 4c4f019c..5c10bac6 100644 --- a/actix-utils/src/lib.rs +++ b/actix-utils/src/lib.rs @@ -1,4 +1,4 @@ -//! Actix utils - various helper services +//! Various network related services and utilities for the Actix ecosystem. #![deny(rust_2018_idioms, nonstandard_style)] #![allow(clippy::type_complexity)] diff --git a/actix-utils/src/mpsc.rs b/actix-utils/src/mpsc.rs index 2299dedb..2f2b3f04 100644 --- a/actix-utils/src/mpsc.rs +++ b/actix-utils/src/mpsc.rs @@ -1,4 +1,5 @@ //! A multi-producer, single-consumer, futures-aware, FIFO queue. + use core::any::Any; use core::cell::RefCell; use core::fmt; diff --git a/actix-utils/src/timeout.rs b/actix-utils/src/timeout.rs index 612c3cb4..85e328b9 100644 --- a/actix-utils/src/timeout.rs +++ b/actix-utils/src/timeout.rs @@ -1,7 +1,6 @@ //! Service that applies a timeout to requests. //! -//! If the response does not complete within the specified timeout, the response -//! will be aborted. +//! If the response does not complete within the specified timeout, the response will be aborted. use core::future::Future; use core::marker::PhantomData; From b7202db8fd989cd8fe534b2f2c74c8205f867094 Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Tue, 29 Dec 2020 07:44:53 +0800 Subject: [PATCH 14/73] update actix-server and actix-testing to tokio 1.0 (#239) --- actix-server/CHANGES.md | 6 + actix-server/Cargo.toml | 26 +- actix-server/src/accept.rs | 500 +++++++++++++----------------- actix-server/src/builder.rs | 207 ++++++------- actix-server/src/config.rs | 61 ++-- actix-server/src/lib.rs | 102 ++++++ actix-server/src/server.rs | 32 +- actix-server/src/service.rs | 43 ++- actix-server/src/signals.rs | 86 +++-- actix-server/src/socket.rs | 326 +++++++++++-------- actix-server/src/waker_queue.rs | 89 ++++++ actix-server/src/worker.rs | 335 +++++++++----------- actix-server/tests/test_server.rs | 27 +- actix-testing/Cargo.toml | 4 +- actix-testing/src/lib.rs | 5 +- 15 files changed, 1008 insertions(+), 841 deletions(-) create mode 100644 actix-server/src/waker_queue.rs diff --git a/actix-server/CHANGES.md b/actix-server/CHANGES.md index 5e28fe0b..15d7d596 100644 --- a/actix-server/CHANGES.md +++ b/actix-server/CHANGES.md @@ -3,6 +3,12 @@ ## Unreleased - 2020-xx-xx * Added explicit info log message on accept queue pause. [#215] * Prevent double registration of sockets when back-pressure is resolved. [#223] +* Update `mio` dependency to `0.7.3`. +* Remove `socket2` dependency. +* `ServerBuilder::backlog` would accept `u32` instead of `i32`. +* Remove `AcceptNotify` type and pass `WakerQueue` to `Worker` for wake up the `Accept`'s `Poll`. +* Convert `mio::net::TcpStream` to `actix_rt::net::TcpStream`(`UnixStream` for uds) using `FromRawFd` and `IntoRawFd`(`FromRawSocket` and `IntoRawSocket` on windows). +* Remove `AsyncRead` and `AsyncWrite` trait bound for `socket::FromStream` trait. [#215]: https://github.com/actix/actix-net/pull/215 [#223]: https://github.com/actix/actix-net/pull/223 diff --git a/actix-server/Cargo.toml b/actix-server/Cargo.toml index 1a67f61c..34fb3775 100644 --- a/actix-server/Cargo.toml +++ b/actix-server/Cargo.toml @@ -20,25 +20,21 @@ path = "src/lib.rs" default = [] [dependencies] -actix-service = "1.0.6" -actix-rt = "1.1.1" -actix-codec = "0.3.0" -actix-utils = "2.0.0" +actix-codec = "0.4.0-beta.1" +actix-rt = "2.0.0-beta.1" +actix-service = "2.0.0-beta.1" +actix-utils = "3.0.0-beta.1" +futures-core = { version = "0.3.7", default-features = false } log = "0.4" +mio = { version = "0.7.6", features = ["os-poll", "net"] } num_cpus = "1.13" -mio = "0.6.19" -socket2 = "0.3" -futures-channel = { version = "0.3.4", default-features = false } -futures-util = { version = "0.3.4", default-features = false, features = ["sink"] } slab = "0.4" - -# unix domain sockets -# FIXME: Remove it and use mio own uds feature once mio 0.7 is released -mio-uds = { version = "0.6.7" } +tokio = { version = "1", features = ["sync"] } [dev-dependencies] -bytes = "0.5" -env_logger = "0.7" actix-testing = "1.0.0" -tokio = { version = "0.2", features = ["io-util"] } +bytes = "1" +env_logger = "0.7" +futures-util = { version = "0.3.7", default-features = false, features = ["sink"] } +tokio = { version = "1", features = ["io-util"] } diff --git a/actix-server/src/accept.rs b/actix-server/src/accept.rs index bef175d8..bf895f06 100644 --- a/actix-server/src/accept.rs +++ b/actix-server/src/accept.rs @@ -1,120 +1,86 @@ -use std::sync::mpsc as sync_mpsc; use std::time::Duration; use std::{io, thread}; -use actix_rt::time::{delay_until, Instant}; +use actix_rt::time::{sleep_until, Instant}; use actix_rt::System; use log::{error, info}; +use mio::{Interest, Poll, Token as MioToken}; use slab::Slab; use crate::server::Server; -use crate::socket::{SocketAddr, SocketListener, StdListener}; -use crate::worker::{Conn, WorkerClient}; +use crate::socket::{MioListener, SocketAddr}; +use crate::waker_queue::{WakerInterest, WakerQueue, WAKER_TOKEN}; +use crate::worker::{Conn, WorkerHandle}; use crate::Token; -pub(crate) enum Command { - Pause, - Resume, - Stop, - Worker(WorkerClient), -} - struct ServerSocketInfo { + // addr for socket. mainly used for logging. addr: SocketAddr, + // be ware this is the crate token for identify socket and should not be confused with + // mio::Token token: Token, - sock: SocketListener, + lst: MioListener, + // timeout is used to mark the deadline when this socket's listener should be registered again + // after an error. timeout: Option, } -#[derive(Clone)] -pub(crate) struct AcceptNotify(mio::SetReadiness); - -impl AcceptNotify { - pub(crate) fn new(ready: mio::SetReadiness) -> Self { - AcceptNotify(ready) - } - - pub(crate) fn notify(&self) { - let _ = self.0.set_readiness(mio::Ready::readable()); - } -} - -impl Default for AcceptNotify { - fn default() -> Self { - AcceptNotify::new(mio::Registration::new2().1) - } -} - +/// Accept loop would live with `ServerBuilder`. +/// +/// It's tasked with construct `Poll` instance and `WakerQueue` which would be distributed to +/// `Accept` and `Worker`. +/// +/// It would also listen to `ServerCommand` and push interests to `WakerQueue`. pub(crate) struct AcceptLoop { - cmd_reg: Option, - cmd_ready: mio::SetReadiness, - notify_reg: Option, - notify_ready: mio::SetReadiness, - tx: sync_mpsc::Sender, - rx: Option>, srv: Option, + poll: Option, + waker: WakerQueue, } impl AcceptLoop { - pub fn new(srv: Server) -> AcceptLoop { - let (tx, rx) = sync_mpsc::channel(); - let (cmd_reg, cmd_ready) = mio::Registration::new2(); - let (notify_reg, notify_ready) = mio::Registration::new2(); + pub fn new(srv: Server) -> Self { + let poll = Poll::new().unwrap_or_else(|e| panic!("Can not create `mio::Poll`: {}", e)); + let waker = WakerQueue::new(poll.registry()) + .unwrap_or_else(|e| panic!("Can not create `mio::Waker`: {}", e)); - AcceptLoop { - tx, - cmd_ready, - cmd_reg: Some(cmd_reg), - notify_ready, - notify_reg: Some(notify_reg), - rx: Some(rx), + Self { srv: Some(srv), + poll: Some(poll), + waker, } } - pub fn send(&self, msg: Command) { - let _ = self.tx.send(msg); - let _ = self.cmd_ready.set_readiness(mio::Ready::readable()); + pub(crate) fn waker_owned(&self) -> WakerQueue { + self.waker.clone() } - pub fn get_notify(&self) -> AcceptNotify { - AcceptNotify::new(self.notify_ready.clone()) + pub fn wake(&self, i: WakerInterest) { + self.waker.wake(i); } pub(crate) fn start( &mut self, - socks: Vec<(Token, StdListener)>, - workers: Vec, + socks: Vec<(Token, MioListener)>, + handles: Vec, ) { let srv = self.srv.take().expect("Can not re-use AcceptInfo"); + let poll = self.poll.take().unwrap(); + let waker = self.waker.clone(); - Accept::start( - self.rx.take().expect("Can not re-use AcceptInfo"), - self.cmd_reg.take().expect("Can not re-use AcceptInfo"), - self.notify_reg.take().expect("Can not re-use AcceptInfo"), - socks, - srv, - workers, - ); + Accept::start(poll, waker, socks, srv, handles); } } +/// poll instance of the server. struct Accept { - poll: mio::Poll, - rx: sync_mpsc::Receiver, - sockets: Slab, - workers: Vec, + poll: Poll, + waker: WakerQueue, + handles: Vec, srv: Server, - timer: (mio::Registration, mio::SetReadiness), next: usize, backpressure: bool, } -const DELTA: usize = 100; -const CMD: mio::Token = mio::Token(0); -const TIMER: mio::Token = mio::Token(1); -const NOTIFY: mio::Token = mio::Token(2); - /// This function defines errors that are per-connection. Which basically /// means that if we get this error from `accept()` system call it means /// next connection might be ready to be accepted. @@ -129,326 +95,290 @@ fn connection_error(e: &io::Error) -> bool { } impl Accept { - #![allow(clippy::too_many_arguments)] pub(crate) fn start( - rx: sync_mpsc::Receiver, - cmd_reg: mio::Registration, - notify_reg: mio::Registration, - socks: Vec<(Token, StdListener)>, + poll: Poll, + waker: WakerQueue, + socks: Vec<(Token, MioListener)>, srv: Server, - workers: Vec, + handles: Vec, ) { + // Accept runs in its own thread and would want to spawn additional futures to current + // actix system. let sys = System::current(); - - // start accept thread - let _ = thread::Builder::new() + thread::Builder::new() .name("actix-server accept loop".to_owned()) .spawn(move || { System::set_current(sys); - let mut accept = Accept::new(rx, socks, workers, srv); - - // Start listening for incoming commands - if let Err(err) = accept.poll.register( - &cmd_reg, - CMD, - mio::Ready::readable(), - mio::PollOpt::edge(), - ) { - panic!("Can not register Registration: {}", err); - } - - // Start listening for notify updates - if let Err(err) = accept.poll.register( - ¬ify_reg, - NOTIFY, - mio::Ready::readable(), - mio::PollOpt::edge(), - ) { - panic!("Can not register Registration: {}", err); - } - - accept.poll(); - }); + let (mut accept, sockets) = + Accept::new_with_sockets(poll, waker, socks, handles, srv); + accept.poll_with(sockets); + }) + .unwrap(); } - fn new( - rx: sync_mpsc::Receiver, - socks: Vec<(Token, StdListener)>, - workers: Vec, + fn new_with_sockets( + poll: Poll, + waker: WakerQueue, + socks: Vec<(Token, MioListener)>, + handles: Vec, srv: Server, - ) -> Accept { - // Create a poll instance - let poll = match mio::Poll::new() { - Ok(poll) => poll, - Err(err) => panic!("Can not create mio::Poll: {}", err), - }; - - // Start accept + ) -> (Accept, Slab) { let mut sockets = Slab::new(); - for (hnd_token, lst) in socks.into_iter() { + for (hnd_token, mut lst) in socks.into_iter() { let addr = lst.local_addr(); - let server = lst.into_listener(); let entry = sockets.vacant_entry(); let token = entry.key(); // Start listening for incoming connections - if let Err(err) = poll.register( - &server, - mio::Token(token + DELTA), - mio::Ready::readable(), - mio::PollOpt::edge(), - ) { - panic!("Can not register io: {}", err); - } + poll.registry() + .register(&mut lst, MioToken(token), Interest::READABLE) + .unwrap_or_else(|e| panic!("Can not register io: {}", e)); entry.insert(ServerSocketInfo { addr, token: hnd_token, - sock: server, + lst, timeout: None, }); } - // Timer - let (tm, tmr) = mio::Registration::new2(); - if let Err(err) = - poll.register(&tm, TIMER, mio::Ready::readable(), mio::PollOpt::edge()) - { - panic!("Can not register Registration: {}", err); - } - - Accept { + let accept = Accept { poll, - rx, - sockets, - workers, + waker, + handles, srv, next: 0, - timer: (tm, tmr), backpressure: false, - } + }; + + (accept, sockets) } - fn poll(&mut self) { - // Create storage for events + fn poll_with(&mut self, mut sockets: Slab) { let mut events = mio::Events::with_capacity(128); loop { - if let Err(err) = self.poll.poll(&mut events, None) { - panic!("Poll error: {}", err); - } + self.poll + .poll(&mut events, None) + .unwrap_or_else(|e| panic!("Poll error: {}", e)); for event in events.iter() { let token = event.token(); match token { - CMD => { - if !self.process_cmd() { - return; + // This is a loop because interests for command from previous version was + // a loop that would try to drain the command channel. It's yet unknown + // if it's necessary/good practice to actively drain the waker queue. + WAKER_TOKEN => 'waker: loop { + // take guard with every iteration so no new interest can be added + // until the current task is done. + let mut guard = self.waker.guard(); + match guard.pop_front() { + // worker notify it becomes available. we may want to recover + // from backpressure. + Some(WakerInterest::WorkerAvailable) => { + drop(guard); + self.maybe_backpressure(&mut sockets, false); + } + // a new worker thread is made and it's handle would be added + // to Accept + Some(WakerInterest::Worker(handle)) => { + drop(guard); + // maybe we want to recover from a backpressure. + self.maybe_backpressure(&mut sockets, false); + self.handles.push(handle); + } + // got timer interest and it's time to try register socket(s) + // again. + Some(WakerInterest::Timer) => { + drop(guard); + self.process_timer(&mut sockets) + } + Some(WakerInterest::Pause) => { + drop(guard); + sockets.iter_mut().for_each(|(_, info)| { + match self.deregister(info) { + Ok(_) => info!( + "Paused accepting connections on {}", + info.addr + ), + Err(e) => { + error!("Can not deregister server socket {}", e) + } + } + }); + } + Some(WakerInterest::Resume) => { + drop(guard); + sockets.iter_mut().for_each(|(token, info)| { + self.register_logged(token, info); + }); + } + Some(WakerInterest::Stop) => { + return self.deregister_all(&mut sockets); + } + // waker queue is drained. + None => { + // Reset the WakerQueue before break so it does not grow + // infinitely. + WakerQueue::reset(&mut guard); + break 'waker; + } } - } - TIMER => self.process_timer(), - NOTIFY => self.backpressure(false), + }, _ => { let token = usize::from(token); - if token < DELTA { - continue; - } - self.accept(token - DELTA); + self.accept(&mut sockets, token); } } } } } - fn process_timer(&mut self) { + fn process_timer(&self, sockets: &mut Slab) { let now = Instant::now(); - for (token, info) in self.sockets.iter_mut() { + sockets.iter_mut().for_each(|(token, info)| { + // only the ServerSocketInfo have an associate timeout value was de registered. if let Some(inst) = info.timeout.take() { if now > inst { - if let Err(err) = self.poll.register( - &info.sock, - mio::Token(token + DELTA), - mio::Ready::readable(), - mio::PollOpt::edge(), - ) { - error!("Can not register server socket {}", err); - } else { - info!("Resume accepting connections on {}", info.addr); - } + self.register_logged(token, info); } else { info.timeout = Some(inst); } } - } - } - - fn process_cmd(&mut self) -> bool { - loop { - match self.rx.try_recv() { - Ok(cmd) => match cmd { - Command::Pause => { - for (_, info) in self.sockets.iter_mut() { - if let Err(err) = self.poll.deregister(&info.sock) { - error!("Can not deregister server socket {}", err); - } else { - info!("Paused accepting connections on {}", info.addr); - } - } - } - Command::Resume => { - for (token, info) in self.sockets.iter() { - if let Err(err) = self.register(token, info) { - error!("Can not resume socket accept process: {}", err); - } else { - info!( - "Accepting connections on {} has been resumed", - info.addr - ); - } - } - } - Command::Stop => { - for (_, info) in self.sockets.iter() { - let _ = self.poll.deregister(&info.sock); - } - return false; - } - Command::Worker(worker) => { - self.backpressure(false); - self.workers.push(worker); - } - }, - Err(err) => match err { - sync_mpsc::TryRecvError::Empty => break, - sync_mpsc::TryRecvError::Disconnected => { - for (_, info) in self.sockets.iter() { - let _ = self.poll.deregister(&info.sock); - } - return false; - } - }, - } - } - true + }); } #[cfg(not(target_os = "windows"))] - fn register(&self, token: usize, info: &ServerSocketInfo) -> io::Result<()> { - self.poll.register( - &info.sock, - mio::Token(token + DELTA), - mio::Ready::readable(), - mio::PollOpt::edge(), - ) + fn register(&self, token: usize, info: &mut ServerSocketInfo) -> io::Result<()> { + self.poll + .registry() + .register(&mut info.lst, MioToken(token), Interest::READABLE) } #[cfg(target_os = "windows")] - fn register(&self, token: usize, info: &ServerSocketInfo) -> io::Result<()> { + fn register(&self, token: usize, info: &mut ServerSocketInfo) -> io::Result<()> { // On windows, calling register without deregister cause an error. // See https://github.com/actix/actix-web/issues/905 // Calling reregister seems to fix the issue. self.poll - .register( - &info.sock, - mio::Token(token + DELTA), - mio::Ready::readable(), - mio::PollOpt::edge(), - ) + .registry() + .register(&mut info.lst, mio::Token(token), Interest::READABLE) .or_else(|_| { - self.poll.reregister( - &info.sock, - mio::Token(token + DELTA), - mio::Ready::readable(), - mio::PollOpt::edge(), + self.poll.registry().reregister( + &mut info.lst, + mio::Token(token), + Interest::READABLE, ) }) } - fn backpressure(&mut self, on: bool) { + fn register_logged(&self, token: usize, info: &mut ServerSocketInfo) { + match self.register(token, info) { + Ok(_) => info!("Resume accepting connections on {}", info.addr), + Err(e) => error!("Can not register server socket {}", e), + } + } + + fn deregister(&self, info: &mut ServerSocketInfo) -> io::Result<()> { + self.poll.registry().deregister(&mut info.lst) + } + + fn deregister_all(&self, sockets: &mut Slab) { + sockets.iter_mut().for_each(|(_, info)| { + info!("Accepting connections on {} has been paused", info.addr); + let _ = self.deregister(info); + }); + } + + fn maybe_backpressure(&mut self, sockets: &mut Slab, on: bool) { if self.backpressure { if !on { self.backpressure = false; - for (token, info) in self.sockets.iter() { + for (token, info) in sockets.iter_mut() { if info.timeout.is_some() { // socket will attempt to re-register itself when its timeout completes continue; } - - if let Err(err) = self.register(token, info) { - error!("Can not resume socket accept process: {}", err); - } else { - info!("Accepting connections on {} has been resumed", info.addr); - } + self.register_logged(token, info); } } } else if on { self.backpressure = true; - for (_, info) in self.sockets.iter() { - let _ = self.poll.deregister(&info.sock); - info!("Accepting connections on {} has been paused", info.addr); - } + self.deregister_all(sockets); } } - fn accept_one(&mut self, mut msg: Conn) { + fn accept_one(&mut self, sockets: &mut Slab, mut msg: Conn) { if self.backpressure { - while !self.workers.is_empty() { - match self.workers[self.next].send(msg) { - Ok(_) => (), + while !self.handles.is_empty() { + match self.handles[self.next].send(msg) { + Ok(_) => { + self.set_next(); + break; + } Err(tmp) => { - self.srv.worker_faulted(self.workers[self.next].idx); + // worker lost contact and could be gone. a message is sent to + // `ServerBuilder` future to notify it a new worker should be made. + // after that remove the fault worker. + self.srv.worker_faulted(self.handles[self.next].idx); msg = tmp; - self.workers.swap_remove(self.next); - if self.workers.is_empty() { + self.handles.swap_remove(self.next); + if self.handles.is_empty() { error!("No workers"); return; - } else if self.workers.len() <= self.next { + } else if self.handles.len() <= self.next { self.next = 0; } continue; } } - self.next = (self.next + 1) % self.workers.len(); - break; } } else { let mut idx = 0; - while idx < self.workers.len() { + while idx < self.handles.len() { idx += 1; - if self.workers[self.next].available() { - match self.workers[self.next].send(msg) { + if self.handles[self.next].available() { + match self.handles[self.next].send(msg) { Ok(_) => { - self.next = (self.next + 1) % self.workers.len(); + self.set_next(); return; } + // worker lost contact and could be gone. a message is sent to + // `ServerBuilder` future to notify it a new worker should be made. + // after that remove the fault worker and enter backpressure if necessary. Err(tmp) => { - self.srv.worker_faulted(self.workers[self.next].idx); + self.srv.worker_faulted(self.handles[self.next].idx); msg = tmp; - self.workers.swap_remove(self.next); - if self.workers.is_empty() { + self.handles.swap_remove(self.next); + if self.handles.is_empty() { error!("No workers"); - self.backpressure(true); + self.maybe_backpressure(sockets, true); return; - } else if self.workers.len() <= self.next { + } else if self.handles.len() <= self.next { self.next = 0; } continue; } } } - self.next = (self.next + 1) % self.workers.len(); + self.set_next(); } // enable backpressure - self.backpressure(true); - self.accept_one(msg); + self.maybe_backpressure(sockets, true); + self.accept_one(sockets, msg); } } - fn accept(&mut self, token: usize) { + // set next worker handle that would accept work. + fn set_next(&mut self) { + self.next = (self.next + 1) % self.handles.len(); + } + + fn accept(&mut self, sockets: &mut Slab, token: usize) { loop { - let msg = if let Some(info) = self.sockets.get_mut(token) { - match info.sock.accept() { + let msg = if let Some(info) = sockets.get_mut(token) { + match info.lst.accept() { Ok(Some((io, addr))) => Conn { io, token: info.token, @@ -458,18 +388,22 @@ impl Accept { Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => return, Err(ref e) if connection_error(e) => continue, Err(e) => { + // deregister listener temporary error!("Error accepting connection: {}", e); - if let Err(err) = self.poll.deregister(&info.sock) { + if let Err(err) = self.deregister(info) { error!("Can not deregister server socket {}", err); } - // sleep after error + // sleep after error. write the timeout to socket info as later the poll + // would need it mark which socket and when it's listener should be + // registered. info.timeout = Some(Instant::now() + Duration::from_millis(500)); - let r = self.timer.1.clone(); + // after the sleep a Timer interest is sent to Accept Poll + let waker = self.waker.clone(); System::current().arbiter().send(Box::pin(async move { - delay_until(Instant::now() + Duration::from_millis(510)).await; - let _ = r.set_readiness(mio::Ready::readable()); + sleep_until(Instant::now() + Duration::from_millis(510)).await; + waker.wake(WakerInterest::Timer); })); return; } @@ -478,7 +412,7 @@ impl Accept { return; }; - self.accept_one(msg); + self.accept_one(sockets, msg); } } } diff --git a/actix-server/src/builder.rs b/actix-server/src/builder.rs index 64a45df9..51dd0eda 100644 --- a/actix-server/src/builder.rs +++ b/actix-server/src/builder.rs @@ -1,36 +1,35 @@ +use std::future::Future; use std::pin::Pin; use std::task::{Context, Poll}; use std::time::Duration; -use std::{io, mem, net}; +use std::{io, mem}; use actix_rt::net::TcpStream; -use actix_rt::time::{delay_until, Instant}; +use actix_rt::time::{sleep_until, Instant}; use actix_rt::{spawn, System}; -use futures_channel::mpsc::{unbounded, UnboundedReceiver}; -use futures_channel::oneshot; -use futures_util::future::ready; -use futures_util::stream::FuturesUnordered; -use futures_util::{future::Future, ready, stream::Stream, FutureExt, StreamExt}; use log::{error, info}; -use socket2::{Domain, Protocol, Socket, Type}; +use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver}; +use tokio::sync::oneshot; -use crate::accept::{AcceptLoop, AcceptNotify, Command}; +use crate::accept::AcceptLoop; use crate::config::{ConfiguredService, ServiceConfig}; use crate::server::{Server, ServerCommand}; use crate::service::{InternalServiceFactory, ServiceFactory, StreamNewService}; use crate::signals::{Signal, Signals}; -use crate::socket::StdListener; -use crate::worker::{self, Worker, WorkerAvailability, WorkerClient}; -use crate::Token; +use crate::socket::{MioListener, StdSocketAddr, StdTcpListener, ToSocketAddrs}; +use crate::socket::{MioTcpListener, MioTcpSocket}; +use crate::waker_queue::{WakerInterest, WakerQueue}; +use crate::worker::{self, Worker, WorkerAvailability, WorkerHandle}; +use crate::{join_all, Token}; /// Server builder pub struct ServerBuilder { threads: usize, token: Token, - backlog: i32, - workers: Vec<(usize, WorkerClient)>, + backlog: u32, + handles: Vec<(usize, WorkerHandle)>, services: Vec>, - sockets: Vec<(Token, String, StdListener)>, + sockets: Vec<(Token, String, MioListener)>, accept: AcceptLoop, exit: bool, shutdown_timeout: Duration, @@ -49,13 +48,13 @@ impl Default for ServerBuilder { impl ServerBuilder { /// Create new Server builder instance pub fn new() -> ServerBuilder { - let (tx, rx) = unbounded(); + let (tx, rx) = unbounded_channel(); let server = Server::new(tx); ServerBuilder { threads: num_cpus::get(), - token: Token(0), - workers: Vec::new(), + token: Token::default(), + handles: Vec::new(), services: Vec::new(), sockets: Vec::new(), accept: AcceptLoop::new(server.clone()), @@ -89,7 +88,7 @@ impl ServerBuilder { /// Generally set in the 64-2048 range. Default value is 2048. /// /// This method should be called before `bind()` method call. - pub fn backlog(mut self, num: i32) -> Self { + pub fn backlog(mut self, num: u32) -> Self { self.backlog = num; self } @@ -147,7 +146,7 @@ impl ServerBuilder { for (name, lst) in cfg.services { let token = self.token.next(); srv.stream(token, name.clone(), lst.local_addr()?); - self.sockets.push((token, name, StdListener::Tcp(lst))); + self.sockets.push((token, name, MioListener::Tcp(lst))); } self.services.push(Box::new(srv)); } @@ -160,7 +159,7 @@ impl ServerBuilder { pub fn bind>(mut self, name: N, addr: U, factory: F) -> io::Result where F: ServiceFactory, - U: net::ToSocketAddrs, + U: ToSocketAddrs, { let sockets = bind_addr(addr, self.backlog)?; @@ -173,12 +172,12 @@ impl ServerBuilder { lst.local_addr()?, )); self.sockets - .push((token, name.as_ref().to_string(), StdListener::Tcp(lst))); + .push((token, name.as_ref().to_string(), MioListener::Tcp(lst))); } Ok(self) } - #[cfg(all(unix))] + #[cfg(unix)] /// Add new unix domain service to the server. pub fn bind_uds(self, name: N, addr: U, factory: F) -> io::Result where @@ -186,8 +185,6 @@ impl ServerBuilder { N: AsRef, U: AsRef, { - use std::os::unix::net::UnixListener; - // The path must not exist when we try to bind. // Try to remove it to avoid bind error. if let Err(e) = std::fs::remove_file(addr.as_ref()) { @@ -197,26 +194,27 @@ impl ServerBuilder { } } - let lst = UnixListener::bind(addr)?; + let lst = crate::socket::StdUnixListener::bind(addr)?; self.listen_uds(name, lst, factory) } - #[cfg(all(unix))] + #[cfg(unix)] /// Add new unix domain service to the server. /// Useful when running as a systemd service and /// a socket FD can be acquired using the systemd crate. pub fn listen_uds>( mut self, name: N, - lst: std::os::unix::net::UnixListener, + lst: crate::socket::StdUnixListener, factory: F, ) -> io::Result where F: ServiceFactory, { - use std::net::{IpAddr, Ipv4Addr, SocketAddr}; + use std::net::{IpAddr, Ipv4Addr}; + lst.set_nonblocking(true)?; let token = self.token.next(); - let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080); + let addr = StdSocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080); self.services.push(StreamNewService::create( name.as_ref().to_string(), token, @@ -224,7 +222,7 @@ impl ServerBuilder { addr, )); self.sockets - .push((token, name.as_ref().to_string(), StdListener::Uds(lst))); + .push((token, name.as_ref().to_string(), MioListener::from(lst))); Ok(self) } @@ -232,21 +230,25 @@ impl ServerBuilder { pub fn listen>( mut self, name: N, - lst: net::TcpListener, + lst: StdTcpListener, factory: F, ) -> io::Result where F: ServiceFactory, { + lst.set_nonblocking(true)?; + let addr = lst.local_addr()?; + let token = self.token.next(); self.services.push(StreamNewService::create( name.as_ref().to_string(), token, factory, - lst.local_addr()?, + addr, )); + self.sockets - .push((token, name.as_ref().to_string(), StdListener::Tcp(lst))); + .push((token, name.as_ref().to_string(), MioListener::from(lst))); Ok(self) } @@ -263,12 +265,12 @@ impl ServerBuilder { info!("Starting {} workers", self.threads); // start workers - let workers = (0..self.threads) + let handles = (0..self.threads) .map(|idx| { - let worker = self.start_worker(idx, self.accept.get_notify()); - self.workers.push((idx, worker.clone())); + let handle = self.start_worker(idx, self.accept.waker_owned()); + self.handles.push((idx, handle.clone())); - worker + handle }) .collect(); @@ -281,7 +283,7 @@ impl ServerBuilder { .into_iter() .map(|t| (t.0, t.2)) .collect(), - workers, + handles, ); // handle signals @@ -296,10 +298,9 @@ impl ServerBuilder { } } - fn start_worker(&self, idx: usize, notify: AcceptNotify) -> WorkerClient { - let avail = WorkerAvailability::new(notify); - let services: Vec> = - self.services.iter().map(|v| v.clone_factory()).collect(); + fn start_worker(&self, idx: usize, waker: WakerQueue) -> WorkerHandle { + let avail = WorkerAvailability::new(waker); + let services = self.services.iter().map(|v| v.clone_factory()).collect(); Worker::start(idx, services, avail, self.shutdown_timeout) } @@ -307,11 +308,11 @@ impl ServerBuilder { fn handle_cmd(&mut self, item: ServerCommand) { match item { ServerCommand::Pause(tx) => { - self.accept.send(Command::Pause); + self.accept.wake(WakerInterest::Pause); let _ = tx.send(()); } ServerCommand::Resume(tx) => { - self.accept.send(Command::Resume); + self.accept.wake(WakerInterest::Resume); let _ = tx.send(()); } ServerCommand::Signal(sig) => { @@ -355,50 +356,41 @@ impl ServerBuilder { let exit = self.exit; // stop accept thread - self.accept.send(Command::Stop); + self.accept.wake(WakerInterest::Stop); let notify = std::mem::take(&mut self.notify); // stop workers - if !self.workers.is_empty() && graceful { - spawn( - self.workers - .iter() - .map(move |worker| worker.1.stop(graceful)) - .collect::>() - .collect::>() - .then(move |_| { - if let Some(tx) = completion { - let _ = tx.send(()); - } - for tx in notify { - let _ = tx.send(()); - } - if exit { - spawn( - async { - delay_until( - Instant::now() + Duration::from_millis(300), - ) - .await; - System::current().stop(); - } - .boxed(), - ); - } - ready(()) - }), - ) + if !self.handles.is_empty() && graceful { + let iter = self + .handles + .iter() + .map(move |worker| worker.1.stop(graceful)) + .collect(); + + let fut = join_all(iter); + + spawn(async move { + let _ = fut.await; + if let Some(tx) = completion { + let _ = tx.send(()); + } + for tx in notify { + let _ = tx.send(()); + } + if exit { + spawn(async { + sleep_until(Instant::now() + Duration::from_millis(300)).await; + System::current().stop(); + }); + } + }) } else { // we need to stop system if server was spawned if self.exit { - spawn( - delay_until(Instant::now() + Duration::from_millis(300)).then( - |_| { - System::current().stop(); - ready(()) - }, - ), - ); + spawn(async { + sleep_until(Instant::now() + Duration::from_millis(300)).await; + System::current().stop(); + }); } if let Some(tx) = completion { let _ = tx.send(()); @@ -410,9 +402,9 @@ impl ServerBuilder { } ServerCommand::WorkerFaulted(idx) => { let mut found = false; - for i in 0..self.workers.len() { - if self.workers[i].0 == idx { - self.workers.swap_remove(i); + for i in 0..self.handles.len() { + if self.handles[i].0 == idx { + self.handles.swap_remove(i); found = true; break; } @@ -421,10 +413,10 @@ impl ServerBuilder { if found { error!("Worker has died {:?}, restarting", idx); - let mut new_idx = self.workers.len(); + let mut new_idx = self.handles.len(); 'found: loop { - for i in 0..self.workers.len() { - if self.workers[i].0 == new_idx { + for i in 0..self.handles.len() { + if self.handles[i].0 == new_idx { new_idx += 1; continue 'found; } @@ -432,9 +424,9 @@ impl ServerBuilder { break; } - let worker = self.start_worker(new_idx, self.accept.get_notify()); - self.workers.push((new_idx, worker.clone())); - self.accept.send(Command::Worker(worker)); + let handle = self.start_worker(new_idx, self.accept.waker_owned()); + self.handles.push((new_idx, handle.clone())); + self.accept.wake(WakerInterest::Worker(handle)); } } } @@ -446,20 +438,18 @@ impl Future for ServerBuilder { fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { loop { - match ready!(Pin::new(&mut self.cmd).poll_next(cx)) { - Some(it) => self.as_mut().get_mut().handle_cmd(it), - None => { - return Poll::Pending; - } + match Pin::new(&mut self.cmd).poll_recv(cx) { + Poll::Ready(Some(it)) => self.as_mut().get_mut().handle_cmd(it), + _ => return Poll::Pending, } } } } -pub(super) fn bind_addr( +pub(super) fn bind_addr( addr: S, - backlog: i32, -) -> io::Result> { + backlog: u32, +) -> io::Result> { let mut err = None; let mut succ = false; let mut sockets = Vec::new(); @@ -487,14 +477,13 @@ pub(super) fn bind_addr( } } -fn create_tcp_listener(addr: net::SocketAddr, backlog: i32) -> io::Result { - let domain = match addr { - net::SocketAddr::V4(_) => Domain::ipv4(), - net::SocketAddr::V6(_) => Domain::ipv6(), +fn create_tcp_listener(addr: StdSocketAddr, backlog: u32) -> io::Result { + let socket = match addr { + StdSocketAddr::V4(_) => MioTcpSocket::new_v4()?, + StdSocketAddr::V6(_) => MioTcpSocket::new_v6()?, }; - let socket = Socket::new(domain, Type::stream(), Some(Protocol::tcp()))?; - socket.set_reuse_address(true)?; - socket.bind(&addr.into())?; - socket.listen(backlog)?; - Ok(socket.into_tcp_listener()) + + socket.set_reuseaddr(true)?; + socket.bind(addr)?; + socket.listen(backlog) } diff --git a/actix-server/src/config.rs b/actix-server/src/config.rs index a1315a72..20270a2f 100644 --- a/actix-server/src/config.rs +++ b/actix-server/src/config.rs @@ -1,5 +1,6 @@ use std::collections::HashMap; -use std::{fmt, io, net}; +use std::future::Future; +use std::{fmt, io}; use actix_rt::net::TcpStream; use actix_service::{ @@ -7,23 +8,23 @@ use actix_service::{ ServiceFactory as BaseServiceFactory, }; use actix_utils::counter::CounterGuard; -use futures_util::future::{ok, Future, FutureExt, LocalBoxFuture}; +use futures_core::future::LocalBoxFuture; use log::error; -use super::builder::bind_addr; -use super::service::{BoxedServerService, InternalServiceFactory, StreamService}; -use super::Token; -use crate::socket::StdStream; +use crate::builder::bind_addr; +use crate::service::{BoxedServerService, InternalServiceFactory, StreamService}; +use crate::socket::{MioStream, MioTcpListener, StdSocketAddr, StdTcpListener, ToSocketAddrs}; +use crate::{ready, Token}; pub struct ServiceConfig { - pub(crate) services: Vec<(String, net::TcpListener)>, + pub(crate) services: Vec<(String, MioTcpListener)>, pub(crate) apply: Option>, pub(crate) threads: usize, - pub(crate) backlog: i32, + pub(crate) backlog: u32, } impl ServiceConfig { - pub(super) fn new(threads: usize, backlog: i32) -> ServiceConfig { + pub(super) fn new(threads: usize, backlog: u32) -> ServiceConfig { ServiceConfig { threads, backlog, @@ -43,24 +44,20 @@ impl ServiceConfig { /// Add new service to server pub fn bind>(&mut self, name: N, addr: U) -> io::Result<&mut Self> where - U: net::ToSocketAddrs, + U: ToSocketAddrs, { let sockets = bind_addr(addr, self.backlog)?; for lst in sockets { - self.listen(name.as_ref(), lst); + self._listen(name.as_ref(), lst); } Ok(self) } /// Add new service to server - pub fn listen>(&mut self, name: N, lst: net::TcpListener) -> &mut Self { - if self.apply.is_none() { - self.apply = Some(Box::new(not_configured)); - } - self.services.push((name.as_ref().to_string(), lst)); - self + pub fn listen>(&mut self, name: N, lst: StdTcpListener) -> &mut Self { + self._listen(name, MioTcpListener::from_std(lst)) } /// Register service configuration function. This function get called @@ -72,11 +69,19 @@ impl ServiceConfig { self.apply = Some(Box::new(f)); Ok(()) } + + fn _listen>(&mut self, name: N, lst: MioTcpListener) -> &mut Self { + if self.apply.is_none() { + self.apply = Some(Box::new(not_configured)); + } + self.services.push((name.as_ref().to_string(), lst)); + self + } } pub(super) struct ConfiguredService { rt: Box, - names: HashMap, + names: HashMap, topics: HashMap, services: Vec, } @@ -91,7 +96,7 @@ impl ConfiguredService { } } - pub(super) fn stream(&mut self, token: Token, name: String, addr: net::SocketAddr) { + pub(super) fn stream(&mut self, token: Token, name: String, addr: StdSocketAddr) { self.names.insert(token, (name.clone(), addr)); self.topics.insert(name, token); self.services.push(token); @@ -121,7 +126,7 @@ impl InternalServiceFactory for ConfiguredService { let tokens = self.services.clone(); // construct services - async move { + Box::pin(async move { let mut services = rt.services; // TODO: Proper error handling here for f in rt.onstart.into_iter() { @@ -146,14 +151,13 @@ impl InternalServiceFactory for ConfiguredService { token, Box::new(StreamService::new(fn_service(move |_: TcpStream| { error!("Service {:?} is not configured", name); - ok::<_, ()>(()) + ready::>(Ok(())) }))), )); }; } Ok(res) - } - .boxed_local() + }) } } @@ -233,13 +237,13 @@ impl ServiceRuntime { where F: Future + 'static, { - self.onstart.push(fut.boxed_local()) + self.onstart.push(Box::pin(fut)) } } type BoxedNewService = Box< dyn BaseServiceFactory< - (Option, StdStream), + (Option, MioStream), Response = (), Error = (), InitError = (), @@ -253,7 +257,7 @@ struct ServiceFactory { inner: T, } -impl BaseServiceFactory<(Option, StdStream)> for ServiceFactory +impl BaseServiceFactory<(Option, MioStream)> for ServiceFactory where T: BaseServiceFactory, T::Future: 'static, @@ -270,7 +274,7 @@ where fn new_service(&self, _: ()) -> Self::Future { let fut = self.inner.new_service(()); - async move { + Box::pin(async move { match fut.await { Ok(s) => Ok(Box::new(StreamService::new(s)) as BoxedServerService), Err(e) => { @@ -278,7 +282,6 @@ where Err(()) } } - } - .boxed_local() + }) } } diff --git a/actix-server/src/lib.rs b/actix-server/src/lib.rs index d7a7c242..64aca7e4 100644 --- a/actix-server/src/lib.rs +++ b/actix-server/src/lib.rs @@ -11,6 +11,7 @@ mod server; mod service; mod signals; mod socket; +mod waker_queue; mod worker; pub use self::builder::ServerBuilder; @@ -21,11 +22,25 @@ pub use self::service::ServiceFactory; #[doc(hidden)] pub use self::socket::FromStream; +use std::future::Future; +use std::pin::Pin; +use std::task::{Context, Poll}; + /// Socket ID token #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub(crate) struct Token(usize); +impl Default for Token { + fn default() -> Self { + Self::new() + } +} + impl Token { + fn new() -> Self { + Self(0) + } + pub(crate) fn next(&mut self) -> Token { let token = Token(self.0); self.0 += 1; @@ -37,3 +52,90 @@ impl Token { pub fn new() -> ServerBuilder { ServerBuilder::default() } + +// temporary Ready type for std::future::{ready, Ready}; Can be removed when MSRV surpass 1.48 +#[doc(hidden)] +pub struct Ready(Option); + +pub(crate) fn ready(t: T) -> Ready { + Ready(Some(t)) +} + +impl Unpin for Ready {} + +impl Future for Ready { + type Output = T; + + fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll { + Poll::Ready(self.get_mut().0.take().unwrap()) + } +} + +// a poor man's join future. joined future is only used when starting/stopping the server. +// pin_project and pinned futures are overkill for this task. +pub(crate) struct JoinAll { + fut: Vec>, +} + +pub(crate) fn join_all(fut: Vec + 'static>) -> JoinAll { + let fut = fut + .into_iter() + .map(|f| JoinFuture::Future(Box::pin(f))) + .collect(); + + JoinAll { fut } +} + +enum JoinFuture { + Future(Pin>>), + Result(Option), +} + +impl Unpin for JoinAll {} + +impl Future for JoinAll { + type Output = Vec; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut ready = true; + + let this = self.get_mut(); + for fut in this.fut.iter_mut() { + if let JoinFuture::Future(f) = fut { + match f.as_mut().poll(cx) { + Poll::Ready(t) => { + *fut = JoinFuture::Result(Some(t)); + } + Poll::Pending => ready = false, + } + } + } + + if ready { + let mut res = Vec::new(); + for fut in this.fut.iter_mut() { + if let JoinFuture::Result(f) = fut { + res.push(f.take().unwrap()); + } + } + + Poll::Ready(res) + } else { + Poll::Pending + } + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[actix_rt::test] + async fn test_join_all() { + let futs = vec![ready(Ok(1)), ready(Err(3)), ready(Ok(9))]; + let mut res = join_all(futs).await.into_iter(); + assert_eq!(Ok(1), res.next().unwrap()); + assert_eq!(Err(3), res.next().unwrap()); + assert_eq!(Ok(9), res.next().unwrap()); + } +} diff --git a/actix-server/src/server.rs b/actix-server/src/server.rs index b29a9e02..6b0d0aea 100644 --- a/actix-server/src/server.rs +++ b/actix-server/src/server.rs @@ -3,9 +3,8 @@ use std::io; use std::pin::Pin; use std::task::{Context, Poll}; -use futures_channel::mpsc::UnboundedSender; -use futures_channel::oneshot; -use futures_util::FutureExt; +use tokio::sync::mpsc::UnboundedSender; +use tokio::sync::oneshot; use crate::builder::ServerBuilder; use crate::signals::Signal; @@ -42,11 +41,11 @@ impl Server { } pub(crate) fn signal(&self, sig: Signal) { - let _ = self.0.unbounded_send(ServerCommand::Signal(sig)); + let _ = self.0.send(ServerCommand::Signal(sig)); } pub(crate) fn worker_faulted(&self, idx: usize) { - let _ = self.0.unbounded_send(ServerCommand::WorkerFaulted(idx)); + let _ = self.0.send(ServerCommand::WorkerFaulted(idx)); } /// Pause accepting incoming connections @@ -55,15 +54,19 @@ impl Server { /// All opened connection remains active. pub fn pause(&self) -> impl Future { let (tx, rx) = oneshot::channel(); - let _ = self.0.unbounded_send(ServerCommand::Pause(tx)); - rx.map(|_| ()) + let _ = self.0.send(ServerCommand::Pause(tx)); + async { + let _ = rx.await; + } } /// Resume accepting incoming connections pub fn resume(&self) -> impl Future { let (tx, rx) = oneshot::channel(); - let _ = self.0.unbounded_send(ServerCommand::Resume(tx)); - rx.map(|_| ()) + let _ = self.0.send(ServerCommand::Resume(tx)); + async { + let _ = rx.await; + } } /// Stop incoming connection processing, stop all workers and exit. @@ -71,11 +74,13 @@ impl Server { /// If server starts with `spawn()` method, then spawned thread get terminated. pub fn stop(&self, graceful: bool) -> impl Future { let (tx, rx) = oneshot::channel(); - let _ = self.0.unbounded_send(ServerCommand::Stop { + let _ = self.0.send(ServerCommand::Stop { graceful, completion: Some(tx), }); - rx.map(|_| ()) + async { + let _ = rx.await; + } } } @@ -93,7 +98,7 @@ impl Future for Server { if this.1.is_none() { let (tx, rx) = oneshot::channel(); - if this.0.unbounded_send(ServerCommand::Notify(tx)).is_err() { + if this.0.send(ServerCommand::Notify(tx)).is_err() { return Poll::Ready(Ok(())); } this.1 = Some(rx); @@ -101,8 +106,7 @@ impl Future for Server { match Pin::new(this.1.as_mut().unwrap()).poll(cx) { Poll::Pending => Poll::Pending, - Poll::Ready(Ok(_)) => Poll::Ready(Ok(())), - Poll::Ready(Err(_)) => Poll::Ready(Ok(())), + Poll::Ready(_) => Poll::Ready(Ok(())), } } } diff --git a/actix-server/src/service.rs b/actix-server/src/service.rs index 569ce048..04b7dce8 100644 --- a/actix-server/src/service.rs +++ b/actix-server/src/service.rs @@ -2,15 +2,13 @@ use std::marker::PhantomData; use std::net::SocketAddr; use std::task::{Context, Poll}; -use actix_rt::spawn; use actix_service::{Service, ServiceFactory as BaseServiceFactory}; use actix_utils::counter::CounterGuard; -use futures_util::future::{err, ok, LocalBoxFuture, Ready}; -use futures_util::{FutureExt, TryFutureExt}; +use futures_core::future::LocalBoxFuture; use log::error; -use super::Token; -use crate::socket::{FromStream, StdStream}; +use crate::socket::{FromStream, MioStream}; +use crate::{ready, Ready, Token}; pub trait ServiceFactory: Send + Clone + 'static { type Factory: BaseServiceFactory; @@ -28,7 +26,7 @@ pub(crate) trait InternalServiceFactory: Send { pub(crate) type BoxedServerService = Box< dyn Service< - (Option, StdStream), + (Option, MioStream), Response = (), Error = (), Future = Ready>, @@ -49,7 +47,7 @@ impl StreamService { } } -impl Service<(Option, StdStream)> for StreamService +impl Service<(Option, MioStream)> for StreamService where S: Service, S::Future: 'static, @@ -64,21 +62,21 @@ where self.service.poll_ready(ctx).map_err(|_| ()) } - fn call(&mut self, (guard, req): (Option, StdStream)) -> Self::Future { - match FromStream::from_stdstream(req) { + fn call(&mut self, (guard, req): (Option, MioStream)) -> Self::Future { + ready(match FromStream::from_mio(req) { Ok(stream) => { let f = self.service.call(stream); - spawn(async move { + actix_rt::spawn(async move { let _ = f.await; drop(guard); }); - ok(()) + Ok(()) } Err(e) => { error!("Can not convert to an async tcp stream: {}", e); - err(()) + Err(()) } - } + }) } } @@ -132,15 +130,16 @@ where fn create(&self) -> LocalBoxFuture<'static, Result, ()>> { let token = self.token; - self.inner - .create() - .new_service(()) - .map_err(|_| ()) - .map_ok(move |inner| { - let service: BoxedServerService = Box::new(StreamService::new(inner)); - vec![(token, service)] - }) - .boxed_local() + let fut = self.inner.create().new_service(()); + Box::pin(async move { + match fut.await { + Ok(inner) => { + let service = Box::new(StreamService::new(inner)) as _; + Ok(vec![(token, service)]) + } + Err(_) => Err(()), + } + }) } } diff --git a/actix-server/src/signals.rs b/actix-server/src/signals.rs index 4fc51fc1..ea1de47e 100644 --- a/actix-server/src/signals.rs +++ b/actix-server/src/signals.rs @@ -2,7 +2,7 @@ use std::future::Future; use std::pin::Pin; use std::task::{Context, Poll}; -use futures_util::future::lazy; +use futures_core::future::LocalBoxFuture; use crate::server::Server; @@ -23,48 +23,51 @@ pub(crate) enum Signal { pub(crate) struct Signals { srv: Server, #[cfg(not(unix))] - stream: Pin>>>, + signals: LocalBoxFuture<'static, std::io::Result<()>>, #[cfg(unix)] - streams: Vec<(Signal, actix_rt::signal::unix::Signal)>, + signals: Vec<(Signal, LocalBoxFuture<'static, ()>)>, } impl Signals { pub(crate) fn start(srv: Server) { - actix_rt::spawn(lazy(|_| { - #[cfg(not(unix))] - { - actix_rt::spawn(Signals { - srv, - stream: Box::pin(actix_rt::signal::ctrl_c()), - }); - } - #[cfg(unix)] - { - use actix_rt::signal::unix; + #[cfg(not(unix))] + { + actix_rt::spawn(Signals { + srv, + signals: Box::pin(actix_rt::signal::ctrl_c()), + }); + } + #[cfg(unix)] + { + use actix_rt::signal::unix; - let mut streams = Vec::new(); + let sig_map = [ + (unix::SignalKind::interrupt(), Signal::Int), + (unix::SignalKind::hangup(), Signal::Hup), + (unix::SignalKind::terminate(), Signal::Term), + (unix::SignalKind::quit(), Signal::Quit), + ]; - let sig_map = [ - (unix::SignalKind::interrupt(), Signal::Int), - (unix::SignalKind::hangup(), Signal::Hup), - (unix::SignalKind::terminate(), Signal::Term), - (unix::SignalKind::quit(), Signal::Quit), - ]; + let mut signals = Vec::new(); - for (kind, sig) in sig_map.iter() { - match unix::signal(*kind) { - Ok(stream) => streams.push((*sig, stream)), - Err(e) => log::error!( - "Can not initialize stream handler for {:?} err: {}", - sig, - e - ), + for (kind, sig) in sig_map.iter() { + match unix::signal(*kind) { + Ok(mut stream) => { + let fut = Box::pin(async move { + let _ = stream.recv().await; + }) as _; + signals.push((*sig, fut)); } + Err(e) => log::error!( + "Can not initialize stream handler for {:?} err: {}", + sig, + e + ), } - - actix_rt::spawn(Signals { srv, streams }) } - })); + + actix_rt::spawn(Signals { srv, signals }); + } } } @@ -73,25 +76,20 @@ impl Future for Signals { fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { #[cfg(not(unix))] - match Pin::new(&mut self.stream).poll(cx) { + match self.signals.as_mut().poll(cx) { Poll::Ready(_) => { self.srv.signal(Signal::Int); Poll::Ready(()) } - Poll::Pending => return Poll::Pending, + Poll::Pending => Poll::Pending, } #[cfg(unix)] { - for idx in 0..self.streams.len() { - loop { - match self.streams[idx].1.poll_recv(cx) { - Poll::Ready(None) => return Poll::Ready(()), - Poll::Pending => break, - Poll::Ready(Some(_)) => { - let sig = self.streams[idx].0; - self.srv.signal(sig); - } - } + for (sig, fut) in self.signals.iter_mut() { + if fut.as_mut().poll(cx).is_ready() { + let sig = *sig; + self.srv.signal(sig); + return Poll::Ready(()); } } Poll::Pending diff --git a/actix-server/src/socket.rs b/actix-server/src/socket.rs index 3025660a..416e253b 100644 --- a/actix-server/src/socket.rs +++ b/actix-server/src/socket.rs @@ -1,135 +1,91 @@ -use std::{fmt, io, net}; +pub(crate) use std::net::{ + SocketAddr as StdSocketAddr, TcpListener as StdTcpListener, ToSocketAddrs, +}; + +pub(crate) use mio::net::{TcpListener as MioTcpListener, TcpSocket as MioTcpSocket}; +#[cfg(unix)] +pub(crate) use { + mio::net::UnixListener as MioUnixListener, + std::os::unix::net::UnixListener as StdUnixListener, +}; + +use std::{fmt, io}; -use actix_codec::{AsyncRead, AsyncWrite}; use actix_rt::net::TcpStream; +use mio::event::Source; +use mio::net::TcpStream as MioTcpStream; +use mio::{Interest, Registry, Token}; -pub(crate) enum StdListener { - Tcp(net::TcpListener), - #[cfg(all(unix))] - Uds(std::os::unix::net::UnixListener), +#[cfg(windows)] +use std::os::windows::io::{FromRawSocket, IntoRawSocket}; +#[cfg(unix)] +use { + actix_rt::net::UnixStream, + mio::net::{SocketAddr as MioSocketAddr, UnixStream as MioUnixStream}, + std::os::unix::io::{FromRawFd, IntoRawFd}, +}; + +pub(crate) enum MioListener { + Tcp(MioTcpListener), + #[cfg(unix)] + Uds(MioUnixListener), } -pub(crate) enum SocketAddr { - Tcp(net::SocketAddr), - #[cfg(all(unix))] - Uds(std::os::unix::net::SocketAddr), -} - -impl fmt::Display for SocketAddr { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - SocketAddr::Tcp(ref addr) => write!(f, "{}", addr), - #[cfg(all(unix))] - SocketAddr::Uds(ref addr) => write!(f, "{:?}", addr), - } - } -} - -impl fmt::Debug for SocketAddr { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - SocketAddr::Tcp(ref addr) => write!(f, "{:?}", addr), - #[cfg(all(unix))] - SocketAddr::Uds(ref addr) => write!(f, "{:?}", addr), - } - } -} - -impl fmt::Display for StdListener { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - StdListener::Tcp(ref lst) => write!(f, "{}", lst.local_addr().ok().unwrap()), - #[cfg(all(unix))] - StdListener::Uds(ref lst) => write!(f, "{:?}", lst.local_addr().ok().unwrap()), - } - } -} - -impl StdListener { +impl MioListener { pub(crate) fn local_addr(&self) -> SocketAddr { - match self { - StdListener::Tcp(lst) => SocketAddr::Tcp(lst.local_addr().unwrap()), - #[cfg(all(unix))] - StdListener::Uds(lst) => SocketAddr::Uds(lst.local_addr().unwrap()), - } - } - - pub(crate) fn into_listener(self) -> SocketListener { - match self { - StdListener::Tcp(lst) => SocketListener::Tcp( - mio::net::TcpListener::from_std(lst) - .expect("Can not create mio::net::TcpListener"), - ), - #[cfg(all(unix))] - StdListener::Uds(lst) => SocketListener::Uds( - mio_uds::UnixListener::from_listener(lst) - .expect("Can not create mio_uds::UnixListener"), - ), - } - } -} - -#[derive(Debug)] -pub enum StdStream { - Tcp(std::net::TcpStream), - #[cfg(all(unix))] - Uds(std::os::unix::net::UnixStream), -} - -pub(crate) enum SocketListener { - Tcp(mio::net::TcpListener), - #[cfg(all(unix))] - Uds(mio_uds::UnixListener), -} - -impl SocketListener { - pub(crate) fn accept(&self) -> io::Result> { match *self { - SocketListener::Tcp(ref lst) => lst - .accept_std() - .map(|(stream, addr)| Some((StdStream::Tcp(stream), SocketAddr::Tcp(addr)))), - #[cfg(all(unix))] - SocketListener::Uds(ref lst) => lst.accept_std().map(|res| { - res.map(|(stream, addr)| (StdStream::Uds(stream), SocketAddr::Uds(addr))) - }), + MioListener::Tcp(ref lst) => SocketAddr::Tcp(lst.local_addr().unwrap()), + #[cfg(unix)] + MioListener::Uds(ref lst) => SocketAddr::Uds(lst.local_addr().unwrap()), + } + } + + pub(crate) fn accept(&self) -> io::Result> { + match *self { + MioListener::Tcp(ref lst) => lst + .accept() + .map(|(stream, addr)| Some((MioStream::Tcp(stream), SocketAddr::Tcp(addr)))), + #[cfg(unix)] + MioListener::Uds(ref lst) => lst + .accept() + .map(|(stream, addr)| Some((MioStream::Uds(stream), SocketAddr::Uds(addr)))), } } } -impl mio::Evented for SocketListener { +impl Source for MioListener { fn register( - &self, - poll: &mio::Poll, - token: mio::Token, - interest: mio::Ready, - opts: mio::PollOpt, + &mut self, + registry: &Registry, + token: Token, + interests: Interest, ) -> io::Result<()> { match *self { - SocketListener::Tcp(ref lst) => lst.register(poll, token, interest, opts), - #[cfg(all(unix))] - SocketListener::Uds(ref lst) => lst.register(poll, token, interest, opts), + MioListener::Tcp(ref mut lst) => lst.register(registry, token, interests), + #[cfg(unix)] + MioListener::Uds(ref mut lst) => lst.register(registry, token, interests), } } fn reregister( - &self, - poll: &mio::Poll, - token: mio::Token, - interest: mio::Ready, - opts: mio::PollOpt, + &mut self, + registry: &Registry, + token: Token, + interests: Interest, ) -> io::Result<()> { match *self { - SocketListener::Tcp(ref lst) => lst.reregister(poll, token, interest, opts), - #[cfg(all(unix))] - SocketListener::Uds(ref lst) => lst.reregister(poll, token, interest, opts), + MioListener::Tcp(ref mut lst) => lst.reregister(registry, token, interests), + #[cfg(unix)] + MioListener::Uds(ref mut lst) => lst.reregister(registry, token, interests), } } - fn deregister(&self, poll: &mio::Poll) -> io::Result<()> { + + fn deregister(&mut self, registry: &Registry) -> io::Result<()> { match *self { - SocketListener::Tcp(ref lst) => lst.deregister(poll), - #[cfg(all(unix))] - SocketListener::Uds(ref lst) => { - let res = lst.deregister(poll); + MioListener::Tcp(ref mut lst) => lst.deregister(registry), + #[cfg(unix)] + MioListener::Uds(ref mut lst) => { + let res = lst.deregister(registry); // cleanup file path if let Ok(addr) = lst.local_addr() { @@ -143,28 +99,156 @@ impl mio::Evented for SocketListener { } } -pub trait FromStream: AsyncRead + AsyncWrite + Sized { - fn from_stdstream(sock: StdStream) -> io::Result; +impl From for MioListener { + fn from(lst: StdTcpListener) -> Self { + MioListener::Tcp(MioTcpListener::from_std(lst)) + } } -impl FromStream for TcpStream { - fn from_stdstream(sock: StdStream) -> io::Result { - match sock { - StdStream::Tcp(stream) => TcpStream::from_std(stream), +#[cfg(unix)] +impl From for MioListener { + fn from(lst: StdUnixListener) -> Self { + MioListener::Uds(MioUnixListener::from_std(lst)) + } +} + +impl fmt::Debug for MioListener { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + MioListener::Tcp(ref lst) => write!(f, "{:?}", lst), #[cfg(all(unix))] - StdStream::Uds(_) => { + MioListener::Uds(ref lst) => write!(f, "{:?}", lst), + } + } +} + +impl fmt::Display for MioListener { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + MioListener::Tcp(ref lst) => write!(f, "{}", lst.local_addr().ok().unwrap()), + #[cfg(unix)] + MioListener::Uds(ref lst) => write!(f, "{:?}", lst.local_addr().ok().unwrap()), + } + } +} + +pub(crate) enum SocketAddr { + Tcp(StdSocketAddr), + #[cfg(unix)] + Uds(MioSocketAddr), +} + +impl fmt::Display for SocketAddr { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + SocketAddr::Tcp(ref addr) => write!(f, "{}", addr), + #[cfg(unix)] + SocketAddr::Uds(ref addr) => write!(f, "{:?}", addr), + } + } +} + +impl fmt::Debug for SocketAddr { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + SocketAddr::Tcp(ref addr) => write!(f, "{:?}", addr), + #[cfg(unix)] + SocketAddr::Uds(ref addr) => write!(f, "{:?}", addr), + } + } +} + +#[derive(Debug)] +pub enum MioStream { + Tcp(MioTcpStream), + #[cfg(unix)] + Uds(MioUnixStream), +} + +/// helper trait for converting mio stream to tokio stream. +pub trait FromStream: Sized { + fn from_mio(sock: MioStream) -> io::Result; +} + +// FIXME: This is a workaround and we need an efficient way to convert between mio and tokio stream +#[cfg(unix)] +impl FromStream for TcpStream { + fn from_mio(sock: MioStream) -> io::Result { + match sock { + MioStream::Tcp(mio) => { + let raw = IntoRawFd::into_raw_fd(mio); + // SAFETY: This is a in place conversion from mio stream to tokio stream. + TcpStream::from_std(unsafe { FromRawFd::from_raw_fd(raw) }) + } + MioStream::Uds(_) => { panic!("Should not happen, bug in server impl"); } } } } -#[cfg(all(unix))] -impl FromStream for actix_rt::net::UnixStream { - fn from_stdstream(sock: StdStream) -> io::Result { +// FIXME: This is a workaround and we need an efficient way to convert between mio and tokio stream +#[cfg(windows)] +impl FromStream for TcpStream { + fn from_mio(sock: MioStream) -> io::Result { match sock { - StdStream::Tcp(_) => panic!("Should not happen, bug in server impl"), - StdStream::Uds(stream) => actix_rt::net::UnixStream::from_std(stream), + MioStream::Tcp(mio) => { + let raw = IntoRawSocket::into_raw_socket(mio); + // SAFETY: This is a in place conversion from mio stream to tokio stream. + TcpStream::from_std(unsafe { FromRawSocket::from_raw_socket(raw) }) + } + } + } +} + +// FIXME: This is a workaround and we need an efficient way to convert between mio and tokio stream +#[cfg(unix)] +impl FromStream for UnixStream { + fn from_mio(sock: MioStream) -> io::Result { + match sock { + MioStream::Tcp(_) => panic!("Should not happen, bug in server impl"), + MioStream::Uds(mio) => { + let raw = IntoRawFd::into_raw_fd(mio); + // SAFETY: This is a in place conversion from mio stream to tokio stream. + UnixStream::from_std(unsafe { FromRawFd::from_raw_fd(raw) }) + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn socket_addr() { + let addr = SocketAddr::Tcp("127.0.0.1:8080".parse().unwrap()); + assert!(format!("{:?}", addr).contains("127.0.0.1:8080")); + assert_eq!(format!("{}", addr), "127.0.0.1:8080"); + + let addr: StdSocketAddr = "127.0.0.1:0".parse().unwrap(); + let socket = MioTcpSocket::new_v4().unwrap(); + socket.set_reuseaddr(true).unwrap(); + socket.bind(addr).unwrap(); + let tcp = socket.listen(128).unwrap(); + let lst = MioListener::Tcp(tcp); + assert!(format!("{:?}", lst).contains("TcpListener")); + assert!(format!("{}", lst).contains("127.0.0.1")); + } + + #[test] + #[cfg(unix)] + fn uds() { + let _ = std::fs::remove_file("/tmp/sock.xxxxx"); + if let Ok(socket) = MioUnixListener::bind("/tmp/sock.xxxxx") { + let addr = socket.local_addr().expect("Couldn't get local address"); + let a = SocketAddr::Uds(addr); + assert!(format!("{:?}", a).contains("/tmp/sock.xxxxx")); + assert!(format!("{}", a).contains("/tmp/sock.xxxxx")); + + let lst = MioListener::Uds(socket); + assert!(format!("{:?}", lst).contains("/tmp/sock.xxxxx")); + assert!(format!("{}", lst).contains("/tmp/sock.xxxxx")); } } } diff --git a/actix-server/src/waker_queue.rs b/actix-server/src/waker_queue.rs new file mode 100644 index 00000000..f92363b5 --- /dev/null +++ b/actix-server/src/waker_queue.rs @@ -0,0 +1,89 @@ +use std::{ + collections::VecDeque, + ops::Deref, + sync::{Arc, Mutex, MutexGuard}, +}; + +use mio::{Registry, Token as MioToken, Waker}; + +use crate::worker::WorkerHandle; + +/// waker token for `mio::Poll` instance +pub(crate) const WAKER_TOKEN: MioToken = MioToken(usize::MAX); + +/// `mio::Waker` with a queue for waking up the `Accept`'s `Poll` and contains the `WakerInterest` +/// the `Poll` would want to look into. +pub(crate) struct WakerQueue(Arc<(Waker, Mutex>)>); + +impl Clone for WakerQueue { + fn clone(&self) -> Self { + Self(self.0.clone()) + } +} + +impl Deref for WakerQueue { + type Target = (Waker, Mutex>); + + fn deref(&self) -> &Self::Target { + self.0.deref() + } +} + +impl WakerQueue { + /// construct a waker queue with given `Poll`'s `Registry` and capacity. + /// + /// A fixed `WAKER_TOKEN` is used to identify the wake interest and the `Poll` needs to match + /// event's token for it to properly handle `WakerInterest`. + pub(crate) fn new(registry: &Registry) -> std::io::Result { + let waker = Waker::new(registry, WAKER_TOKEN)?; + let queue = Mutex::new(VecDeque::with_capacity(16)); + + Ok(Self(Arc::new((waker, queue)))) + } + + /// push a new interest to the queue and wake up the accept poll afterwards. + pub(crate) fn wake(&self, interest: WakerInterest) { + let (waker, queue) = self.deref(); + + queue + .lock() + .expect("Failed to lock WakerQueue") + .push_back(interest); + + waker + .wake() + .unwrap_or_else(|e| panic!("can not wake up Accept Poll: {}", e)); + } + + /// get a MutexGuard of the waker queue. + pub(crate) fn guard(&self) -> MutexGuard<'_, VecDeque> { + self.deref().1.lock().expect("Failed to lock WakerQueue") + } + + /// reset the waker queue so it does not grow infinitely. + pub(crate) fn reset(queue: &mut VecDeque) { + std::mem::swap(&mut VecDeque::::with_capacity(16), queue); + } +} + +/// types of interests we would look into when `Accept`'s `Poll` is waked up by waker. +/// +/// *. These interests should not be confused with `mio::Interest` and mostly not I/O related +pub(crate) enum WakerInterest { + /// `WorkerAvailable` is an interest from `Worker` notifying `Accept` there is a worker + /// available and can accept new tasks. + WorkerAvailable, + /// `Pause`, `Resume`, `Stop` Interest are from `ServerBuilder` future. It listens to + /// `ServerCommand` and notify `Accept` to do exactly these tasks. + Pause, + Resume, + Stop, + /// `Timer` is an interest sent as a delayed future. When an error happens on accepting + /// connection `Accept` would deregister socket listener temporary and wake up the poll and + /// register them again after the delayed future resolve. + Timer, + /// `Worker` is an interest happen after a worker runs into faulted state(This is determined + /// by if work can be sent to it successfully).`Accept` would be waked up and add the new + /// `WorkerHandle`. + Worker(WorkerHandle), +} diff --git a/actix-server/src/worker.rs b/actix-server/src/worker.rs index bfd11979..91e98fc2 100644 --- a/actix-server/src/worker.rs +++ b/actix-server/src/worker.rs @@ -1,22 +1,22 @@ +use std::future::Future; use std::pin::Pin; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use std::sync::Arc; use std::task::{Context, Poll}; -use std::time; +use std::time::Duration; -use actix_rt::time::{delay_until, Delay, Instant}; +use actix_rt::time::{sleep_until, Instant, Sleep}; use actix_rt::{spawn, Arbiter}; use actix_utils::counter::Counter; -use futures_channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender}; -use futures_channel::oneshot; -use futures_util::future::{join_all, LocalBoxFuture, MapOk}; -use futures_util::{future::Future, stream::Stream, FutureExt, TryFutureExt}; +use futures_core::future::LocalBoxFuture; use log::{error, info, trace}; +use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}; +use tokio::sync::oneshot; -use crate::accept::AcceptNotify; use crate::service::{BoxedServerService, InternalServiceFactory}; -use crate::socket::{SocketAddr, StdStream}; -use crate::Token; +use crate::socket::{MioStream, SocketAddr}; +use crate::waker_queue::{WakerInterest, WakerQueue}; +use crate::{join_all, Token}; pub(crate) struct WorkerCommand(Conn); @@ -29,7 +29,7 @@ pub(crate) struct StopCommand { #[derive(Debug)] pub(crate) struct Conn { - pub io: StdStream, + pub io: MioStream, pub token: Token, pub peer: Option, } @@ -46,31 +46,33 @@ pub fn max_concurrent_connections(num: usize) { MAX_CONNS.store(num, Ordering::Relaxed); } -pub(crate) fn num_connections() -> usize { - MAX_CONNS_COUNTER.with(|conns| conns.total()) -} - thread_local! { static MAX_CONNS_COUNTER: Counter = Counter::new(MAX_CONNS.load(Ordering::Relaxed)); } +pub(crate) fn num_connections() -> usize { + MAX_CONNS_COUNTER.with(|conns| conns.total()) +} + +// a handle to worker that can send message to worker and share the availability of worker to other +// thread. #[derive(Clone)] -pub(crate) struct WorkerClient { +pub(crate) struct WorkerHandle { pub idx: usize, tx1: UnboundedSender, tx2: UnboundedSender, avail: WorkerAvailability, } -impl WorkerClient { +impl WorkerHandle { pub fn new( idx: usize, tx1: UnboundedSender, tx2: UnboundedSender, avail: WorkerAvailability, ) -> Self { - WorkerClient { + WorkerHandle { idx, tx1, tx2, @@ -79,9 +81,7 @@ impl WorkerClient { } pub fn send(&self, msg: Conn) -> Result<(), Conn> { - self.tx1 - .unbounded_send(WorkerCommand(msg)) - .map_err(|msg| msg.into_inner().0) + self.tx1.send(WorkerCommand(msg)).map_err(|msg| msg.0 .0) } pub fn available(&self) -> bool { @@ -90,21 +90,21 @@ impl WorkerClient { pub fn stop(&self, graceful: bool) -> oneshot::Receiver { let (result, rx) = oneshot::channel(); - let _ = self.tx2.unbounded_send(StopCommand { graceful, result }); + let _ = self.tx2.send(StopCommand { graceful, result }); rx } } #[derive(Clone)] pub(crate) struct WorkerAvailability { - notify: AcceptNotify, + waker: WakerQueue, available: Arc, } impl WorkerAvailability { - pub fn new(notify: AcceptNotify) -> Self { + pub fn new(waker: WakerQueue) -> Self { WorkerAvailability { - notify, + waker, available: Arc::new(AtomicBool::new(false)), } } @@ -115,8 +115,9 @@ impl WorkerAvailability { pub fn set(&self, val: bool) { let old = self.available.swap(val, Ordering::Release); + // notify the accept on switched to available. if !old && val { - self.notify.notify() + self.waker.wake(WakerInterest::WorkerAvailable); } } } @@ -133,7 +134,7 @@ pub(crate) struct Worker { conns: Counter, factories: Vec>, state: WorkerState, - shutdown_timeout: time::Duration, + shutdown_timeout: Duration, } struct WorkerService { @@ -164,63 +165,65 @@ impl Worker { idx: usize, factories: Vec>, availability: WorkerAvailability, - shutdown_timeout: time::Duration, - ) -> WorkerClient { - let (tx1, rx) = unbounded(); - let (tx2, rx2) = unbounded(); + shutdown_timeout: Duration, + ) -> WorkerHandle { + let (tx1, rx) = unbounded_channel(); + let (tx2, rx2) = unbounded_channel(); let avail = availability.clone(); - Arbiter::new().send( - async move { - availability.set(false); - let mut wrk = MAX_CONNS_COUNTER.with(move |conns| Worker { - rx, - rx2, - availability, - factories, - shutdown_timeout, - services: Vec::new(), - conns: conns.clone(), - state: WorkerState::Unavailable(Vec::new()), - }); + // every worker runs in it's own arbiter. + Arbiter::new().send(Box::pin(async move { + availability.set(false); + let mut wrk = MAX_CONNS_COUNTER.with(move |conns| Worker { + rx, + rx2, + availability, + factories, + shutdown_timeout, + services: Vec::new(), + conns: conns.clone(), + state: WorkerState::Unavailable, + }); - let mut fut: Vec, _>> = Vec::new(); - for (idx, factory) in wrk.factories.iter().enumerate() { - fut.push(factory.create().map_ok(move |r| { - r.into_iter() - .map(|(t, s): (Token, _)| (idx, t, s)) - .collect::>() - })); - } + let fut = wrk + .factories + .iter() + .enumerate() + .map(|(idx, factory)| { + let fut = factory.create(); + async move { + fut.await.map(|r| { + r.into_iter().map(|(t, s)| (idx, t, s)).collect::>() + }) + } + }) + .collect::>(); - spawn(async move { - let res = join_all(fut).await; - let res: Result, _> = res.into_iter().collect(); - match res { - Ok(services) => { - for item in services { - for (factory, token, service) in item { - assert_eq!(token.0, wrk.services.len()); - wrk.services.push(WorkerService { - factory, - service, - status: WorkerServiceStatus::Unavailable, - }); - } + spawn(async move { + let res: Result, _> = join_all(fut).await.into_iter().collect(); + match res { + Ok(services) => { + for item in services { + for (factory, token, service) in item { + assert_eq!(token.0, wrk.services.len()); + wrk.services.push(WorkerService { + factory, + service, + status: WorkerServiceStatus::Unavailable, + }); } } - Err(e) => { - error!("Can not start worker: {:?}", e); - Arbiter::current().stop(); - } } - wrk.await - }); - } - .boxed(), - ); + Err(e) => { + error!("Can not start worker: {:?}", e); + Arbiter::current().stop(); + } + } + wrk.await + }); + })); - WorkerClient::new(idx, tx1, tx2, avail) + WorkerHandle::new(idx, tx1, tx2, avail) } fn shutdown(&mut self, force: bool) { @@ -242,7 +245,7 @@ impl Worker { fn check_readiness(&mut self, cx: &mut Context<'_>) -> Result { let mut ready = self.conns.available(cx); let mut failed = None; - for (idx, srv) in &mut self.services.iter_mut().enumerate() { + for (idx, srv) in self.services.iter_mut().enumerate() { if srv.status == WorkerServiceStatus::Available || srv.status == WorkerServiceStatus::Unavailable { @@ -288,16 +291,15 @@ impl Worker { enum WorkerState { Available, - Unavailable(Vec), + Unavailable, Restarting( usize, Token, - #[allow(clippy::type_complexity)] - Pin, ()>>>>, + LocalBoxFuture<'static, Result, ()>>, ), Shutdown( - Pin>, - Pin>, + Pin>, + Pin>, Option>, ), } @@ -305,12 +307,10 @@ enum WorkerState { impl Future for Worker { type Output = (); - // FIXME: remove this attribute - #[allow(clippy::never_loop)] fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { // `StopWorker` message handler if let Poll::Ready(Some(StopCommand { graceful, result })) = - Pin::new(&mut self.rx2).poll_next(cx) + Pin::new(&mut self.rx2).poll_recv(cx) { self.availability.set(false); let num = num_connections(); @@ -324,8 +324,8 @@ impl Future for Worker { if num != 0 { info!("Graceful worker shutdown, {} connections", num); self.state = WorkerState::Shutdown( - Box::pin(delay_until(Instant::now() + time::Duration::from_secs(1))), - Box::pin(delay_until(Instant::now() + self.shutdown_timeout)), + Box::pin(sleep_until(Instant::now() + Duration::from_secs(1))), + Box::pin(sleep_until(Instant::now() + self.shutdown_timeout)), Some(result), ); } else { @@ -341,53 +341,35 @@ impl Future for Worker { } match self.state { - WorkerState::Unavailable(ref mut conns) => { - let conn = conns.pop(); - match self.check_readiness(cx) { - Ok(true) => { - // process requests from wait queue - if let Some(conn) = conn { - let guard = self.conns.get(); - let _ = self.services[conn.token.0] - .service - .call((Some(guard), conn.io)); - } else { - self.state = WorkerState::Available; - self.availability.set(true); - } - self.poll(cx) - } - Ok(false) => { - // push connection back to queue - if let Some(conn) = conn { - if let WorkerState::Unavailable(ref mut conns) = self.state { - conns.push(conn); - } - } - Poll::Pending - } - Err((token, idx)) => { - trace!( - "Service {:?} failed, restarting", - self.factories[idx].name(token) - ); - self.services[token.0].status = WorkerServiceStatus::Restarting; - self.state = - WorkerState::Restarting(idx, token, self.factories[idx].create()); - self.poll(cx) - } + WorkerState::Unavailable => match self.check_readiness(cx) { + Ok(true) => { + self.state = WorkerState::Available; + self.availability.set(true); + self.poll(cx) } - } + Ok(false) => Poll::Pending, + Err((token, idx)) => { + trace!( + "Service {:?} failed, restarting", + self.factories[idx].name(token) + ); + self.services[token.0].status = WorkerServiceStatus::Restarting; + self.state = + WorkerState::Restarting(idx, token, self.factories[idx].create()); + self.poll(cx) + } + }, WorkerState::Restarting(idx, token, ref mut fut) => { - match Pin::new(fut).poll(cx) { + match fut.as_mut().poll(cx) { Poll::Ready(Ok(item)) => { - for (token, service) in item { + // only interest in the first item? + if let Some((token, service)) = item.into_iter().next() { trace!( "Service {:?} has been restarted", self.factories[idx].name(token) ); self.services[token.0].created(service); - self.state = WorkerState::Unavailable(Vec::new()); + self.state = WorkerState::Unavailable; return self.poll(cx); } } @@ -397,9 +379,7 @@ impl Future for Worker { self.factories[idx].name(token) ); } - Poll::Pending => { - return Poll::Pending; - } + Poll::Pending => return Poll::Pending, } self.poll(cx) } @@ -412,71 +392,56 @@ impl Future for Worker { } // check graceful timeout - match t2.as_mut().poll(cx) { - Poll::Pending => (), - Poll::Ready(_) => { - let _ = tx.take().unwrap().send(false); - self.shutdown(true); - Arbiter::current().stop(); - return Poll::Ready(()); - } + if Pin::new(t2).poll(cx).is_ready() { + let _ = tx.take().unwrap().send(false); + self.shutdown(true); + Arbiter::current().stop(); + return Poll::Ready(()); } // sleep for 1 second and then check again - match t1.as_mut().poll(cx) { - Poll::Pending => (), - Poll::Ready(_) => { - *t1 = Box::pin(delay_until( - Instant::now() + time::Duration::from_secs(1), - )); - let _ = t1.as_mut().poll(cx); - } + if t1.as_mut().poll(cx).is_ready() { + *t1 = Box::pin(sleep_until(Instant::now() + Duration::from_secs(1))); + let _ = t1.as_mut().poll(cx); } + Poll::Pending } - WorkerState::Available => { - loop { - match Pin::new(&mut self.rx).poll_next(cx) { - // handle incoming io stream - Poll::Ready(Some(WorkerCommand(msg))) => { - match self.check_readiness(cx) { - Ok(true) => { - let guard = self.conns.get(); - let _ = self.services[msg.token.0] - .service - .call((Some(guard), msg.io)); - continue; - } - Ok(false) => { - trace!("Worker is unavailable"); - self.availability.set(false); - self.state = WorkerState::Unavailable(vec![msg]); - } - Err((token, idx)) => { - trace!( - "Service {:?} failed, restarting", - self.factories[idx].name(token) - ); - self.availability.set(false); - self.services[token.0].status = - WorkerServiceStatus::Restarting; - self.state = WorkerState::Restarting( - idx, - token, - self.factories[idx].create(), - ); - } - } - return self.poll(cx); - } - Poll::Pending => { - self.state = WorkerState::Available; - return Poll::Pending; - } - Poll::Ready(None) => return Poll::Ready(()), + // actively poll stream and handle worker command + WorkerState::Available => loop { + match self.check_readiness(cx) { + Ok(true) => (), + Ok(false) => { + trace!("Worker is unavailable"); + self.availability.set(false); + self.state = WorkerState::Unavailable; + return self.poll(cx); + } + Err((token, idx)) => { + trace!( + "Service {:?} failed, restarting", + self.factories[idx].name(token) + ); + self.availability.set(false); + self.services[token.0].status = WorkerServiceStatus::Restarting; + self.state = + WorkerState::Restarting(idx, token, self.factories[idx].create()); + return self.poll(cx); } } - } + + match Pin::new(&mut self.rx).poll_recv(cx) { + // handle incoming io stream + Poll::Ready(Some(WorkerCommand(msg))) => { + let guard = self.conns.get(); + let _ = self.services[msg.token.0] + .service + .call((Some(guard), msg.io)); + } + Poll::Pending => return Poll::Pending, + Poll::Ready(None) => return Poll::Ready(()), + }; + }, } } } diff --git a/actix-server/tests/test_server.rs b/actix-server/tests/test_server.rs index 838c3cf1..2604df74 100644 --- a/actix-server/tests/test_server.rs +++ b/actix-server/tests/test_server.rs @@ -5,14 +5,13 @@ use std::{net, thread, time}; use actix_server::Server; use actix_service::fn_service; use futures_util::future::{lazy, ok}; -use socket2::{Domain, Protocol, Socket, Type}; fn unused_addr() -> net::SocketAddr { let addr: net::SocketAddr = "127.0.0.1:0".parse().unwrap(); - let socket = Socket::new(Domain::ipv4(), Type::stream(), Some(Protocol::tcp())).unwrap(); - socket.bind(&addr.into()).unwrap(); - socket.set_reuse_address(true).unwrap(); - let tcp = socket.into_tcp_listener(); + let socket = mio::net::TcpSocket::new_v4().unwrap(); + socket.bind(addr).unwrap(); + socket.set_reuseaddr(true).unwrap(); + let tcp = socket.listen(32).unwrap(); tcp.local_addr().unwrap() } @@ -22,8 +21,7 @@ fn test_bind() { let (tx, rx) = mpsc::channel(); let h = thread::spawn(move || { - let mut sys = actix_rt::System::new("test"); - + let sys = actix_rt::System::new("test"); let srv = sys.block_on(lazy(|_| { Server::build() .workers(1) @@ -49,17 +47,17 @@ fn test_listen() { let (tx, rx) = mpsc::channel(); let h = thread::spawn(move || { - let mut sys = actix_rt::System::new("test"); + let sys = actix_rt::System::new("test"); let lst = net::TcpListener::bind(addr).unwrap(); - sys.block_on(lazy(|_| { + sys.block_on(async { Server::build() .disable_signals() .workers(1) .listen("test", lst, move || fn_service(|_| ok::<_, ()>(()))) .unwrap() - .start() - })); - let _ = tx.send(actix_rt::System::current()); + .start(); + let _ = tx.send(actix_rt::System::current()); + }); let _ = sys.run(); }); let sys = rx.recv().unwrap(); @@ -83,7 +81,7 @@ fn test_start() { let (tx, rx) = mpsc::channel(); let h = thread::spawn(move || { - let mut sys = actix_rt::System::new("test"); + let sys = actix_rt::System::new("test"); let srv = sys.block_on(lazy(|_| { Server::build() .backlog(100) @@ -102,6 +100,7 @@ fn test_start() { let _ = tx.send((srv, actix_rt::System::current())); let _ = sys.run(); }); + let (srv, sys) = rx.recv().unwrap(); let mut buf = [1u8; 4]; @@ -151,7 +150,7 @@ fn test_configure() { let h = thread::spawn(move || { let num = num2.clone(); - let mut sys = actix_rt::System::new("test"); + let sys = actix_rt::System::new("test"); let srv = sys.block_on(lazy(|_| { Server::build() .disable_signals() diff --git a/actix-testing/Cargo.toml b/actix-testing/Cargo.toml index 430a12b6..17855a24 100644 --- a/actix-testing/Cargo.toml +++ b/actix-testing/Cargo.toml @@ -18,10 +18,10 @@ name = "actix_testing" path = "src/lib.rs" [dependencies] -actix-rt = "1.0.0" +actix-rt = "2.0.0-beta.1" actix-macros = "0.1.0" actix-server = "1.0.0" -actix-service = "1.0.0" +actix-service = "2.0.0-beta.1" log = "0.4" socket2 = "0.3" diff --git a/actix-testing/src/lib.rs b/actix-testing/src/lib.rs index eadfe6c9..57e2c223 100644 --- a/actix-testing/src/lib.rs +++ b/actix-testing/src/lib.rs @@ -83,7 +83,7 @@ impl TestServer { // run server in separate thread thread::spawn(move || { - let mut sys = System::new("actix-test-server"); + let sys = System::new("actix-test-server"); let tcp = net::TcpListener::bind("127.0.0.1:0").unwrap(); let local_addr = tcp.local_addr().unwrap(); @@ -94,9 +94,8 @@ impl TestServer { .workers(1) .disable_signals() .start(); + tx.send((System::current(), local_addr)).unwrap(); }); - - tx.send((System::current(), local_addr)).unwrap(); sys.run() }); From 98a17081b89b26b1c9eb08b5c4daa041d7a2dbc7 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Mon, 28 Dec 2020 23:50:00 +0000 Subject: [PATCH 15/73] prepare server beta release --- actix-server/CHANGES.md | 17 +++++++++++------ actix-server/Cargo.toml | 9 ++++++--- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/actix-server/CHANGES.md b/actix-server/CHANGES.md index 15d7d596..302ea576 100644 --- a/actix-server/CHANGES.md +++ b/actix-server/CHANGES.md @@ -1,17 +1,22 @@ # Changes ## Unreleased - 2020-xx-xx + + +## 2.0.0-beta.1 - 2020-12-28 * Added explicit info log message on accept queue pause. [#215] * Prevent double registration of sockets when back-pressure is resolved. [#223] -* Update `mio` dependency to `0.7.3`. -* Remove `socket2` dependency. -* `ServerBuilder::backlog` would accept `u32` instead of `i32`. -* Remove `AcceptNotify` type and pass `WakerQueue` to `Worker` for wake up the `Accept`'s `Poll`. -* Convert `mio::net::TcpStream` to `actix_rt::net::TcpStream`(`UnixStream` for uds) using `FromRawFd` and `IntoRawFd`(`FromRawSocket` and `IntoRawSocket` on windows). -* Remove `AsyncRead` and `AsyncWrite` trait bound for `socket::FromStream` trait. +* Update `mio` dependency to `0.7.3`. [#239] +* Remove `socket2` dependency. [#239] +* `ServerBuilder::backlog` now accepts `u32` instead of `i32`. [#239] +* Remove `AcceptNotify` type and pass `WakerQueue` to `Worker` to wake up `Accept`'s `Poll`. [#239] +* Convert `mio::net::TcpStream` to `actix_rt::net::TcpStream`(`UnixStream` for uds) using + `FromRawFd` and `IntoRawFd`(`FromRawSocket` and `IntoRawSocket` on windows). [#239] +* Remove `AsyncRead` and `AsyncWrite` trait bound for `socket::FromStream` trait. [#239] [#215]: https://github.com/actix/actix-net/pull/215 [#223]: https://github.com/actix/actix-net/pull/223 +[#239]: https://github.com/actix/actix-net/pull/239 ## 1.0.4 - 2020-09-12 diff --git a/actix-server/Cargo.toml b/actix-server/Cargo.toml index 34fb3775..a222f9c2 100644 --- a/actix-server/Cargo.toml +++ b/actix-server/Cargo.toml @@ -1,7 +1,10 @@ [package] name = "actix-server" -version = "1.0.4" -authors = ["Nikolay Kim "] +version = "2.0.0-beta.1" +authors = [ + "Nikolay Kim ", + "fakeshadow <24548779@qq.com>", +] description = "General purpose TCP server built for the Actix ecosystem" keywords = ["network", "framework", "async", "futures"] homepage = "https://actix.rs" @@ -35,6 +38,6 @@ tokio = { version = "1", features = ["sync"] } [dev-dependencies] actix-testing = "1.0.0" bytes = "1" -env_logger = "0.7" +env_logger = "0.8" futures-util = { version = "0.3.7", default-features = false, features = ["sink"] } tokio = { version = "1", features = ["io-util"] } From ef83647ac9936d3b6e3520e42156dcb53dd42674 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Mon, 28 Dec 2020 23:54:21 +0000 Subject: [PATCH 16/73] prepare testing beta release --- actix-server/Cargo.toml | 2 +- actix-testing/CHANGES.md | 22 +++++++++++++--------- actix-testing/Cargo.toml | 7 +++---- actix-testing/README.md | 2 +- actix-tls/Cargo.toml | 4 ++-- 5 files changed, 20 insertions(+), 17 deletions(-) diff --git a/actix-server/Cargo.toml b/actix-server/Cargo.toml index a222f9c2..c57b8ac6 100644 --- a/actix-server/Cargo.toml +++ b/actix-server/Cargo.toml @@ -36,7 +36,7 @@ slab = "0.4" tokio = { version = "1", features = ["sync"] } [dev-dependencies] -actix-testing = "1.0.0" +actix-testing = "2.0.0-beta.1" bytes = "1" env_logger = "0.8" futures-util = { version = "0.3.7", default-features = false, features = ["sink"] } diff --git a/actix-testing/CHANGES.md b/actix-testing/CHANGES.md index 86a79ed7..f23c7521 100644 --- a/actix-testing/CHANGES.md +++ b/actix-testing/CHANGES.md @@ -1,33 +1,37 @@ # Changes -## [1.0.1] - 2020-05-19 +## Unreleased - 2021-xx-xx + +## 2.0.0-beta.1 - 2020-12-28 +* Update `actix-server` to v2.0.0-beta.1. + + +## 1.0.1 - 2020-05-19 * Replace deprecated `net2` crate with `socket2` - * Remove unused `futures` dependency -## [1.0.0] - 2019-12-11 +## 1.0.0 - 2019-12-11 * Update actix-server to 1.0.0 -## [1.0.0-alpha.3] - 2019-12-07 +## 1.0.0-alpha.3 - 2019-12-07 * Migrate to tokio 0.2 -## [1.0.0-alpha.2] - 2019-12-02 +## 1.0.0-alpha.2 - 2019-12-02 * Re-export `test` attribute macros -## [0.3.0-alpha.1] - 2019-11-22 +## 0.3.0-alpha.1 - 2019-11-22 * Migrate to std::future -## [0.2.0] - 2019-10-14 +## 0.2.0 - 2019-10-14 * Upgrade actix-server and actix-server-config deps -## [0.1.0] - 2019-09-25 - +## 0.1.0 - 2019-09-25 * Initial impl diff --git a/actix-testing/Cargo.toml b/actix-testing/Cargo.toml index 17855a24..3f29679e 100644 --- a/actix-testing/Cargo.toml +++ b/actix-testing/Cargo.toml @@ -1,8 +1,8 @@ [package] name = "actix-testing" -version = "1.0.1" +version = "2.0.0-beta.1" authors = ["Nikolay Kim "] -description = "Actix testing utils" +description = "Various helpers for Actix applications to use during testing" keywords = ["network", "framework", "async", "futures"] homepage = "https://actix.rs" repository = "https://github.com/actix/actix-net.git" @@ -10,7 +10,6 @@ documentation = "https://docs.rs/actix-testing/" categories = ["network-programming", "asynchronous"] license = "MIT OR Apache-2.0" edition = "2018" -workspace = ".." readme = "README.md" [lib] @@ -20,7 +19,7 @@ path = "src/lib.rs" [dependencies] actix-rt = "2.0.0-beta.1" actix-macros = "0.1.0" -actix-server = "1.0.0" +actix-server = "2.0.0-beta.1" actix-service = "2.0.0-beta.1" log = "0.4" diff --git a/actix-testing/README.md b/actix-testing/README.md index bd4eec2f..ae54b46d 100644 --- a/actix-testing/README.md +++ b/actix-testing/README.md @@ -6,4 +6,4 @@ * [API Documentation](https://docs.rs/actix-testing/) * [Chat on gitter](https://gitter.im/actix/actix) * Cargo package: [actix-http-test](https://crates.io/crates/actix-testing) -* Minimum supported Rust version: 1.37 or later +* Minimum supported Rust version: 1.46 or later diff --git a/actix-tls/Cargo.toml b/actix-tls/Cargo.toml index 801f6144..dcd5978a 100644 --- a/actix-tls/Cargo.toml +++ b/actix-tls/Cargo.toml @@ -59,6 +59,6 @@ tokio-tls = { version = "0.3", optional = true } bytes = "0.5" log = "0.4" env_logger = "0.7" -actix-testing = "1.0.0" -actix-server = "1" +actix-testing = "2.0.0-beta.1" +actix-server = "2.0.0-beta.1" actix-rt = "1" From 3c6de3a81ba3d3923f2b03e3299cba49f5b8b06a Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Tue, 29 Dec 2020 00:08:59 +0000 Subject: [PATCH 17/73] use correct service version for tracing --- actix-tracing/Cargo.toml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/actix-tracing/Cargo.toml b/actix-tracing/Cargo.toml index d0b7bf29..720e7ad5 100644 --- a/actix-tracing/Cargo.toml +++ b/actix-tracing/Cargo.toml @@ -16,11 +16,12 @@ name = "actix_tracing" path = "src/lib.rs" [dependencies] -actix-service = "1.0.4" +actix-service = "2.0.0-beta.1" + futures-util = { version = "0.3.4", default-features = false } tracing = "0.1" tracing-futures = "0.2" [dev_dependencies] actix-rt = "1.0" -slab = "0.4" \ No newline at end of file +slab = "0.4" From 5759c9e14467c59f9dedf4b2d80bf745106dd40e Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Tue, 29 Dec 2020 00:38:41 +0000 Subject: [PATCH 18/73] merge -connect and -tls and upgrade to rt v2 (#238) --- Cargo.toml | 10 +- actix-connect/CHANGES.md | 154 ------------------ actix-connect/Cargo.toml | 58 ------- actix-connect/LICENSE-APACHE | 1 - actix-connect/LICENSE-MIT | 1 - actix-connect/tests/test_connect.rs | 127 --------------- actix-tls/CHANGES.md | 5 + actix-tls/Cargo.toml | 68 +++++--- actix-tls/examples/basic.rs | 8 +- actix-tls/src/accept/mod.rs | 42 +++++ actix-tls/src/{ => accept}/nativetls.rs | 16 +- actix-tls/src/{ => accept}/openssl.rs | 48 +++--- actix-tls/src/{ => accept}/rustls.rs | 28 +++- .../src => actix-tls/src/connect}/connect.rs | 0 .../src/connect}/connector.rs | 1 + .../src => actix-tls/src/connect}/error.rs | 0 .../lib.rs => actix-tls/src/connect/mod.rs | 10 -- .../src => actix-tls/src/connect}/resolve.rs | 7 +- .../src => actix-tls/src/connect}/service.rs | 8 +- .../src => actix-tls/src/connect}/ssl/mod.rs | 0 .../src/connect}/ssl/openssl.rs | 67 ++++---- .../src/connect}/ssl/rustls.rs | 11 +- .../src => actix-tls/src/connect}/uri.rs | 2 +- actix-tls/src/lib.rs | 47 +----- 24 files changed, 212 insertions(+), 507 deletions(-) delete mode 100644 actix-connect/CHANGES.md delete mode 100644 actix-connect/Cargo.toml delete mode 120000 actix-connect/LICENSE-APACHE delete mode 120000 actix-connect/LICENSE-MIT delete mode 100644 actix-connect/tests/test_connect.rs create mode 100644 actix-tls/src/accept/mod.rs rename actix-tls/src/{ => accept}/nativetls.rs (87%) rename actix-tls/src/{ => accept}/openssl.rs (69%) rename actix-tls/src/{ => accept}/rustls.rs (85%) rename {actix-connect/src => actix-tls/src/connect}/connect.rs (100%) rename {actix-connect/src => actix-tls/src/connect}/connector.rs (99%) rename {actix-connect/src => actix-tls/src/connect}/error.rs (100%) rename actix-connect/src/lib.rs => actix-tls/src/connect/mod.rs (93%) rename {actix-connect/src => actix-tls/src/connect}/resolve.rs (97%) rename {actix-connect/src => actix-tls/src/connect}/service.rs (97%) rename {actix-connect/src => actix-tls/src/connect}/ssl/mod.rs (100%) rename {actix-connect/src => actix-tls/src/connect}/ssl/openssl.rs (80%) rename {actix-connect/src => actix-tls/src/connect}/ssl/rustls.rs (93%) rename {actix-connect/src => actix-tls/src/connect}/uri.rs (97%) diff --git a/Cargo.toml b/Cargo.toml index d46b6283..533fbc17 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,6 @@ [workspace] members = [ "actix-codec", - "actix-connect", "actix-rt", "actix-macros", "actix-service", @@ -16,16 +15,15 @@ members = [ ] [patch.crates-io] -actix-codec = { git = "https://github.com/actix/actix-net.git", rev = "ba44ea7d0bafaf5fccb9a34003d503e1910943ee" } -actix-connect = { path = "actix-connect" } -actix-rt = { git = "https://github.com/actix/actix-net.git", rev = "ba44ea7d0bafaf5fccb9a34003d503e1910943ee" } +actix-codec = { path = "actix-codec" } +actix-rt = { path = "actix-rt" } actix-macros = { path = "actix-macros" } actix-server = { path = "actix-server" } -actix-service = { git = "https://github.com/actix/actix-net.git", rev = "ba44ea7d0bafaf5fccb9a34003d503e1910943ee" } +actix-service = { path = "actix-service" } actix-testing = { path = "actix-testing" } actix-threadpool = { path = "actix-threadpool" } actix-tls = { path = "actix-tls" } actix-tracing = { path = "actix-tracing" } -actix-utils = { git = "https://github.com/actix/actix-net.git", rev = "ba44ea7d0bafaf5fccb9a34003d503e1910943ee" } +actix-utils = { path = "actix-utils" } actix-router = { path = "router" } bytestring = { path = "string" } diff --git a/actix-connect/CHANGES.md b/actix-connect/CHANGES.md deleted file mode 100644 index 6f18b5b4..00000000 --- a/actix-connect/CHANGES.md +++ /dev/null @@ -1,154 +0,0 @@ -# Changes - -## Unreleased - 2020-xx-xx - - -## 2.0.0 - 2020-09-02 -- No significant changes from `2.0.0-alpha.4`. - -## 2.0.0-alpha.4 - 2020-08-17 - -### Changed - -* Update `rustls` dependency to 0.18 -* Update `tokio-rustls` dependency to 0.14 - - -## [2.0.0-alpha.3] - 2020-05-08 - -### Fixed - -* Corrected spelling of `ConnectError::Unresolverd` to `ConnectError::Unresolved` - -## [2.0.0-alpha.2] - 2020-03-08 - -### Changed - -* Update `trust-dns-proto` dependency to 0.19. [#116] -* Update `trust-dns-resolver` dependency to 0.19. [#116] -* `Address` trait is now required to have static lifetime. [#116] -* `start_resolver` and `start_default_resolver` are now `async` and may return a `ConnectError`. [#116] - -[#116]: https://github.com/actix/actix-net/pull/116 - -## [2.0.0-alpha.1] - 2020-03-03 - -### Changed - -* Update `rustls` dependency to 0.17 -* Update `tokio-rustls` dependency to 0.13 - -## [1.0.2] - 2020-01-15 - -* Fix actix-service 1.0.3 compatibility - -## [1.0.1] - 2019-12-15 - -* Fix trust-dns-resolver compilation - -## [1.0.0] - 2019-12-11 - -* Release - -## [1.0.0-alpha.3] - 2019-12-07 - -### Changed - -* Migrate to tokio 0.2 - - -## [1.0.0-alpha.2] - 2019-12-02 - -### Changed - -* Migrated to `std::future` - - -## [0.3.0] - 2019-10-03 - -### Changed - -* Update `rustls` to 0.16 -* Minimum required Rust version upped to 1.37.0 - -## [0.2.5] - 2019-09-05 - -* Add `TcpConnectService` - -## [0.2.4] - 2019-09-02 - -* Use arbiter's storage for default async resolver - -## [0.2.3] - 2019-08-05 - -* Add `ConnectService` and `OpensslConnectService` - -## [0.2.2] - 2019-07-24 - -* Add `rustls` support - -## [0.2.1] - 2019-07-17 - -### Added - -* Expose Connect addrs #30 - -### Changed - -* Update `derive_more` to 0.15 - - -## [0.2.0] - 2019-05-12 - -### Changed - -* Upgrade to actix-service 0.4 - - -## [0.1.5] - 2019-04-19 - -### Added - -* `Connect::set_addr()` - -### Changed - -* Use trust-dns-resolver 0.11.0 - - -## [0.1.4] - 2019-04-12 - -### Changed - -* Do not start default resolver immediately for default connector. - - -## [0.1.3] - 2019-04-11 - -### Changed - -* Start trust-dns default resolver on first use - -## [0.1.2] - 2019-04-04 - -### Added - -* Log error if dns system config could not be loaded. - -### Changed - -* Rename connect Connector to TcpConnector #10 - - -## [0.1.1] - 2019-03-15 - -### Fixed - -* Fix error handling for single address - - -## [0.1.0] - 2019-03-14 - -* Refactor resolver and connector services - -* Rename crate diff --git a/actix-connect/Cargo.toml b/actix-connect/Cargo.toml deleted file mode 100644 index 233195c5..00000000 --- a/actix-connect/Cargo.toml +++ /dev/null @@ -1,58 +0,0 @@ -[package] -name = "actix-connect" -version = "2.0.0" -authors = ["Nikolay Kim "] -description = "TCP connector service for Actix ecosystem." -keywords = ["network", "framework", "async", "futures"] -homepage = "https://actix.rs" -repository = "https://github.com/actix/actix-net.git" -documentation = "https://docs.rs/actix-connect/" -categories = ["network-programming", "asynchronous"] -license = "MIT OR Apache-2.0" -edition = "2018" - -[package.metadata.docs.rs] -features = ["openssl", "rustls", "uri"] - -[lib] -name = "actix_connect" -path = "src/lib.rs" - -[features] -default = ["uri"] - -# openssl -openssl = ["open-ssl", "tokio-openssl"] - -# rustls -rustls = ["rust-tls", "tokio-rustls", "webpki"] - -# support http::Uri as connect address -uri = ["http"] - -[dependencies] -actix-service = "1.0.6" -actix-codec = "0.3.0" -actix-utils = "2.0.0" -actix-rt = "1.1.1" - -derive_more = "0.99.2" -either = "1.5.3" -futures-util = { version = "0.3.4", default-features = false } -http = { version = "0.2.0", optional = true } -log = "0.4" -trust-dns-proto = { version = "0.19", default-features = false, features = ["tokio-runtime"] } -trust-dns-resolver = { version = "0.19", default-features = false, features = ["tokio-runtime", "system-config"] } - -# openssl -open-ssl = { package = "openssl", version = "0.10", optional = true } -tokio-openssl = { version = "0.4.0", optional = true } - -# rustls -rust-tls = { package = "rustls", version = "0.18.0", optional = true } -tokio-rustls = { version = "0.14.0", optional = true } -webpki = { version = "0.21", optional = true } - -[dev-dependencies] -bytes = "0.5.3" -actix-testing = "1.0.0" diff --git a/actix-connect/LICENSE-APACHE b/actix-connect/LICENSE-APACHE deleted file mode 120000 index 965b606f..00000000 --- a/actix-connect/LICENSE-APACHE +++ /dev/null @@ -1 +0,0 @@ -../LICENSE-APACHE \ No newline at end of file diff --git a/actix-connect/LICENSE-MIT b/actix-connect/LICENSE-MIT deleted file mode 120000 index 76219eb7..00000000 --- a/actix-connect/LICENSE-MIT +++ /dev/null @@ -1 +0,0 @@ -../LICENSE-MIT \ No newline at end of file diff --git a/actix-connect/tests/test_connect.rs b/actix-connect/tests/test_connect.rs deleted file mode 100644 index 21d78d2c..00000000 --- a/actix-connect/tests/test_connect.rs +++ /dev/null @@ -1,127 +0,0 @@ -use std::io; - -use actix_codec::{BytesCodec, Framed}; -use actix_rt::net::TcpStream; -use actix_service::{fn_service, Service, ServiceFactory}; -use actix_testing::TestServer; -use bytes::Bytes; -use futures_util::sink::SinkExt; - -use actix_connect::resolver::{ResolverConfig, ResolverOpts}; -use actix_connect::Connect; - -#[cfg(feature = "openssl")] -#[actix_rt::test] -async fn test_string() { - let srv = TestServer::with(|| { - fn_service(|io: TcpStream| async { - let mut framed = Framed::new(io, BytesCodec); - framed.send(Bytes::from_static(b"test")).await?; - Ok::<_, io::Error>(()) - }) - }); - - let mut conn = actix_connect::default_connector(); - let addr = format!("localhost:{}", srv.port()); - let con = conn.call(addr.into()).await.unwrap(); - assert_eq!(con.peer_addr().unwrap(), srv.addr()); -} - -#[cfg(feature = "rustls")] -#[actix_rt::test] -async fn test_rustls_string() { - let srv = TestServer::with(|| { - fn_service(|io: TcpStream| async { - let mut framed = Framed::new(io, BytesCodec); - framed.send(Bytes::from_static(b"test")).await?; - Ok::<_, io::Error>(()) - }) - }); - - let mut conn = actix_connect::default_connector(); - let addr = format!("localhost:{}", srv.port()); - let con = conn.call(addr.into()).await.unwrap(); - assert_eq!(con.peer_addr().unwrap(), srv.addr()); -} - -#[actix_rt::test] -async fn test_static_str() { - let srv = TestServer::with(|| { - fn_service(|io: TcpStream| async { - let mut framed = Framed::new(io, BytesCodec); - framed.send(Bytes::from_static(b"test")).await?; - Ok::<_, io::Error>(()) - }) - }); - - let resolver = actix_connect::start_default_resolver().await.unwrap(); - let mut conn = actix_connect::new_connector(resolver.clone()); - - let con = conn.call(Connect::with("10", srv.addr())).await.unwrap(); - assert_eq!(con.peer_addr().unwrap(), srv.addr()); - - let connect = Connect::new(srv.host().to_owned()); - let mut conn = actix_connect::new_connector(resolver); - let con = conn.call(connect).await; - assert!(con.is_err()); -} - -#[actix_rt::test] -async fn test_new_service() { - let srv = TestServer::with(|| { - fn_service(|io: TcpStream| async { - let mut framed = Framed::new(io, BytesCodec); - framed.send(Bytes::from_static(b"test")).await?; - Ok::<_, io::Error>(()) - }) - }); - - let resolver = - actix_connect::start_resolver(ResolverConfig::default(), ResolverOpts::default()) - .await - .unwrap(); - - let factory = actix_connect::new_connector_factory(resolver); - - let mut conn = factory.new_service(()).await.unwrap(); - let con = conn.call(Connect::with("10", srv.addr())).await.unwrap(); - assert_eq!(con.peer_addr().unwrap(), srv.addr()); -} - -#[cfg(all(feature = "openssl", feature = "uri"))] -#[actix_rt::test] -async fn test_openssl_uri() { - use std::convert::TryFrom; - - let srv = TestServer::with(|| { - fn_service(|io: TcpStream| async { - let mut framed = Framed::new(io, BytesCodec); - framed.send(Bytes::from_static(b"test")).await?; - Ok::<_, io::Error>(()) - }) - }); - - let mut conn = actix_connect::default_connector(); - let addr = http::Uri::try_from(format!("https://localhost:{}", srv.port())).unwrap(); - let con = conn.call(addr.into()).await.unwrap(); - assert_eq!(con.peer_addr().unwrap(), srv.addr()); -} - -#[cfg(all(feature = "rustls", feature = "uri"))] -#[actix_rt::test] -async fn test_rustls_uri() { - use std::convert::TryFrom; - - let srv = TestServer::with(|| { - fn_service(|io: TcpStream| async { - let mut framed = Framed::new(io, BytesCodec); - framed.send(Bytes::from_static(b"test")).await?; - Ok::<_, io::Error>(()) - }) - }); - - let mut conn = actix_connect::default_connector(); - let addr = http::Uri::try_from(format!("https://localhost:{}", srv.port())).unwrap(); - let con = conn.call(addr.into()).await.unwrap(); - assert_eq!(con.peer_addr().unwrap(), srv.addr()); -} diff --git a/actix-tls/CHANGES.md b/actix-tls/CHANGES.md index 592b96b0..9f03bfeb 100644 --- a/actix-tls/CHANGES.md +++ b/actix-tls/CHANGES.md @@ -1,6 +1,11 @@ # Changes ## Unreleased - 2020-xx-xx +* Move acceptors under `accept` module. [#238] +* Merge `actix-connect` crate under `connect` module. [#238] +* Add feature flags to enable acceptors and/or connectors individually. + +[#238]: https://github.com/actix/actix-net/pull/238 ## 2.0.0 - 2020-09-03 diff --git a/actix-tls/Cargo.toml b/actix-tls/Cargo.toml index dcd5978a..de2d859f 100644 --- a/actix-tls/Cargo.toml +++ b/actix-tls/Cargo.toml @@ -2,8 +2,8 @@ name = "actix-tls" version = "2.0.0" authors = ["Nikolay Kim "] -description = "TLS acceptor services for Actix ecosystem." -keywords = ["network", "framework", "async", "tls", "ssl"] +description = "TLS acceptor and connector services for Actix ecosystem" +keywords = ["network", "tls", "ssl", "async", "transport"] homepage = "https://actix.rs" repository = "https://github.com/actix/actix-net.git" documentation = "https://docs.rs/actix-tls/" @@ -12,7 +12,7 @@ license = "MIT OR Apache-2.0" edition = "2018" [package.metadata.docs.rs] -features = ["openssl", "rustls", "nativetls"] +features = ["openssl", "rustls", "native-tls", "accept", "connect", "http"] [lib] name = "actix_tls" @@ -20,45 +20,59 @@ path = "src/lib.rs" [[example]] name = "basic" -required-features = ["rustls"] +required-features = ["accept", "rustls"] [features] -default = [] +default = ["accept", "connect", "http"] -# openssl -openssl = ["open-ssl", "tokio-openssl"] +# enable acceptor services +accept = [] -# rustls -rustls = ["rust-tls", "webpki", "webpki-roots", "tokio-rustls"] +# enable connector services +connect = [] -# nativetls -nativetls = ["native-tls", "tokio-tls"] +# use openssl impls +openssl = ["tls-openssl", "tokio-openssl"] + +# use rustls impls +rustls = ["tls-rustls", "webpki", "webpki-roots", "tokio-rustls"] + +# use native-tls impls +native-tls = ["tls-native-tls", "tokio-native-tls"] [dependencies] -actix-service = "1.0.0" -actix-codec = "0.3.0" -actix-utils = "2.0.0" +actix-codec = "0.4.0-beta.1" +actix-rt = "2.0.0-beta.1" +actix-service = "2.0.0-beta.1" +actix-utils = "3.0.0-beta.1" -futures-util = { version = "0.3.4", default-features = false } +derive_more = "0.99.5" +either = "1.6" +futures-util = { version = "0.3.7", default-features = false } +http = { version = "0.2.0", optional = true } +log = "0.4" +trust-dns-proto = { version = "0.19", default-features = false, features = ["tokio-runtime"] } +trust-dns-resolver = { version = "0.19", default-features = false, features = ["tokio-runtime", "system-config"] } # openssl -open-ssl = { package = "openssl", version = "0.10", optional = true } -tokio-openssl = { version = "0.4.0", optional = true } +tls-openssl = { package = "openssl", version = "0.10", optional = true } +tokio-openssl = { version = "0.6", optional = true } + +# TODO: Reduce dependencies where tokio wrappers re-export base crate. # rustls -rust-tls = { package = "rustls", version = "0.18.0", optional = true } +tls-rustls = { package = "rustls", version = "0.19", optional = true } +tokio-rustls = { version = "0.22", optional = true } webpki = { version = "0.21", optional = true } -webpki-roots = { version = "0.20", optional = true } -tokio-rustls = { version = "0.14.0", optional = true } +webpki-roots = { version = "0.21", optional = true } # native-tls -native-tls = { version = "0.2", optional = true } -tokio-tls = { version = "0.3", optional = true } +tls-native-tls = { package = "native-tls", version = "0.2", optional = true } +tokio-native-tls = { version = "0.3", optional = true } [dev-dependencies] -bytes = "0.5" -log = "0.4" -env_logger = "0.7" -actix-testing = "2.0.0-beta.1" actix-server = "2.0.0-beta.1" -actix-rt = "1" +actix-testing = "2.0.0-beta.1" +bytes = "1" +log = "0.4" +env_logger = "0.8" diff --git a/actix-tls/examples/basic.rs b/actix-tls/examples/basic.rs index cd706958..3f4cdb24 100644 --- a/actix-tls/examples/basic.rs +++ b/actix-tls/examples/basic.rs @@ -15,6 +15,10 @@ //! http --verify=false https://127.0.0.1:8443 //! ``` +// this rename only exists because of how we have organised the crate's feature flags +// it is not necessary for your actual code +extern crate tls_rustls as rustls; + use std::{ env, fs::File, @@ -27,10 +31,10 @@ use std::{ use actix_server::Server; use actix_service::pipeline_factory; -use actix_tls::rustls::Acceptor as RustlsAcceptor; +use actix_tls::accept::rustls::Acceptor as RustlsAcceptor; use futures_util::future::ok; use log::info; -use rust_tls::{ +use rustls::{ internal::pemfile::certs, internal::pemfile::rsa_private_keys, NoClientAuth, ServerConfig, }; diff --git a/actix-tls/src/accept/mod.rs b/actix-tls/src/accept/mod.rs new file mode 100644 index 00000000..8b1fe47c --- /dev/null +++ b/actix-tls/src/accept/mod.rs @@ -0,0 +1,42 @@ +//! TLS acceptor services for Actix ecosystem. +//! +//! ## Crate Features +//! * `openssl` - TLS acceptor using the `openssl` crate. +//! * `rustls` - TLS acceptor using the `rustls` crate. +//! * `native-tls` - TLS acceptor using the `native-tls` crate. + +use std::sync::atomic::{AtomicUsize, Ordering}; + +use actix_utils::counter::Counter; + +#[cfg(feature = "openssl")] +pub mod openssl; + +#[cfg(feature = "rustls")] +pub mod rustls; + +#[cfg(feature = "native-tls")] +pub mod nativetls; + +pub(crate) static MAX_CONN: AtomicUsize = AtomicUsize::new(256); + +thread_local! { + static MAX_CONN_COUNTER: Counter = Counter::new(MAX_CONN.load(Ordering::Relaxed)); +} + +/// Sets the maximum per-worker concurrent TLS connection limit. +/// +/// All listeners will stop accepting connections when this limit is reached. +/// It can be used to regulate the global TLS CPU usage. +/// +/// By default, the connection limit is 256. +pub fn max_concurrent_tls_connect(num: usize) { + MAX_CONN.store(num, Ordering::Relaxed); +} + +/// TLS error combined with service error. +#[derive(Debug)] +pub enum TlsError { + Tls(E1), + Service(E2), +} diff --git a/actix-tls/src/nativetls.rs b/actix-tls/src/accept/nativetls.rs similarity index 87% rename from actix-tls/src/nativetls.rs rename to actix-tls/src/accept/nativetls.rs index fa66bff6..12d23855 100644 --- a/actix-tls/src/nativetls.rs +++ b/actix-tls/src/accept/nativetls.rs @@ -7,13 +7,13 @@ use actix_utils::counter::Counter; use futures_util::future::{self, FutureExt, LocalBoxFuture, TryFutureExt}; pub use native_tls::Error; -pub use tokio_tls::{TlsAcceptor, TlsStream}; +pub use tokio_native_tls::{TlsAcceptor, TlsStream}; -use crate::MAX_CONN_COUNTER; +use super::MAX_CONN_COUNTER; /// Accept TLS connections via `native-tls` package. /// -/// `nativetls` feature enables this `Acceptor` type. +/// `native-tls` feature enables this `Acceptor` type. pub struct Acceptor { acceptor: TlsAcceptor, io: PhantomData, @@ -43,11 +43,10 @@ impl Clone for Acceptor { } } -impl ServiceFactory for Acceptor +impl ServiceFactory for Acceptor where T: AsyncRead + AsyncWrite + Unpin + 'static, { - type Request = T; type Response = TlsStream; type Error = Error; type Service = NativeTlsAcceptorService; @@ -83,11 +82,10 @@ impl Clone for NativeTlsAcceptorService { } } -impl Service for NativeTlsAcceptorService +impl Service for NativeTlsAcceptorService where T: AsyncRead + AsyncWrite + Unpin + 'static, { - type Request = T; type Response = TlsStream; type Error = Error; type Future = LocalBoxFuture<'static, Result, Error>>; @@ -100,10 +98,10 @@ where } } - fn call(&mut self, req: Self::Request) -> Self::Future { + fn call(&mut self, io: T) -> Self::Future { let guard = self.conns.get(); let this = self.clone(); - async move { this.acceptor.accept(req).await } + async move { this.acceptor.accept(io).await } .map_ok(move |io| { // Required to preserve `CounterGuard` until `Self::Future` is completely resolved. let _ = guard; diff --git a/actix-tls/src/openssl.rs b/actix-tls/src/accept/openssl.rs similarity index 69% rename from actix-tls/src/openssl.rs rename to actix-tls/src/accept/openssl.rs index 46dc10de..44877b24 100644 --- a/actix-tls/src/openssl.rs +++ b/actix-tls/src/accept/openssl.rs @@ -6,12 +6,17 @@ use std::task::{Context, Poll}; use actix_codec::{AsyncRead, AsyncWrite}; use actix_service::{Service, ServiceFactory}; use actix_utils::counter::{Counter, CounterGuard}; -use futures_util::future::{ok, FutureExt, LocalBoxFuture, Ready}; +use futures_util::{ + future::{ok, Ready}, + ready, +}; -pub use open_ssl::ssl::{AlpnError, SslAcceptor, SslAcceptorBuilder}; -pub use tokio_openssl::{HandshakeError, SslStream}; +pub use openssl::ssl::{ + AlpnError, Error as SslError, HandshakeError, Ssl, SslAcceptor, SslAcceptorBuilder, +}; +pub use tokio_openssl::SslStream; -use crate::MAX_CONN_COUNTER; +use super::MAX_CONN_COUNTER; /// Accept TLS connections via `openssl` package. /// @@ -42,10 +47,12 @@ impl Clone for Acceptor { } } -impl ServiceFactory for Acceptor { - type Request = T; +impl ServiceFactory for Acceptor +where + T: AsyncRead + AsyncWrite + Unpin + 'static, +{ type Response = SslStream; - type Error = HandshakeError; + type Error = SslError; type Config = (); type Service = AcceptorService; type InitError = (); @@ -68,10 +75,12 @@ pub struct AcceptorService { io: PhantomData, } -impl Service for AcceptorService { - type Request = T; +impl Service for AcceptorService +where + T: AsyncRead + AsyncWrite + Unpin + 'static, +{ type Response = SslStream; - type Error = HandshakeError; + type Error = SslError; type Future = AcceptorServiceResponse; fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll> { @@ -82,15 +91,14 @@ impl Service for AcceptorService } } - fn call(&mut self, req: Self::Request) -> Self::Future { + fn call(&mut self, io: T) -> Self::Future { let acc = self.acceptor.clone(); + let ssl_ctx = acc.into_context(); + let ssl = Ssl::new(&ssl_ctx).expect("Provided SSL acceptor was invalid."); + AcceptorServiceResponse { _guard: self.conns.get(), - fut: async move { - let acc = acc; - tokio_openssl::accept(&acc, req).await - } - .boxed_local(), + stream: Some(SslStream::new(ssl, io).unwrap()), } } } @@ -99,15 +107,15 @@ pub struct AcceptorServiceResponse where T: AsyncRead + AsyncWrite, { - fut: LocalBoxFuture<'static, Result, HandshakeError>>, + stream: Option>, _guard: CounterGuard, } impl Future for AcceptorServiceResponse { - type Output = Result, HandshakeError>; + type Output = Result, SslError>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let io = futures_util::ready!(Pin::new(&mut self.fut).poll(cx))?; - Poll::Ready(Ok(io)) + ready!(Pin::new(self.stream.as_mut().unwrap()).poll_accept(cx))?; + Poll::Ready(Ok(self.stream.take().expect("SSL connect has resolved."))) } } diff --git a/actix-tls/src/rustls.rs b/actix-tls/src/accept/rustls.rs similarity index 85% rename from actix-tls/src/rustls.rs rename to actix-tls/src/accept/rustls.rs index 3c3c60bf..1c40757e 100644 --- a/actix-tls/src/rustls.rs +++ b/actix-tls/src/accept/rustls.rs @@ -11,11 +11,11 @@ use actix_utils::counter::{Counter, CounterGuard}; use futures_util::future::{ok, Ready}; use tokio_rustls::{Accept, TlsAcceptor}; -pub use rust_tls::{ServerConfig, Session}; +pub use rustls::{ServerConfig, Session}; pub use tokio_rustls::server::TlsStream; pub use webpki_roots::TLS_SERVER_ROOTS; -use crate::MAX_CONN_COUNTER; +use super::MAX_CONN_COUNTER; /// Accept TLS connections via `rustls` package. /// @@ -25,7 +25,10 @@ pub struct Acceptor { io: PhantomData, } -impl Acceptor { +impl Acceptor +where + T: AsyncRead + AsyncWrite, +{ /// Create Rustls based `Acceptor` service factory. #[inline] pub fn new(config: ServerConfig) -> Self { @@ -46,8 +49,10 @@ impl Clone for Acceptor { } } -impl ServiceFactory for Acceptor { - type Request = T; +impl ServiceFactory for Acceptor +where + T: AsyncRead + AsyncWrite + Unpin, +{ type Response = TlsStream; type Error = io::Error; type Service = AcceptorService; @@ -74,8 +79,10 @@ pub struct AcceptorService { conns: Counter, } -impl Service for AcceptorService { - type Request = T; +impl Service for AcceptorService +where + T: AsyncRead + AsyncWrite + Unpin, +{ type Response = TlsStream; type Error = io::Error; type Future = AcceptorServiceFut; @@ -88,7 +95,7 @@ impl Service for AcceptorService { } } - fn call(&mut self, req: Self::Request) -> Self::Future { + fn call(&mut self, req: T) -> Self::Future { AcceptorServiceFut { _guard: self.conns.get(), fut: self.acceptor.accept(req), @@ -104,7 +111,10 @@ where _guard: CounterGuard, } -impl Future for AcceptorServiceFut { +impl Future for AcceptorServiceFut +where + T: AsyncRead + AsyncWrite + Unpin, +{ type Output = Result, io::Error>; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { diff --git a/actix-connect/src/connect.rs b/actix-tls/src/connect/connect.rs similarity index 100% rename from actix-connect/src/connect.rs rename to actix-tls/src/connect/connect.rs diff --git a/actix-connect/src/connector.rs b/actix-tls/src/connect/connector.rs similarity index 99% rename from actix-connect/src/connector.rs rename to actix-tls/src/connect/connector.rs index d3ef9813..c7a31e0a 100644 --- a/actix-connect/src/connector.rs +++ b/actix-tls/src/connect/connector.rs @@ -9,6 +9,7 @@ use std::task::{Context, Poll}; use actix_rt::net::TcpStream; use actix_service::{Service, ServiceFactory}; use futures_util::future::{err, ok, BoxFuture, Either, FutureExt, Ready}; +use log::{error, trace}; use super::connect::{Address, Connect, Connection}; use super::error::ConnectError; diff --git a/actix-connect/src/error.rs b/actix-tls/src/connect/error.rs similarity index 100% rename from actix-connect/src/error.rs rename to actix-tls/src/connect/error.rs diff --git a/actix-connect/src/lib.rs b/actix-tls/src/connect/mod.rs similarity index 93% rename from actix-connect/src/lib.rs rename to actix-tls/src/connect/mod.rs index d2111d41..753cfc33 100644 --- a/actix-connect/src/lib.rs +++ b/actix-tls/src/connect/mod.rs @@ -5,22 +5,12 @@ //! * `openssl` - enables TLS support via `openssl` crate //! * `rustls` - enables TLS support via `rustls` crate -#![deny(rust_2018_idioms, nonstandard_style)] -#![recursion_limit = "128"] -#![doc(html_logo_url = "https://actix.rs/img/logo.png")] -#![doc(html_favicon_url = "https://actix.rs/favicon.ico")] - -#[macro_use] -extern crate log; - mod connect; mod connector; mod error; mod resolve; mod service; pub mod ssl; - -#[cfg(feature = "uri")] mod uri; use actix_rt::{net::TcpStream, Arbiter}; diff --git a/actix-connect/src/resolve.rs b/actix-tls/src/connect/resolve.rs similarity index 97% rename from actix-connect/src/resolve.rs rename to actix-tls/src/connect/resolve.rs index 2c75cc0d..61535faa 100644 --- a/actix-connect/src/resolve.rs +++ b/actix-tls/src/connect/resolve.rs @@ -6,12 +6,13 @@ use std::task::{Context, Poll}; use actix_service::{Service, ServiceFactory}; use futures_util::future::{ok, Either, Ready}; +use log::trace; use trust_dns_resolver::TokioAsyncResolver as AsyncResolver; use trust_dns_resolver::{error::ResolveError, lookup_ip::LookupIp}; -use crate::connect::{Address, Connect}; -use crate::error::ConnectError; -use crate::get_default_resolver; +use super::connect::{Address, Connect}; +use super::error::ConnectError; +use super::get_default_resolver; /// DNS Resolver Service factory pub struct ResolverFactory { diff --git a/actix-connect/src/service.rs b/actix-tls/src/connect/service.rs similarity index 97% rename from actix-connect/src/service.rs rename to actix-tls/src/connect/service.rs index b942d230..59fe20cc 100644 --- a/actix-connect/src/service.rs +++ b/actix-tls/src/connect/service.rs @@ -8,10 +8,10 @@ use either::Either; use futures_util::future::{ok, Ready}; use trust_dns_resolver::TokioAsyncResolver as AsyncResolver; -use crate::connect::{Address, Connect, Connection}; -use crate::connector::{TcpConnector, TcpConnectorFactory}; -use crate::error::ConnectError; -use crate::resolve::{Resolver, ResolverFactory}; +use super::connect::{Address, Connect, Connection}; +use super::connector::{TcpConnector, TcpConnectorFactory}; +use super::error::ConnectError; +use super::resolve::{Resolver, ResolverFactory}; pub struct ConnectServiceFactory { tcp: TcpConnectorFactory, diff --git a/actix-connect/src/ssl/mod.rs b/actix-tls/src/connect/ssl/mod.rs similarity index 100% rename from actix-connect/src/ssl/mod.rs rename to actix-tls/src/connect/ssl/mod.rs diff --git a/actix-connect/src/ssl/openssl.rs b/actix-tls/src/connect/ssl/openssl.rs similarity index 80% rename from actix-connect/src/ssl/openssl.rs rename to actix-tls/src/connect/ssl/openssl.rs index a9bcc3c7..f249717d 100644 --- a/actix-connect/src/ssl/openssl.rs +++ b/actix-tls/src/connect/ssl/openssl.rs @@ -4,16 +4,19 @@ use std::pin::Pin; use std::task::{Context, Poll}; use std::{fmt, io}; -pub use open_ssl::ssl::{Error as SslError, SslConnector, SslMethod}; -pub use tokio_openssl::{HandshakeError, SslStream}; - use actix_codec::{AsyncRead, AsyncWrite}; use actix_rt::net::TcpStream; use actix_service::{Service, ServiceFactory}; -use futures_util::future::{err, ok, Either, FutureExt, LocalBoxFuture, Ready}; +use futures_util::{ + future::{err, ok, Either, Ready}, + ready, +}; +use log::trace; +pub use openssl::ssl::{Error as SslError, HandshakeError, SslConnector, SslMethod}; +pub use tokio_openssl::SslStream; use trust_dns_resolver::TokioAsyncResolver as AsyncResolver; -use crate::{ +use crate::connect::{ Address, Connect, ConnectError, ConnectService, ConnectServiceFactory, Connection, }; @@ -54,12 +57,11 @@ impl Clone for OpensslConnector { } } -impl ServiceFactory for OpensslConnector +impl ServiceFactory> for OpensslConnector where T: Address + 'static, U: AsyncRead + AsyncWrite + Unpin + fmt::Debug + 'static, { - type Request = Connection; type Response = Connection>; type Error = io::Error; type Config = (); @@ -89,12 +91,11 @@ impl Clone for OpensslConnectorService { } } -impl Service for OpensslConnectorService +impl Service> for OpensslConnectorService where T: Address + 'static, U: AsyncRead + AsyncWrite + Unpin + fmt::Debug + 'static, { - type Request = Connection; type Response = Connection>; type Error = io::Error; #[allow(clippy::type_complexity)] @@ -109,18 +110,23 @@ where match self.connector.configure() { Err(e) => Either::Right(err(io::Error::new(io::ErrorKind::Other, e))), - Ok(config) => Either::Left(ConnectAsyncExt { - fut: async move { tokio_openssl::connect(config, &host, io).await } - .boxed_local(), - stream: Some(stream), - _t: PhantomData, - }), + Ok(config) => { + let ssl = config + .into_ssl(&host) + .expect("SSL connect configuration was invalid."); + + Either::Left(ConnectAsyncExt { + io: Some(SslStream::new(ssl, io).unwrap()), + stream: Some(stream), + _t: PhantomData, + }) + } } } } pub struct ConnectAsyncExt { - fut: LocalBoxFuture<'static, Result, HandshakeError>>, + io: Option>, stream: Option>, _t: PhantomData, } @@ -134,17 +140,16 @@ where fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let this = self.get_mut(); - match Pin::new(&mut this.fut).poll(cx) { - Poll::Ready(Ok(stream)) => { - let s = this.stream.take().unwrap(); - trace!("SSL Handshake success: {:?}", s.host()); - Poll::Ready(Ok(s.replace(stream).1)) + match ready!(Pin::new(this.io.as_mut().unwrap()).poll_connect(cx)) { + Ok(_) => { + let stream = this.stream.take().unwrap(); + trace!("SSL Handshake success: {:?}", stream.host()); + Poll::Ready(Ok(stream.replace(this.io.take().unwrap()).1)) } - Poll::Ready(Err(e)) => { + Err(e) => { trace!("SSL Handshake error: {:?}", e); Poll::Ready(Err(io::Error::new(io::ErrorKind::Other, format!("{}", e)))) } - Poll::Pending => Poll::Pending, } } } @@ -192,8 +197,7 @@ impl Clone for OpensslConnectServiceFactory { } } -impl ServiceFactory for OpensslConnectServiceFactory { - type Request = Connect; +impl ServiceFactory> for OpensslConnectServiceFactory { type Response = SslStream; type Error = ConnectError; type Config = (); @@ -212,8 +216,7 @@ pub struct OpensslConnectService { openssl: OpensslConnectorService, } -impl Service for OpensslConnectService { - type Request = Connect; +impl Service> for OpensslConnectService { type Response = SslStream; type Error = ConnectError; type Future = OpensslConnectServiceResponse; @@ -230,8 +233,10 @@ impl Service for OpensslConnectService { } pub struct OpensslConnectServiceResponse { - fut1: Option< as Service>::Future>, - fut2: Option< as Service>::Future>, + fut1: Option< as Service>>::Future>, + fut2: Option< + as Service>>::Future, + >, openssl: OpensslConnectorService, } @@ -240,7 +245,7 @@ impl Future for OpensslConnectServiceResponse { fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { if let Some(ref mut fut) = self.fut1 { - match futures_util::ready!(Pin::new(fut).poll(cx)) { + match ready!(Pin::new(fut).poll(cx)) { Ok(res) => { let _ = self.fut1.take(); self.fut2 = Some(self.openssl.call(res)); @@ -250,7 +255,7 @@ impl Future for OpensslConnectServiceResponse { } if let Some(ref mut fut) = self.fut2 { - match futures_util::ready!(Pin::new(fut).poll(cx)) { + match ready!(Pin::new(fut).poll(cx)) { Ok(connect) => Poll::Ready(Ok(connect.into_parts().0)), Err(e) => Poll::Ready(Err(ConnectError::Io(io::Error::new( io::ErrorKind::Other, diff --git a/actix-connect/src/ssl/rustls.rs b/actix-tls/src/connect/ssl/rustls.rs similarity index 93% rename from actix-connect/src/ssl/rustls.rs rename to actix-tls/src/connect/ssl/rustls.rs index 984fbe49..f5259280 100644 --- a/actix-connect/src/ssl/rustls.rs +++ b/actix-tls/src/connect/ssl/rustls.rs @@ -5,16 +5,17 @@ use std::pin::Pin; use std::sync::Arc; use std::task::{Context, Poll}; -pub use rust_tls::Session; +pub use rustls::Session; pub use tokio_rustls::{client::TlsStream, rustls::ClientConfig}; use actix_codec::{AsyncRead, AsyncWrite}; use actix_service::{Service, ServiceFactory}; use futures_util::future::{ok, Ready}; +use log::trace; use tokio_rustls::{Connect, TlsConnector}; use webpki::DNSNameRef; -use crate::{Address, Connection}; +use crate::connect::{Address, Connection}; /// Rustls connector factory pub struct RustlsConnector { @@ -53,11 +54,10 @@ impl Clone for RustlsConnector { } } -impl ServiceFactory for RustlsConnector +impl ServiceFactory> for RustlsConnector where U: AsyncRead + AsyncWrite + Unpin + fmt::Debug, { - type Request = Connection; type Response = Connection>; type Error = std::io::Error; type Config = (); @@ -87,11 +87,10 @@ impl Clone for RustlsConnectorService { } } -impl Service for RustlsConnectorService +impl Service> for RustlsConnectorService where U: AsyncRead + AsyncWrite + Unpin + fmt::Debug, { - type Request = Connection; type Response = Connection>; type Error = std::io::Error; type Future = ConnectAsyncExt; diff --git a/actix-connect/src/uri.rs b/actix-tls/src/connect/uri.rs similarity index 97% rename from actix-connect/src/uri.rs rename to actix-tls/src/connect/uri.rs index 5f5f15de..b208a8b3 100644 --- a/actix-connect/src/uri.rs +++ b/actix-tls/src/connect/uri.rs @@ -1,6 +1,6 @@ use http::Uri; -use crate::Address; +use super::Address; impl Address for Uri { fn host(&self) -> &str { diff --git a/actix-tls/src/lib.rs b/actix-tls/src/lib.rs index 8cc18046..1fa08b6b 100644 --- a/actix-tls/src/lib.rs +++ b/actix-tls/src/lib.rs @@ -1,46 +1,17 @@ -//! TLS acceptor services for Actix ecosystem. -//! -//! ## Crate Features -//! * `openssl` - TLS acceptor using the `openssl` crate. -//! * `rustls` - TLS acceptor using the `rustls` crate. -//! * `nativetls` - TLS acceptor using the `native-tls` crate. +//! TLS acceptor and connector services for Actix ecosystem #![deny(rust_2018_idioms, nonstandard_style)] #![doc(html_logo_url = "https://actix.rs/img/logo.png")] #![doc(html_favicon_url = "https://actix.rs/favicon.ico")] -use std::sync::atomic::{AtomicUsize, Ordering}; - -use actix_utils::counter::Counter; - +#[cfg(feature = "native-tls")] +extern crate tls_native_tls as native_tls; #[cfg(feature = "openssl")] -pub mod openssl; - +extern crate tls_openssl as openssl; #[cfg(feature = "rustls")] -pub mod rustls; +extern crate tls_rustls as rustls; -#[cfg(feature = "nativetls")] -pub mod nativetls; - -pub(crate) static MAX_CONN: AtomicUsize = AtomicUsize::new(256); - -thread_local! { - static MAX_CONN_COUNTER: Counter = Counter::new(MAX_CONN.load(Ordering::Relaxed)); -} - -/// Sets the maximum per-worker concurrent TLS connection limit. -/// -/// All listeners will stop accepting connections when this limit is reached. -/// It can be used to regulate the global TLS CPU usage. -/// -/// By default, the connection limit is 256. -pub fn max_concurrent_tls_connect(num: usize) { - MAX_CONN.store(num, Ordering::Relaxed); -} - -/// TLS error combined with service error. -#[derive(Debug)] -pub enum TlsError { - Tls(E1), - Service(E2), -} +#[cfg(feature = "accept")] +pub mod accept; +#[cfg(feature = "connect")] +pub mod connect; From 0934078947e294f6a70b56ecbf50cd41a963e47b Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Tue, 29 Dec 2020 01:04:21 +0000 Subject: [PATCH 19/73] prepare tls beta release --- actix-tls/CHANGES.md | 7 +++++-- actix-tls/Cargo.toml | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/actix-tls/CHANGES.md b/actix-tls/CHANGES.md index 9f03bfeb..dcb477d8 100644 --- a/actix-tls/CHANGES.md +++ b/actix-tls/CHANGES.md @@ -1,9 +1,12 @@ # Changes -## Unreleased - 2020-xx-xx +## Unreleased - 2021-xx-xx + + +## 3.0.0-beta.1 - 2020-12-29 * Move acceptors under `accept` module. [#238] * Merge `actix-connect` crate under `connect` module. [#238] -* Add feature flags to enable acceptors and/or connectors individually. +* Add feature flags to enable acceptors and/or connectors individually. [#238] [#238]: https://github.com/actix/actix-net/pull/238 diff --git a/actix-tls/Cargo.toml b/actix-tls/Cargo.toml index de2d859f..af0b4238 100644 --- a/actix-tls/Cargo.toml +++ b/actix-tls/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-tls" -version = "2.0.0" +version = "3.0.0-beta.1" authors = ["Nikolay Kim "] description = "TLS acceptor and connector services for Actix ecosystem" keywords = ["network", "tls", "ssl", "async", "transport"] From 03eb96d6d4007e9c9492ca0b07b8f39238a8afaa Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Tue, 29 Dec 2020 19:36:17 +0800 Subject: [PATCH 20/73] fix actix-tls tests (#241) --- Cargo.toml | 4 + actix-threadpool/Cargo.toml | 2 +- actix-tls/Cargo.toml | 20 +++-- actix-tls/src/accept/nativetls.rs | 52 ++++------- actix-tls/src/accept/openssl.rs | 30 +++---- actix-tls/src/accept/rustls.rs | 38 +++----- actix-tls/src/connect/connector.rs | 91 ++++++++++--------- actix-tls/src/connect/mod.rs | 5 +- actix-tls/src/connect/ssl/openssl.rs | 60 +++++-------- actix-tls/src/connect/ssl/rustls.rs | 64 ++++++------- actix-tls/tests/test_connect.rs | 130 +++++++++++++++++++++++++++ string/Cargo.toml | 2 +- 12 files changed, 291 insertions(+), 207 deletions(-) create mode 100644 actix-tls/tests/test_connect.rs diff --git a/Cargo.toml b/Cargo.toml index 533fbc17..342f866a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,3 +27,7 @@ actix-tracing = { path = "actix-tracing" } actix-utils = { path = "actix-utils" } actix-router = { path = "router" } bytestring = { path = "string" } + +# FIXME: remove override +trust-dns-proto = { git = "https://github.com/bluejekyll/trust-dns.git", branch = "main" } +trust-dns-resolver = { git = "https://github.com/bluejekyll/trust-dns.git", branch = "main" } \ No newline at end of file diff --git a/actix-threadpool/Cargo.toml b/actix-threadpool/Cargo.toml index 5998bae7..3a633d62 100644 --- a/actix-threadpool/Cargo.toml +++ b/actix-threadpool/Cargo.toml @@ -19,7 +19,7 @@ path = "src/lib.rs" [dependencies] derive_more = "0.99.2" -futures-channel = "0.3.1" +futures-channel = "0.3.7" parking_lot = "0.11" lazy_static = "1.3" log = "0.4" diff --git a/actix-tls/Cargo.toml b/actix-tls/Cargo.toml index af0b4238..1c24e0eb 100644 --- a/actix-tls/Cargo.toml +++ b/actix-tls/Cargo.toml @@ -12,7 +12,7 @@ license = "MIT OR Apache-2.0" edition = "2018" [package.metadata.docs.rs] -features = ["openssl", "rustls", "native-tls", "accept", "connect", "http"] +features = ["openssl", "rustls", "native-tls", "accept", "connect", "uri"] [lib] name = "actix_tls" @@ -23,13 +23,13 @@ name = "basic" required-features = ["accept", "rustls"] [features] -default = ["accept", "connect", "http"] +default = ["accept", "connect", "uri"] # enable acceptor services accept = [] # enable connector services -connect = [] +connect = ["trust-dns-proto/tokio-runtime", "trust-dns-resolver/tokio-runtime", "trust-dns-resolver/system-config"] # use openssl impls openssl = ["tls-openssl", "tokio-openssl"] @@ -40,6 +40,9 @@ rustls = ["tls-rustls", "webpki", "webpki-roots", "tokio-rustls"] # use native-tls impls native-tls = ["tls-native-tls", "tokio-native-tls"] +# support http::Uri as connect address +uri = ["http"] + [dependencies] actix-codec = "0.4.0-beta.1" actix-rt = "2.0.0-beta.1" @@ -49,10 +52,12 @@ actix-utils = "3.0.0-beta.1" derive_more = "0.99.5" either = "1.6" futures-util = { version = "0.3.7", default-features = false } -http = { version = "0.2.0", optional = true } +http = { version = "0.2.2", optional = true } log = "0.4" -trust-dns-proto = { version = "0.19", default-features = false, features = ["tokio-runtime"] } -trust-dns-resolver = { version = "0.19", default-features = false, features = ["tokio-runtime", "system-config"] } + +# resolver +trust-dns-proto = { version = "0.20.0-alpha.3", default-features = false, optional = true } +trust-dns-resolver = { version = "0.20.0-alpha.3", default-features = false, optional = true } # openssl tls-openssl = { package = "openssl", version = "0.10", optional = true } @@ -74,5 +79,6 @@ tokio-native-tls = { version = "0.3", optional = true } actix-server = "2.0.0-beta.1" actix-testing = "2.0.0-beta.1" bytes = "1" -log = "0.4" env_logger = "0.8" +futures-util = { version = "0.3.7", default-features = false, features = ["sink"] } +log = "0.4" diff --git a/actix-tls/src/accept/nativetls.rs b/actix-tls/src/accept/nativetls.rs index 12d23855..5d80ce8b 100644 --- a/actix-tls/src/accept/nativetls.rs +++ b/actix-tls/src/accept/nativetls.rs @@ -1,10 +1,9 @@ -use std::marker::PhantomData; use std::task::{Context, Poll}; use actix_codec::{AsyncRead, AsyncWrite}; use actix_service::{Service, ServiceFactory}; use actix_utils::counter::Counter; -use futures_util::future::{self, FutureExt, LocalBoxFuture, TryFutureExt}; +use futures_util::future::{ready, LocalBoxFuture, Ready}; pub use native_tls::Error; pub use tokio_native_tls::{TlsAcceptor, TlsStream}; @@ -14,75 +13,64 @@ use super::MAX_CONN_COUNTER; /// Accept TLS connections via `native-tls` package. /// /// `native-tls` feature enables this `Acceptor` type. -pub struct Acceptor { +pub struct Acceptor { acceptor: TlsAcceptor, - io: PhantomData, } -impl Acceptor -where - T: AsyncRead + AsyncWrite + Unpin, -{ +impl Acceptor { /// Create `native-tls` based `Acceptor` service factory. #[inline] pub fn new(acceptor: TlsAcceptor) -> Self { - Acceptor { - acceptor, - io: PhantomData, - } + Acceptor { acceptor } } } -impl Clone for Acceptor { +impl Clone for Acceptor { #[inline] fn clone(&self) -> Self { Self { acceptor: self.acceptor.clone(), - io: PhantomData, } } } -impl ServiceFactory for Acceptor +impl ServiceFactory for Acceptor where T: AsyncRead + AsyncWrite + Unpin + 'static, { type Response = TlsStream; type Error = Error; - type Service = NativeTlsAcceptorService; - type Config = (); + + type Service = NativeTlsAcceptorService; type InitError = (); - type Future = future::Ready>; + type Future = Ready>; fn new_service(&self, _: ()) -> Self::Future { MAX_CONN_COUNTER.with(|conns| { - future::ok(NativeTlsAcceptorService { + ready(Ok(NativeTlsAcceptorService { acceptor: self.acceptor.clone(), conns: conns.clone(), - io: PhantomData, - }) + })) }) } } -pub struct NativeTlsAcceptorService { +pub struct NativeTlsAcceptorService { acceptor: TlsAcceptor, - io: PhantomData, conns: Counter, } -impl Clone for NativeTlsAcceptorService { +impl Clone for NativeTlsAcceptorService { fn clone(&self) -> Self { Self { acceptor: self.acceptor.clone(), - io: PhantomData, conns: self.conns.clone(), } } } -impl Service for NativeTlsAcceptorService +impl Service for NativeTlsAcceptorService where T: AsyncRead + AsyncWrite + Unpin + 'static, { @@ -101,12 +89,10 @@ where fn call(&mut self, io: T) -> Self::Future { let guard = self.conns.get(); let this = self.clone(); - async move { this.acceptor.accept(io).await } - .map_ok(move |io| { - // Required to preserve `CounterGuard` until `Self::Future` is completely resolved. - let _ = guard; - io - }) - .boxed_local() + Box::pin(async move { + let io = this.acceptor.accept(io).await; + drop(guard); + io + }) } } diff --git a/actix-tls/src/accept/openssl.rs b/actix-tls/src/accept/openssl.rs index 44877b24..e51808a3 100644 --- a/actix-tls/src/accept/openssl.rs +++ b/actix-tls/src/accept/openssl.rs @@ -1,5 +1,4 @@ use std::future::Future; -use std::marker::PhantomData; use std::pin::Pin; use std::task::{Context, Poll}; @@ -7,7 +6,7 @@ use actix_codec::{AsyncRead, AsyncWrite}; use actix_service::{Service, ServiceFactory}; use actix_utils::counter::{Counter, CounterGuard}; use futures_util::{ - future::{ok, Ready}, + future::{ready, Ready}, ready, }; @@ -21,61 +20,54 @@ use super::MAX_CONN_COUNTER; /// Accept TLS connections via `openssl` package. /// /// `openssl` feature enables this `Acceptor` type. -pub struct Acceptor { +pub struct Acceptor { acceptor: SslAcceptor, - io: PhantomData, } -impl Acceptor { +impl Acceptor { /// Create OpenSSL based `Acceptor` service factory. #[inline] pub fn new(acceptor: SslAcceptor) -> Self { - Acceptor { - acceptor, - io: PhantomData, - } + Acceptor { acceptor } } } -impl Clone for Acceptor { +impl Clone for Acceptor { #[inline] fn clone(&self) -> Self { Self { acceptor: self.acceptor.clone(), - io: PhantomData, } } } -impl ServiceFactory for Acceptor +impl ServiceFactory for Acceptor where T: AsyncRead + AsyncWrite + Unpin + 'static, { type Response = SslStream; type Error = SslError; type Config = (); - type Service = AcceptorService; + type Service = AcceptorService; type InitError = (); type Future = Ready>; fn new_service(&self, _: ()) -> Self::Future { MAX_CONN_COUNTER.with(|conns| { - ok(AcceptorService { + ready(Ok(AcceptorService { acceptor: self.acceptor.clone(), conns: conns.clone(), - io: PhantomData, - }) + })) }) } } -pub struct AcceptorService { +pub struct AcceptorService { acceptor: SslAcceptor, conns: Counter, - io: PhantomData, } -impl Service for AcceptorService +impl Service for AcceptorService where T: AsyncRead + AsyncWrite + Unpin + 'static, { diff --git a/actix-tls/src/accept/rustls.rs b/actix-tls/src/accept/rustls.rs index 1c40757e..a6686f44 100644 --- a/actix-tls/src/accept/rustls.rs +++ b/actix-tls/src/accept/rustls.rs @@ -1,6 +1,5 @@ use std::future::Future; use std::io; -use std::marker::PhantomData; use std::pin::Pin; use std::sync::Arc; use std::task::{Context, Poll}; @@ -8,7 +7,7 @@ use std::task::{Context, Poll}; use actix_codec::{AsyncRead, AsyncWrite}; use actix_service::{Service, ServiceFactory}; use actix_utils::counter::{Counter, CounterGuard}; -use futures_util::future::{ok, Ready}; +use futures_util::future::{ready, Ready}; use tokio_rustls::{Accept, TlsAcceptor}; pub use rustls::{ServerConfig, Session}; @@ -20,66 +19,58 @@ use super::MAX_CONN_COUNTER; /// Accept TLS connections via `rustls` package. /// /// `rustls` feature enables this `Acceptor` type. -pub struct Acceptor { +pub struct Acceptor { config: Arc, - io: PhantomData, } -impl Acceptor -where - T: AsyncRead + AsyncWrite, -{ +impl Acceptor { /// Create Rustls based `Acceptor` service factory. #[inline] pub fn new(config: ServerConfig) -> Self { Acceptor { config: Arc::new(config), - io: PhantomData, } } } -impl Clone for Acceptor { +impl Clone for Acceptor { #[inline] fn clone(&self) -> Self { Self { config: self.config.clone(), - io: PhantomData, } } } -impl ServiceFactory for Acceptor +impl ServiceFactory for Acceptor where T: AsyncRead + AsyncWrite + Unpin, { type Response = TlsStream; type Error = io::Error; - type Service = AcceptorService; - type Config = (); + + type Service = AcceptorService; type InitError = (); type Future = Ready>; fn new_service(&self, _: ()) -> Self::Future { MAX_CONN_COUNTER.with(|conns| { - ok(AcceptorService { + ready(Ok(AcceptorService { acceptor: self.config.clone().into(), conns: conns.clone(), - io: PhantomData, - }) + })) }) } } /// Rustls based `Acceptor` service -pub struct AcceptorService { +pub struct AcceptorService { acceptor: TlsAcceptor, - io: PhantomData, conns: Counter, } -impl Service for AcceptorService +impl Service for AcceptorService where T: AsyncRead + AsyncWrite + Unpin, { @@ -119,11 +110,6 @@ where fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let this = self.get_mut(); - - let res = futures_util::ready!(Pin::new(&mut this.fut).poll(cx)); - match res { - Ok(io) => Poll::Ready(Ok(io)), - Err(e) => Poll::Ready(Err(e)), - } + Pin::new(&mut this.fut).poll(cx) } } diff --git a/actix-tls/src/connect/connector.rs b/actix-tls/src/connect/connector.rs index c7a31e0a..a0a6b8b5 100644 --- a/actix-tls/src/connect/connector.rs +++ b/actix-tls/src/connect/connector.rs @@ -8,7 +8,7 @@ use std::task::{Context, Poll}; use actix_rt::net::TcpStream; use actix_service::{Service, ServiceFactory}; -use futures_util::future::{err, ok, BoxFuture, Either, FutureExt, Ready}; +use futures_util::future::{ready, Ready}; use log::{error, trace}; use super::connect::{Address, Connect, Connection}; @@ -50,7 +50,7 @@ impl ServiceFactory> for TcpConnectorFactory { type Future = Ready>; fn new_service(&self, _: ()) -> Self::Future { - ok(self.service()) + ready(Ok(self.service())) } } @@ -73,8 +73,7 @@ impl Clone for TcpConnector { impl Service> for TcpConnector { type Response = Connection; type Error = ConnectError; - #[allow(clippy::type_complexity)] - type Future = Either, Ready>>; + type Future = TcpConnectorResponse; actix_service::always_ready!(); @@ -83,21 +82,26 @@ impl Service> for TcpConnector { let Connect { req, addr, .. } = req; if let Some(addr) = addr { - Either::Left(TcpConnectorResponse::new(req, port, addr)) + TcpConnectorResponse::new(req, port, addr) } else { error!("TCP connector: got unresolved address"); - Either::Right(err(ConnectError::Unresolved)) + TcpConnectorResponse::Error(Some(ConnectError::Unresolved)) } } } +type LocalBoxFuture<'a, T> = Pin + 'a>>; + #[doc(hidden)] /// TCP stream connector response future -pub struct TcpConnectorResponse { - req: Option, - port: u16, - addrs: Option>, - stream: Option>>, +pub enum TcpConnectorResponse { + Response { + req: Option, + port: u16, + addrs: Option>, + stream: Option>>, + }, + Error(Option), } impl TcpConnectorResponse { @@ -113,13 +117,13 @@ impl TcpConnectorResponse { ); match addr { - either::Either::Left(addr) => TcpConnectorResponse { + either::Either::Left(addr) => TcpConnectorResponse::Response { req: Some(req), port, addrs: None, - stream: Some(TcpStream::connect(addr).boxed()), + stream: Some(Box::pin(TcpStream::connect(addr))), }, - either::Either::Right(addrs) => TcpConnectorResponse { + either::Either::Right(addrs) => TcpConnectorResponse::Response { req: Some(req), port, addrs: Some(addrs), @@ -134,36 +138,43 @@ impl Future for TcpConnectorResponse { fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let this = self.get_mut(); - - // connect - loop { - if let Some(new) = this.stream.as_mut() { - match new.as_mut().poll(cx) { - Poll::Ready(Ok(sock)) => { - let req = this.req.take().unwrap(); - trace!( - "TCP connector - successfully connected to connecting to {:?} - {:?}", - req.host(), sock.peer_addr() - ); - return Poll::Ready(Ok(Connection::new(sock, req))); - } - Poll::Pending => return Poll::Pending, - Poll::Ready(Err(err)) => { - trace!( - "TCP connector - failed to connect to connecting to {:?} port: {}", - this.req.as_ref().unwrap().host(), - this.port, - ); - if this.addrs.is_none() || this.addrs.as_ref().unwrap().is_empty() { - return Poll::Ready(Err(err.into())); + match this { + TcpConnectorResponse::Error(e) => Poll::Ready(Err(e.take().unwrap())), + // connect + TcpConnectorResponse::Response { + req, + port, + addrs, + stream, + } => loop { + if let Some(new) = stream.as_mut() { + match new.as_mut().poll(cx) { + Poll::Ready(Ok(sock)) => { + let req = req.take().unwrap(); + trace!( + "TCP connector - successfully connected to connecting to {:?} - {:?}", + req.host(), sock.peer_addr() + ); + return Poll::Ready(Ok(Connection::new(sock, req))); + } + Poll::Pending => return Poll::Pending, + Poll::Ready(Err(err)) => { + trace!( + "TCP connector - failed to connect to connecting to {:?} port: {}", + req.as_ref().unwrap().host(), + port, + ); + if addrs.is_none() || addrs.as_ref().unwrap().is_empty() { + return Poll::Ready(Err(err.into())); + } } } } - } - // try to connect - let addr = this.addrs.as_mut().unwrap().pop_front().unwrap(); - this.stream = Some(TcpStream::connect(addr).boxed()); + // try to connect + let addr = addrs.as_mut().unwrap().pop_front().unwrap(); + *stream = Some(Box::pin(TcpStream::connect(addr))); + }, } } } diff --git a/actix-tls/src/connect/mod.rs b/actix-tls/src/connect/mod.rs index 753cfc33..75312c59 100644 --- a/actix-tls/src/connect/mod.rs +++ b/actix-tls/src/connect/mod.rs @@ -11,6 +11,7 @@ mod error; mod resolve; mod service; pub mod ssl; +#[cfg(feature = "uri")] mod uri; use actix_rt::{net::TcpStream, Arbiter}; @@ -35,7 +36,7 @@ pub async fn start_resolver( cfg: ResolverConfig, opts: ResolverOpts, ) -> Result { - Ok(AsyncResolver::tokio(cfg, opts).await?) + Ok(AsyncResolver::tokio(cfg, opts)?) } struct DefaultResolver(AsyncResolver); @@ -52,7 +53,7 @@ pub(crate) async fn get_default_resolver() -> Result { +pub struct OpensslConnector { connector: SslConnector, - _t: PhantomData<(T, U)>, } -impl OpensslConnector { +impl OpensslConnector { pub fn new(connector: SslConnector) -> Self { - OpensslConnector { - connector, - _t: PhantomData, - } + OpensslConnector { connector } } } -impl OpensslConnector -where - T: Address + 'static, - U: AsyncRead + AsyncWrite + Unpin + fmt::Debug + 'static, -{ - pub fn service(connector: SslConnector) -> OpensslConnectorService { - OpensslConnectorService { - connector, - _t: PhantomData, - } +impl OpensslConnector { + pub fn service(connector: SslConnector) -> OpensslConnectorService { + OpensslConnectorService { connector } } } -impl Clone for OpensslConnector { +impl Clone for OpensslConnector { fn clone(&self) -> Self { Self { connector: self.connector.clone(), - _t: PhantomData, } } } -impl ServiceFactory> for OpensslConnector +impl ServiceFactory> for OpensslConnector where T: Address + 'static, U: AsyncRead + AsyncWrite + Unpin + fmt::Debug + 'static, @@ -65,33 +53,30 @@ where type Response = Connection>; type Error = io::Error; type Config = (); - type Service = OpensslConnectorService; + type Service = OpensslConnectorService; type InitError = (); type Future = Ready>; fn new_service(&self, _: ()) -> Self::Future { - ok(OpensslConnectorService { + ready(Ok(OpensslConnectorService { connector: self.connector.clone(), - _t: PhantomData, - }) + })) } } -pub struct OpensslConnectorService { +pub struct OpensslConnectorService { connector: SslConnector, - _t: PhantomData<(T, U)>, } -impl Clone for OpensslConnectorService { +impl Clone for OpensslConnectorService { fn clone(&self) -> Self { Self { connector: self.connector.clone(), - _t: PhantomData, } } } -impl Service> for OpensslConnectorService +impl Service> for OpensslConnectorService where T: Address + 'static, U: AsyncRead + AsyncWrite + Unpin + fmt::Debug + 'static, @@ -109,7 +94,7 @@ where let host = stream.host().to_string(); match self.connector.configure() { - Err(e) => Either::Right(err(io::Error::new(io::ErrorKind::Other, e))), + Err(e) => Either::Right(ready(Err(io::Error::new(io::ErrorKind::Other, e)))), Ok(config) => { let ssl = config .into_ssl(&host) @@ -156,7 +141,7 @@ where pub struct OpensslConnectServiceFactory { tcp: ConnectServiceFactory, - openssl: OpensslConnector, + openssl: OpensslConnector, } impl OpensslConnectServiceFactory { @@ -182,7 +167,6 @@ impl OpensslConnectServiceFactory { tcp: self.tcp.service(), openssl: OpensslConnectorService { connector: self.openssl.connector.clone(), - _t: PhantomData, }, } } @@ -206,14 +190,14 @@ impl ServiceFactory> for OpensslConnectServiceF type Future = Ready>; fn new_service(&self, _: ()) -> Self::Future { - ok(self.service()) + ready(Ok(self.service())) } } #[derive(Clone)] pub struct OpensslConnectService { tcp: ConnectService, - openssl: OpensslConnectorService, + openssl: OpensslConnectorService, } impl Service> for OpensslConnectService { @@ -234,10 +218,8 @@ impl Service> for OpensslConnectService { pub struct OpensslConnectServiceResponse { fut1: Option< as Service>>::Future>, - fut2: Option< - as Service>>::Future, - >, - openssl: OpensslConnectorService, + fut2: Option<>>::Future>, + openssl: OpensslConnectorService, } impl Future for OpensslConnectServiceResponse { diff --git a/actix-tls/src/connect/ssl/rustls.rs b/actix-tls/src/connect/ssl/rustls.rs index f5259280..390ba413 100644 --- a/actix-tls/src/connect/ssl/rustls.rs +++ b/actix-tls/src/connect/ssl/rustls.rs @@ -1,6 +1,5 @@ use std::fmt; use std::future::Future; -use std::marker::PhantomData; use std::pin::Pin; use std::sync::Arc; use std::task::{Context, Poll}; @@ -10,7 +9,10 @@ pub use tokio_rustls::{client::TlsStream, rustls::ClientConfig}; use actix_codec::{AsyncRead, AsyncWrite}; use actix_service::{Service, ServiceFactory}; -use futures_util::future::{ok, Ready}; +use futures_util::{ + future::{ready, Ready}, + ready, +}; use log::trace; use tokio_rustls::{Connect, TlsConnector}; use webpki::DNSNameRef; @@ -18,77 +20,63 @@ use webpki::DNSNameRef; use crate::connect::{Address, Connection}; /// Rustls connector factory -pub struct RustlsConnector { +pub struct RustlsConnector { connector: Arc, - _t: PhantomData<(T, U)>, } -impl RustlsConnector { +impl RustlsConnector { pub fn new(connector: Arc) -> Self { - RustlsConnector { - connector, - _t: PhantomData, - } + RustlsConnector { connector } } } -impl RustlsConnector -where - T: Address, - U: AsyncRead + AsyncWrite + Unpin + fmt::Debug, -{ - pub fn service(connector: Arc) -> RustlsConnectorService { - RustlsConnectorService { - connector, - _t: PhantomData, - } +impl RustlsConnector { + pub fn service(connector: Arc) -> RustlsConnectorService { + RustlsConnectorService { connector } } } -impl Clone for RustlsConnector { +impl Clone for RustlsConnector { fn clone(&self) -> Self { Self { connector: self.connector.clone(), - _t: PhantomData, } } } -impl ServiceFactory> for RustlsConnector +impl ServiceFactory> for RustlsConnector where U: AsyncRead + AsyncWrite + Unpin + fmt::Debug, { type Response = Connection>; type Error = std::io::Error; type Config = (); - type Service = RustlsConnectorService; + type Service = RustlsConnectorService; type InitError = (); type Future = Ready>; fn new_service(&self, _: ()) -> Self::Future { - ok(RustlsConnectorService { + ready(Ok(RustlsConnectorService { connector: self.connector.clone(), - _t: PhantomData, - }) + })) } } -pub struct RustlsConnectorService { +pub struct RustlsConnectorService { connector: Arc, - _t: PhantomData<(T, U)>, } -impl Clone for RustlsConnectorService { +impl Clone for RustlsConnectorService { fn clone(&self) -> Self { Self { connector: self.connector.clone(), - _t: PhantomData, } } } -impl Service> for RustlsConnectorService +impl Service> for RustlsConnectorService where + T: Address, U: AsyncRead + AsyncWrite + Unpin + fmt::Debug, { type Response = Connection>; @@ -114,20 +102,18 @@ pub struct ConnectAsyncExt { stream: Option>, } -impl Future for ConnectAsyncExt +impl Future for ConnectAsyncExt where + T: Address, U: AsyncRead + AsyncWrite + Unpin + fmt::Debug, { type Output = Result>, std::io::Error>; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let this = self.get_mut(); - Poll::Ready( - futures_util::ready!(Pin::new(&mut this.fut).poll(cx)).map(|stream| { - let s = this.stream.take().unwrap(); - trace!("SSL Handshake success: {:?}", s.host()); - s.replace(stream).1 - }), - ) + let stream = ready!(Pin::new(&mut this.fut).poll(cx))?; + let s = this.stream.take().unwrap(); + trace!("SSL Handshake success: {:?}", s.host()); + Poll::Ready(Ok(s.replace(stream).1)) } } diff --git a/actix-tls/tests/test_connect.rs b/actix-tls/tests/test_connect.rs new file mode 100644 index 00000000..fd083893 --- /dev/null +++ b/actix-tls/tests/test_connect.rs @@ -0,0 +1,130 @@ +use std::io; + +use actix_codec::{BytesCodec, Framed}; +use actix_rt::net::TcpStream; +use actix_service::{fn_service, Service, ServiceFactory}; +use actix_testing::TestServer; +use bytes::Bytes; +use futures_util::sink::SinkExt; + +use actix_tls::connect::{ + self as actix_connect, + resolver::{ResolverConfig, ResolverOpts}, + Connect, +}; + +#[cfg(all(feature = "connect", feature = "openssl"))] +#[actix_rt::test] +async fn test_string() { + let srv = TestServer::with(|| { + fn_service(|io: TcpStream| async { + let mut framed = Framed::new(io, BytesCodec); + framed.send(Bytes::from_static(b"test")).await?; + Ok::<_, io::Error>(()) + }) + }); + + let mut conn = actix_connect::default_connector(); + let addr = format!("localhost:{}", srv.port()); + let con = conn.call(addr.into()).await.unwrap(); + assert_eq!(con.peer_addr().unwrap(), srv.addr()); +} + +#[cfg(feature = "rustls")] +#[actix_rt::test] +async fn test_rustls_string() { + let srv = TestServer::with(|| { + fn_service(|io: TcpStream| async { + let mut framed = Framed::new(io, BytesCodec); + framed.send(Bytes::from_static(b"test")).await?; + Ok::<_, io::Error>(()) + }) + }); + + let mut conn = actix_connect::default_connector(); + let addr = format!("localhost:{}", srv.port()); + let con = conn.call(addr.into()).await.unwrap(); + assert_eq!(con.peer_addr().unwrap(), srv.addr()); +} + +#[actix_rt::test] +async fn test_static_str() { + let srv = TestServer::with(|| { + fn_service(|io: TcpStream| async { + let mut framed = Framed::new(io, BytesCodec); + framed.send(Bytes::from_static(b"test")).await?; + Ok::<_, io::Error>(()) + }) + }); + + let resolver = actix_connect::start_default_resolver().await.unwrap(); + let mut conn = actix_connect::new_connector(resolver.clone()); + + let con = conn.call(Connect::with("10", srv.addr())).await.unwrap(); + assert_eq!(con.peer_addr().unwrap(), srv.addr()); + + let connect = Connect::new(srv.host().to_owned()); + let mut conn = actix_connect::new_connector(resolver); + let con = conn.call(connect).await; + assert!(con.is_err()); +} + +#[actix_rt::test] +async fn test_new_service() { + let srv = TestServer::with(|| { + fn_service(|io: TcpStream| async { + let mut framed = Framed::new(io, BytesCodec); + framed.send(Bytes::from_static(b"test")).await?; + Ok::<_, io::Error>(()) + }) + }); + + let resolver = + actix_connect::start_resolver(ResolverConfig::default(), ResolverOpts::default()) + .await + .unwrap(); + + let factory = actix_connect::new_connector_factory(resolver); + + let mut conn = factory.new_service(()).await.unwrap(); + let con = conn.call(Connect::with("10", srv.addr())).await.unwrap(); + assert_eq!(con.peer_addr().unwrap(), srv.addr()); +} + +#[cfg(all(feature = "openssl", feature = "uri"))] +#[actix_rt::test] +async fn test_openssl_uri() { + use std::convert::TryFrom; + + let srv = TestServer::with(|| { + fn_service(|io: TcpStream| async { + let mut framed = Framed::new(io, BytesCodec); + framed.send(Bytes::from_static(b"test")).await?; + Ok::<_, io::Error>(()) + }) + }); + + let mut conn = actix_connect::default_connector(); + let addr = http::Uri::try_from(format!("https://localhost:{}", srv.port())).unwrap(); + let con = conn.call(addr.into()).await.unwrap(); + assert_eq!(con.peer_addr().unwrap(), srv.addr()); +} + +#[cfg(all(feature = "rustls", feature = "uri"))] +#[actix_rt::test] +async fn test_rustls_uri() { + use std::convert::TryFrom; + + let srv = TestServer::with(|| { + fn_service(|io: TcpStream| async { + let mut framed = Framed::new(io, BytesCodec); + framed.send(Bytes::from_static(b"test")).await?; + Ok::<_, io::Error>(()) + }) + }); + + let mut conn = actix_connect::default_connector(); + let addr = http::Uri::try_from(format!("https://localhost:{}", srv.port())).unwrap(); + let con = conn.call(addr.into()).await.unwrap(); + assert_eq!(con.peer_addr().unwrap(), srv.addr()); +} diff --git a/string/Cargo.toml b/string/Cargo.toml index 3653fe58..d3c290c8 100644 --- a/string/Cargo.toml +++ b/string/Cargo.toml @@ -15,7 +15,7 @@ name = "bytestring" path = "src/lib.rs" [dependencies] -bytes = "0.5.3" +bytes = "1" serde = { version = "1.0", optional = true } [dev-dependencies] From b5eefb4d42bbe96e6ba07a1a8b12026fd7a4041c Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Wed, 30 Dec 2020 05:20:24 +0800 Subject: [PATCH 21/73] merge actix-testing into actix-server (#242) --- Cargo.toml | 2 - actix-server/CHANGES.md | 2 +- actix-server/Cargo.toml | 1 - actix-server/src/lib.rs | 2 + .../lib.rs => actix-server/src/test_server.rs | 23 +++--------- actix-service/src/map_config.rs | 2 +- actix-testing/CHANGES.md | 37 ------------------- actix-testing/Cargo.toml | 26 ------------- actix-testing/LICENSE-APACHE | 1 - actix-testing/LICENSE-MIT | 1 - actix-testing/README.md | 9 ----- actix-tls/Cargo.toml | 1 - actix-tls/tests/test_connect.rs | 2 +- 13 files changed, 11 insertions(+), 98 deletions(-) rename actix-testing/src/lib.rs => actix-server/src/test_server.rs (81%) delete mode 100644 actix-testing/CHANGES.md delete mode 100644 actix-testing/Cargo.toml delete mode 120000 actix-testing/LICENSE-APACHE delete mode 120000 actix-testing/LICENSE-MIT delete mode 100644 actix-testing/README.md diff --git a/Cargo.toml b/Cargo.toml index 342f866a..17d1c857 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,6 @@ members = [ "actix-macros", "actix-service", "actix-server", - "actix-testing", "actix-threadpool", "actix-tls", "actix-tracing", @@ -20,7 +19,6 @@ actix-rt = { path = "actix-rt" } actix-macros = { path = "actix-macros" } actix-server = { path = "actix-server" } actix-service = { path = "actix-service" } -actix-testing = { path = "actix-testing" } actix-threadpool = { path = "actix-threadpool" } actix-tls = { path = "actix-tls" } actix-tracing = { path = "actix-tracing" } diff --git a/actix-server/CHANGES.md b/actix-server/CHANGES.md index 302ea576..f3b7c93c 100644 --- a/actix-server/CHANGES.md +++ b/actix-server/CHANGES.md @@ -1,7 +1,7 @@ # Changes ## Unreleased - 2020-xx-xx - +* Merge `actix-testing` to `actix-server` as `test_server` mod. ## 2.0.0-beta.1 - 2020-12-28 * Added explicit info log message on accept queue pause. [#215] diff --git a/actix-server/Cargo.toml b/actix-server/Cargo.toml index c57b8ac6..0bdd5d23 100644 --- a/actix-server/Cargo.toml +++ b/actix-server/Cargo.toml @@ -36,7 +36,6 @@ slab = "0.4" tokio = { version = "1", features = ["sync"] } [dev-dependencies] -actix-testing = "2.0.0-beta.1" bytes = "1" env_logger = "0.8" futures-util = { version = "0.3.7", default-features = false, features = ["sink"] } diff --git a/actix-server/src/lib.rs b/actix-server/src/lib.rs index 64aca7e4..24129b5a 100644 --- a/actix-server/src/lib.rs +++ b/actix-server/src/lib.rs @@ -11,6 +11,7 @@ mod server; mod service; mod signals; mod socket; +mod test_server; mod waker_queue; mod worker; @@ -18,6 +19,7 @@ pub use self::builder::ServerBuilder; pub use self::config::{ServiceConfig, ServiceRuntime}; pub use self::server::Server; pub use self::service::ServiceFactory; +pub use self::test_server::TestServer; #[doc(hidden)] pub use self::socket::FromStream; diff --git a/actix-testing/src/lib.rs b/actix-server/src/test_server.rs similarity index 81% rename from actix-testing/src/lib.rs rename to actix-server/src/test_server.rs index 57e2c223..627cc675 100644 --- a/actix-testing/src/lib.rs +++ b/actix-server/src/test_server.rs @@ -1,19 +1,9 @@ -//! Various helpers for Actix applications to use during testing. - -#![deny(rust_2018_idioms, nonstandard_style)] -#![allow(clippy::type_complexity, clippy::needless_doctest_main)] -#![doc(html_logo_url = "https://actix.rs/img/logo.png")] -#![doc(html_favicon_url = "https://actix.rs/favicon.ico")] - use std::sync::mpsc; use std::{net, thread}; use actix_rt::{net::TcpStream, System}; -use actix_server::{Server, ServerBuilder, ServiceFactory}; -use socket2::{Domain, Protocol, Socket, Type}; -#[cfg(not(test))] // Work around for rust-lang/rust#62127 -pub use actix_macros::test; +use crate::{Server, ServerBuilder, ServiceFactory}; /// The `TestServer` type. /// @@ -24,7 +14,7 @@ pub use actix_macros::test; /// /// ```rust /// use actix_service::fn_service; -/// use actix_testing::TestServer; +/// use actix_server::TestServer; /// /// #[actix_rt::main] /// async fn main() { @@ -115,11 +105,10 @@ impl TestServer { /// Get first available unused local address pub fn unused_addr() -> net::SocketAddr { let addr: net::SocketAddr = "127.0.0.1:0".parse().unwrap(); - let socket = - Socket::new(Domain::ipv4(), Type::stream(), Some(Protocol::tcp())).unwrap(); - socket.bind(&addr.into()).unwrap(); - socket.set_reuse_address(true).unwrap(); - let tcp = socket.into_tcp_listener(); + let socket = mio::net::TcpSocket::new_v4().unwrap(); + socket.bind(addr).unwrap(); + socket.set_reuseaddr(true).unwrap(); + let tcp = socket.listen(1024).unwrap(); tcp.local_addr().unwrap() } } diff --git a/actix-service/src/map_config.rs b/actix-service/src/map_config.rs index d6d6f6b2..1297f7a0 100644 --- a/actix-service/src/map_config.rs +++ b/actix-service/src/map_config.rs @@ -6,7 +6,7 @@ use super::{IntoServiceFactory, ServiceFactory}; /// /// Note that this function consumes the receiving service factory and returns /// a wrapped version of it. -pub fn map_config(factory: I, f: F) -> MapConfig +pub fn map_config(factory: I, f: F) -> MapConfig where I: IntoServiceFactory, SF: ServiceFactory, diff --git a/actix-testing/CHANGES.md b/actix-testing/CHANGES.md deleted file mode 100644 index f23c7521..00000000 --- a/actix-testing/CHANGES.md +++ /dev/null @@ -1,37 +0,0 @@ -# Changes - -## Unreleased - 2021-xx-xx - - -## 2.0.0-beta.1 - 2020-12-28 -* Update `actix-server` to v2.0.0-beta.1. - - -## 1.0.1 - 2020-05-19 -* Replace deprecated `net2` crate with `socket2` -* Remove unused `futures` dependency - - -## 1.0.0 - 2019-12-11 -* Update actix-server to 1.0.0 - - -## 1.0.0-alpha.3 - 2019-12-07 -* Migrate to tokio 0.2 - - -## 1.0.0-alpha.2 - 2019-12-02 -* Re-export `test` attribute macros - - - -## 0.3.0-alpha.1 - 2019-11-22 -* Migrate to std::future - - -## 0.2.0 - 2019-10-14 -* Upgrade actix-server and actix-server-config deps - - -## 0.1.0 - 2019-09-25 -* Initial impl diff --git a/actix-testing/Cargo.toml b/actix-testing/Cargo.toml deleted file mode 100644 index 3f29679e..00000000 --- a/actix-testing/Cargo.toml +++ /dev/null @@ -1,26 +0,0 @@ -[package] -name = "actix-testing" -version = "2.0.0-beta.1" -authors = ["Nikolay Kim "] -description = "Various helpers for Actix applications to use during testing" -keywords = ["network", "framework", "async", "futures"] -homepage = "https://actix.rs" -repository = "https://github.com/actix/actix-net.git" -documentation = "https://docs.rs/actix-testing/" -categories = ["network-programming", "asynchronous"] -license = "MIT OR Apache-2.0" -edition = "2018" -readme = "README.md" - -[lib] -name = "actix_testing" -path = "src/lib.rs" - -[dependencies] -actix-rt = "2.0.0-beta.1" -actix-macros = "0.1.0" -actix-server = "2.0.0-beta.1" -actix-service = "2.0.0-beta.1" - -log = "0.4" -socket2 = "0.3" diff --git a/actix-testing/LICENSE-APACHE b/actix-testing/LICENSE-APACHE deleted file mode 120000 index 965b606f..00000000 --- a/actix-testing/LICENSE-APACHE +++ /dev/null @@ -1 +0,0 @@ -../LICENSE-APACHE \ No newline at end of file diff --git a/actix-testing/LICENSE-MIT b/actix-testing/LICENSE-MIT deleted file mode 120000 index 76219eb7..00000000 --- a/actix-testing/LICENSE-MIT +++ /dev/null @@ -1 +0,0 @@ -../LICENSE-MIT \ No newline at end of file diff --git a/actix-testing/README.md b/actix-testing/README.md deleted file mode 100644 index ae54b46d..00000000 --- a/actix-testing/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# Actix test utilities [![crates.io](https://meritbadge.herokuapp.com/actix-testing)](https://crates.io/crates/actix-testint) [![Join the chat at https://gitter.im/actix/actix](https://badges.gitter.im/actix/actix.svg)](https://gitter.im/actix/actix?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) - -## Documentation & community resources - -* [User Guide](https://actix.rs/docs/) -* [API Documentation](https://docs.rs/actix-testing/) -* [Chat on gitter](https://gitter.im/actix/actix) -* Cargo package: [actix-http-test](https://crates.io/crates/actix-testing) -* Minimum supported Rust version: 1.46 or later diff --git a/actix-tls/Cargo.toml b/actix-tls/Cargo.toml index 1c24e0eb..2905fd5a 100644 --- a/actix-tls/Cargo.toml +++ b/actix-tls/Cargo.toml @@ -77,7 +77,6 @@ tokio-native-tls = { version = "0.3", optional = true } [dev-dependencies] actix-server = "2.0.0-beta.1" -actix-testing = "2.0.0-beta.1" bytes = "1" env_logger = "0.8" futures-util = { version = "0.3.7", default-features = false, features = ["sink"] } diff --git a/actix-tls/tests/test_connect.rs b/actix-tls/tests/test_connect.rs index fd083893..aa773c7f 100644 --- a/actix-tls/tests/test_connect.rs +++ b/actix-tls/tests/test_connect.rs @@ -2,8 +2,8 @@ use std::io; use actix_codec::{BytesCodec, Framed}; use actix_rt::net::TcpStream; +use actix_server::TestServer; use actix_service::{fn_service, Service, ServiceFactory}; -use actix_testing::TestServer; use bytes::Bytes; use futures_util::sink::SinkExt; From 647817ef1451845eaa17a3de66d3f5797010c0c9 Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Thu, 31 Dec 2020 06:11:50 +0800 Subject: [PATCH 22/73] tokio 1.0 and mio 0.7 (#204) --- Cargo.toml | 4 ---- actix-tls/Cargo.toml | 4 ++-- actix-tls/src/accept/openssl.rs | 6 ++---- router/Cargo.toml | 4 ++-- string/CHANGES.md | 3 +++ 5 files changed, 9 insertions(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 17d1c857..13fc843e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,3 @@ actix-tracing = { path = "actix-tracing" } actix-utils = { path = "actix-utils" } actix-router = { path = "router" } bytestring = { path = "string" } - -# FIXME: remove override -trust-dns-proto = { git = "https://github.com/bluejekyll/trust-dns.git", branch = "main" } -trust-dns-resolver = { git = "https://github.com/bluejekyll/trust-dns.git", branch = "main" } \ No newline at end of file diff --git a/actix-tls/Cargo.toml b/actix-tls/Cargo.toml index 2905fd5a..017f17fe 100644 --- a/actix-tls/Cargo.toml +++ b/actix-tls/Cargo.toml @@ -56,8 +56,8 @@ http = { version = "0.2.2", optional = true } log = "0.4" # resolver -trust-dns-proto = { version = "0.20.0-alpha.3", default-features = false, optional = true } -trust-dns-resolver = { version = "0.20.0-alpha.3", default-features = false, optional = true } +trust-dns-proto = { version = "0.20.0", default-features = false, optional = true } +trust-dns-resolver = { version = "0.20.0", default-features = false, optional = true } # openssl tls-openssl = { package = "openssl", version = "0.10", optional = true } diff --git a/actix-tls/src/accept/openssl.rs b/actix-tls/src/accept/openssl.rs index e51808a3..efda5c38 100644 --- a/actix-tls/src/accept/openssl.rs +++ b/actix-tls/src/accept/openssl.rs @@ -84,10 +84,8 @@ where } fn call(&mut self, io: T) -> Self::Future { - let acc = self.acceptor.clone(); - let ssl_ctx = acc.into_context(); - let ssl = Ssl::new(&ssl_ctx).expect("Provided SSL acceptor was invalid."); - + let ssl_ctx = self.acceptor.context(); + let ssl = Ssl::new(ssl_ctx).expect("Provided SSL acceptor was invalid."); AcceptorServiceResponse { _guard: self.conns.get(), stream: Some(SslStream::new(ssl, io).unwrap()), diff --git a/router/Cargo.toml b/router/Cargo.toml index fe553e27..6fc6ecc8 100644 --- a/router/Cargo.toml +++ b/router/Cargo.toml @@ -22,8 +22,8 @@ regex = "1.3.1" serde = "1.0.104" bytestring = "0.1.2" log = "0.4.8" -http = { version = "0.2.0", optional = true } +http = { version = "0.2.2", optional = true } [dev-dependencies] -http = "0.2.0" +http = "0.2.2" serde_derive = "1.0" diff --git a/string/CHANGES.md b/string/CHANGES.md index 030c3cd5..e66f81cd 100644 --- a/string/CHANGES.md +++ b/string/CHANGES.md @@ -1,5 +1,8 @@ # Changes +## Unreleased - 2020-xx-xx +* Update `bytes` dependency to `1` + ## [0.1.5] - 2020-03-30 * Serde support From 14ff379150fc38123f4979f4f199e7bf36f2db14 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Thu, 31 Dec 2020 02:20:49 +0000 Subject: [PATCH 23/73] prepare bytestring release 1.0.0 (#243) --- string/CHANGES.md | 22 ++++++---- string/Cargo.toml | 15 ++++--- string/src/lib.rs | 107 ++++++++++++++++++++++++++++++---------------- 3 files changed, 93 insertions(+), 51 deletions(-) diff --git a/string/CHANGES.md b/string/CHANGES.md index e66f81cd..90b32769 100644 --- a/string/CHANGES.md +++ b/string/CHANGES.md @@ -1,30 +1,36 @@ # Changes ## Unreleased - 2020-xx-xx -* Update `bytes` dependency to `1` -## [0.1.5] - 2020-03-30 +## 1.0.0 - 2020-12-31 +* Update `bytes` dependency to `1`. +* Add array and slice of `u8` impls of `TryFrom` up to 32 in length. +* Rename `get_ref` to `as_bytes` and rename `into_inner` to `into_bytes`. +* `ByteString::new` is now a `const fn`. +* Crate is now `#[no_std]` compatible. + + +## 0.1.5 - 2020-03-30 * Serde support -## [0.1.4] - 2020-01-14 +## 0.1.4 - 2020-01-14 * Fix `AsRef` impl -## [0.1.3] - 2020-01-13 +## 0.1.3 - 2020-01-13 * Add `PartialEq>`, `AsRef<[u8]>` impls -## [0.1.2] - 2019-12-22 +## 0.1.2 - 2019-12-22 * Fix `new()` method - * Make `ByteString::from_static()` and `ByteString::from_bytes_unchecked()` methods const. -## [0.1.1] - 2019-12-07 +## 0.1.1 - 2019-12-07 * Fix hash impl -## [0.1.0] - 2019-12-07 +## 0.1.0 - 2019-12-07 * Initial release diff --git a/string/Cargo.toml b/string/Cargo.toml index d3c290c8..4e41b5ea 100644 --- a/string/Cargo.toml +++ b/string/Cargo.toml @@ -1,9 +1,13 @@ [package] name = "bytestring" -version = "0.1.5" -authors = ["Nikolay Kim "] -description = "A UTF-8 encoded string with Bytes as a storage" -keywords = ["actix"] +version = "1.0.0" +authors = [ + "Nikolay Kim ", + "Rob Ede ", +] +description = "An immutable UTF-8 encoded string using Bytes as storage" +keywords = ["string", "bytes", "utf8", "web-programming", "actix"] +categories = ["no-std", "web-programming"] homepage = "https://actix.rs" repository = "https://github.com/actix/actix-net.git" documentation = "https://docs.rs/bytestring/" @@ -19,4 +23,5 @@ bytes = "1" serde = { version = "1.0", optional = true } [dev-dependencies] -serde_json = "1.0" \ No newline at end of file +serde_json = "1.0" +siphasher = "0.3" diff --git a/string/src/lib.rs b/string/src/lib.rs index 18cbc890..11c5a75a 100644 --- a/string/src/lib.rs +++ b/string/src/lib.rs @@ -1,37 +1,38 @@ //! A UTF-8 encoded read-only string using Bytes as storage. +#![no_std] #![deny(rust_2018_idioms, nonstandard_style)] #![doc(html_logo_url = "https://actix.rs/img/logo.png")] #![doc(html_favicon_url = "https://actix.rs/favicon.ico")] -use std::convert::TryFrom; -use std::{borrow, fmt, hash, ops, str}; +extern crate alloc; + +use alloc::{string::String, vec::Vec}; +use core::{borrow, convert::TryFrom, fmt, hash, ops, str}; use bytes::Bytes; -/// A UTF-8 encoded string with [`Bytes`] as a storage. -/// -/// [`Bytes`]: bytes::Bytes -#[derive(Clone, Eq, Ord, PartialOrd, Default)] +/// An immutable UTF-8 encoded string with [`Bytes`] as a storage. +#[derive(Clone, Default, Eq, PartialOrd, Ord)] pub struct ByteString(Bytes); impl ByteString { - /// Creates a new `ByteString`. - pub fn new() -> Self { + /// Creates a new empty `ByteString`. + pub const fn new() -> Self { ByteString(Bytes::new()) } - /// Get a reference to the underlying bytes object. - pub fn get_ref(&self) -> &Bytes { + /// Get a reference to the underlying `Bytes` object. + pub fn as_bytes(&self) -> &Bytes { &self.0 } - /// Unwraps this `ByteString`, returning the underlying bytes object. - pub fn into_inner(self) -> Bytes { + /// Unwraps this `ByteString` into the underlying `Bytes` object. + pub fn into_bytes(self) -> Bytes { self.0 } - /// Creates a new `ByteString` from a static str. + /// Creates a new `ByteString` from a `&'static str`. pub const fn from_static(src: &'static str) -> ByteString { Self(Bytes::from_static(src.as_bytes())) } @@ -39,11 +40,10 @@ impl ByteString { /// Creates a new `ByteString` from a Bytes. /// /// # Safety - /// This function is unsafe because it does not check the bytes passed to it - /// are valid UTF-8. If this constraint is violated, - /// it may cause memory unsafety issues with future users of the `ByteString`, - /// as we assume that `ByteString`s are valid UTF-8. - /// However, the most likely issue is that the data gets corrupted. + /// This function is unsafe because it does not check the bytes passed to it are valid UTF-8. + /// If this constraint is violated, it may cause memory unsafety issues with future users of + /// the `ByteString`, as we assume that `ByteString`s are valid UTF-8. However, the most likely + /// issue is that the data gets corrupted. pub const unsafe fn from_bytes_unchecked(src: Bytes) -> ByteString { Self(src) } @@ -84,8 +84,10 @@ impl ops::Deref for ByteString { #[inline] fn deref(&self) -> &str { - let b = self.0.as_ref(); - unsafe { str::from_utf8_unchecked(b) } + let bytes = self.0.as_ref(); + // SAFETY: + // UTF-8 validity is guaranteed at during construction. + unsafe { str::from_utf8_unchecked(bytes) } } } @@ -96,21 +98,24 @@ impl borrow::Borrow for ByteString { } impl From for ByteString { + #[inline] fn from(value: String) -> Self { Self(Bytes::from(value)) } } -impl<'a> From<&'a str> for ByteString { - fn from(value: &'a str) -> Self { +impl From<&str> for ByteString { + #[inline] + fn from(value: &str) -> Self { Self(Bytes::copy_from_slice(value.as_ref())) } } -impl<'a> TryFrom<&'a [u8]> for ByteString { +impl TryFrom<&[u8]> for ByteString { type Error = str::Utf8Error; - fn try_from(value: &'a [u8]) -> Result { + #[inline] + fn try_from(value: &[u8]) -> Result { let _ = str::from_utf8(value)?; Ok(ByteString(Bytes::copy_from_slice(value))) } @@ -119,15 +124,17 @@ impl<'a> TryFrom<&'a [u8]> for ByteString { impl TryFrom> for ByteString { type Error = str::Utf8Error; + #[inline] fn try_from(value: Vec) -> Result { - let _ = str::from_utf8(value.as_ref())?; - Ok(ByteString(Bytes::from(value))) + let buf = String::from_utf8(value).map_err(|err| err.utf8_error())?; + Ok(ByteString(Bytes::from(buf))) } } impl TryFrom for ByteString { type Error = str::Utf8Error; + #[inline] fn try_from(value: Bytes) -> Result { let _ = str::from_utf8(value.as_ref())?; Ok(ByteString(value)) @@ -137,8 +144,9 @@ impl TryFrom for ByteString { impl TryFrom for ByteString { type Error = str::Utf8Error; + #[inline] fn try_from(value: bytes::BytesMut) -> Result { - let _ = str::from_utf8(value.as_ref())?; + let _ = str::from_utf8(&value)?; Ok(ByteString(value.freeze())) } } @@ -146,10 +154,20 @@ impl TryFrom for ByteString { macro_rules! array_impls { ($($len:expr)+) => { $( - impl<'a> TryFrom<&'a [u8; $len]> for ByteString { + impl TryFrom<[u8; $len]> for ByteString { type Error = str::Utf8Error; - fn try_from(value: &'a [u8; $len]) -> Result { + #[inline] + fn try_from(value: [u8; $len]) -> Result { + ByteString::try_from(&value[..]) + } + } + + impl TryFrom<&[u8; $len]> for ByteString { + type Error = str::Utf8Error; + + #[inline] + fn try_from(value: &[u8; $len]) -> Result { ByteString::try_from(&value[..]) } } @@ -157,7 +175,7 @@ macro_rules! array_impls { } } -array_impls!(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16); +array_impls!(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32); impl fmt::Debug for ByteString { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -173,6 +191,8 @@ impl fmt::Display for ByteString { #[cfg(feature = "serde")] mod serde { + use alloc::string::String; + use serde::de::{Deserialize, Deserializer}; use serde::ser::{Serialize, Serializer}; @@ -201,16 +221,19 @@ mod serde { #[cfg(test)] mod test { + use alloc::borrow::ToOwned; + use core::hash::{Hash, Hasher}; + + use siphasher::sip::SipHasher; + use super::*; - use std::collections::hash_map::DefaultHasher; - use std::hash::{Hash, Hasher}; #[test] fn test_partial_eq() { let s: ByteString = ByteString::from_static("test"); assert_eq!(s, "test"); assert_eq!(s, *"test"); - assert_eq!(s, "test".to_string()); + assert_eq!(s, "test".to_owned()); } #[test] @@ -220,10 +243,10 @@ mod test { #[test] fn test_hash() { - let mut hasher1 = DefaultHasher::default(); + let mut hasher1 = SipHasher::default(); "str".hash(&mut hasher1); - let mut hasher2 = DefaultHasher::default(); + let mut hasher2 = SipHasher::default(); let s = ByteString::from_static("str"); s.hash(&mut hasher2); assert_eq!(hasher1.finish(), hasher2.finish()); @@ -231,7 +254,7 @@ mod test { #[test] fn test_from_string() { - let s: ByteString = "hello".to_string().into(); + let s: ByteString = "hello".to_owned().into(); assert_eq!(&s, "hello"); let t: &str = s.as_ref(); assert_eq!(t, "hello"); @@ -249,17 +272,25 @@ mod test { } #[test] - fn test_try_from_rbytes() { + fn test_try_from_slice() { let _ = ByteString::try_from(b"nice bytes").unwrap(); } + #[test] + fn test_try_from_array() { + assert_eq!( + ByteString::try_from([b'h', b'i']).unwrap(), + ByteString::from_static("hi") + ); + } + #[test] fn test_try_from_bytes() { let _ = ByteString::try_from(Bytes::from_static(b"nice bytes")).unwrap(); } #[test] - fn test_try_from_bytesmut() { + fn test_try_from_bytes_mut() { let _ = ByteString::try_from(bytes::BytesMut::from(&b"nice bytes"[..])).unwrap(); } From 6bcf6d81607eae01e995a749617b56105e20caa2 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Thu, 31 Dec 2020 02:21:50 +0000 Subject: [PATCH 24/73] use bytestring crate name as dir name --- {string => bytestring}/CHANGES.md | 0 {string => bytestring}/Cargo.toml | 2 +- {string => bytestring}/LICENSE-APACHE | 0 {string => bytestring}/LICENSE-MIT | 0 {string => bytestring}/src/lib.rs | 0 5 files changed, 1 insertion(+), 1 deletion(-) rename {string => bytestring}/CHANGES.md (100%) rename {string => bytestring}/Cargo.toml (90%) rename {string => bytestring}/LICENSE-APACHE (100%) rename {string => bytestring}/LICENSE-MIT (100%) rename {string => bytestring}/src/lib.rs (100%) diff --git a/string/CHANGES.md b/bytestring/CHANGES.md similarity index 100% rename from string/CHANGES.md rename to bytestring/CHANGES.md diff --git a/string/Cargo.toml b/bytestring/Cargo.toml similarity index 90% rename from string/Cargo.toml rename to bytestring/Cargo.toml index 4e41b5ea..3fc43fe8 100644 --- a/string/Cargo.toml +++ b/bytestring/Cargo.toml @@ -6,7 +6,7 @@ authors = [ "Rob Ede ", ] description = "An immutable UTF-8 encoded string using Bytes as storage" -keywords = ["string", "bytes", "utf8", "web-programming", "actix"] +keywords = ["string", "bytes", "utf8", "web", "actix"] categories = ["no-std", "web-programming"] homepage = "https://actix.rs" repository = "https://github.com/actix/actix-net.git" diff --git a/string/LICENSE-APACHE b/bytestring/LICENSE-APACHE similarity index 100% rename from string/LICENSE-APACHE rename to bytestring/LICENSE-APACHE diff --git a/string/LICENSE-MIT b/bytestring/LICENSE-MIT similarity index 100% rename from string/LICENSE-MIT rename to bytestring/LICENSE-MIT diff --git a/string/src/lib.rs b/bytestring/src/lib.rs similarity index 100% rename from string/src/lib.rs rename to bytestring/src/lib.rs From 93865de848d2da56e5a13c300dac73049cc985fb Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Thu, 31 Dec 2020 02:29:27 +0000 Subject: [PATCH 25/73] move router to actix-router --- Cargo.toml | 14 +++++++------- {router => actix-router}/CHANGES.md | 0 {router => actix-router}/Cargo.toml | 0 {router => actix-router}/LICENSE-APACHE | 0 {router => actix-router}/LICENSE-MIT | 0 {router => actix-router}/src/de.rs | 0 {router => actix-router}/src/lib.rs | 0 {router => actix-router}/src/path.rs | 0 {router => actix-router}/src/resource.rs | 0 {router => actix-router}/src/router.rs | 0 {router => actix-router}/src/url.rs | 0 11 files changed, 7 insertions(+), 7 deletions(-) rename {router => actix-router}/CHANGES.md (100%) rename {router => actix-router}/Cargo.toml (100%) rename {router => actix-router}/LICENSE-APACHE (100%) rename {router => actix-router}/LICENSE-MIT (100%) rename {router => actix-router}/src/de.rs (100%) rename {router => actix-router}/src/lib.rs (100%) rename {router => actix-router}/src/path.rs (100%) rename {router => actix-router}/src/resource.rs (100%) rename {router => actix-router}/src/router.rs (100%) rename {router => actix-router}/src/url.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index 13fc843e..be5bf339 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,27 +1,27 @@ [workspace] members = [ "actix-codec", - "actix-rt", "actix-macros", - "actix-service", + "actix-router", + "actix-rt", "actix-server", + "actix-service", "actix-threadpool", "actix-tls", "actix-tracing", "actix-utils", - "router", - "string", + "bytestring", ] [patch.crates-io] actix-codec = { path = "actix-codec" } -actix-rt = { path = "actix-rt" } actix-macros = { path = "actix-macros" } +actix-router = { path = "actix-router" } +actix-rt = { path = "actix-rt" } actix-server = { path = "actix-server" } actix-service = { path = "actix-service" } actix-threadpool = { path = "actix-threadpool" } actix-tls = { path = "actix-tls" } actix-tracing = { path = "actix-tracing" } actix-utils = { path = "actix-utils" } -actix-router = { path = "router" } -bytestring = { path = "string" } +bytestring = { path = "bytestring" } diff --git a/router/CHANGES.md b/actix-router/CHANGES.md similarity index 100% rename from router/CHANGES.md rename to actix-router/CHANGES.md diff --git a/router/Cargo.toml b/actix-router/Cargo.toml similarity index 100% rename from router/Cargo.toml rename to actix-router/Cargo.toml diff --git a/router/LICENSE-APACHE b/actix-router/LICENSE-APACHE similarity index 100% rename from router/LICENSE-APACHE rename to actix-router/LICENSE-APACHE diff --git a/router/LICENSE-MIT b/actix-router/LICENSE-MIT similarity index 100% rename from router/LICENSE-MIT rename to actix-router/LICENSE-MIT diff --git a/router/src/de.rs b/actix-router/src/de.rs similarity index 100% rename from router/src/de.rs rename to actix-router/src/de.rs diff --git a/router/src/lib.rs b/actix-router/src/lib.rs similarity index 100% rename from router/src/lib.rs rename to actix-router/src/lib.rs diff --git a/router/src/path.rs b/actix-router/src/path.rs similarity index 100% rename from router/src/path.rs rename to actix-router/src/path.rs diff --git a/router/src/resource.rs b/actix-router/src/resource.rs similarity index 100% rename from router/src/resource.rs rename to actix-router/src/resource.rs diff --git a/router/src/router.rs b/actix-router/src/router.rs similarity index 100% rename from router/src/router.rs rename to actix-router/src/router.rs diff --git a/router/src/url.rs b/actix-router/src/url.rs similarity index 100% rename from router/src/url.rs rename to actix-router/src/url.rs From 296294061fcf89d3fab6f79bf24fc95d2609cbab Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Thu, 31 Dec 2020 02:52:55 +0000 Subject: [PATCH 26/73] update readme --- README.md | 66 ++++++++++--------------------------------------------- 1 file changed, 11 insertions(+), 55 deletions(-) diff --git a/README.md b/README.md index 1f7f063a..95500e43 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,12 @@ -# Actix net [![codecov](https://codecov.io/gh/actix/actix-net/branch/master/graph/badge.svg)](https://codecov.io/gh/actix/actix-net) [![Join the chat at https://gitter.im/actix/actix](https://badges.gitter.im/actix/actix.svg)](https://gitter.im/actix/actix?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +# Actix Net -Actix net - framework for composable network services +> A collection of lower-level libraries for composable network services. + +![Apache 2.0 or MIT licensed](https://img.shields.io/crates/l/actix-server) +[![codecov](https://codecov.io/gh/actix/actix-net/branch/master/graph/badge.svg)](https://codecov.io/gh/actix/actix-net) +[![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x) ## Build statuses - | Platform | Build Status | | ---------------- | ------------ | | Linux | [![build status](https://github.com/actix/actix-net/workflows/CI%20%28Linux%29/badge.svg?branch=master&event=push)](https://github.com/actix/actix-net/actions?query=workflow%3A"CI+(Linux)") | @@ -11,59 +14,13 @@ Actix net - framework for composable network services | Windows | [![build status](https://github.com/actix/actix-net/workflows/CI%20%28Windows%29/badge.svg?branch=master&event=push)](https://github.com/actix/actix-net/actions?query=workflow%3A"CI+(Windows)") | | Windows (MinGW) | [![build status](https://github.com/actix/actix-net/workflows/CI%20%28Windows-mingw%29/badge.svg?branch=master&event=push)](https://github.com/actix/actix-net/actions?query=workflow%3A"CI+(Windows-mingw)") | -## Documentation & community resources - -* [Chat on Gitter](https://gitter.im/actix/actix) -* Minimum supported Rust version: 1.46 or later - ## Example +See `actix-server/examples` and `actix-tls/examples` for some basic examples. -```rust -fn main() -> io::Result<()> { - // load ssl keys - let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap(); - builder.set_private_key_file("./examples/key.pem", SslFiletype::PEM).unwrap(); - builder.set_certificate_chain_file("./examples/cert.pem").unwrap(); - let acceptor = builder.build(); - - let num = Arc::new(AtomicUsize::new(0)); - - // bind socket address and start workers. By default server uses number of - // available logical cpu as threads count. actix net start separate - // instances of service pipeline in each worker. - Server::build() - .bind( - // configure service pipeline - "basic", "0.0.0.0:8443", - move || { - let num = num.clone(); - let acceptor = acceptor.clone(); - - // construct transformation pipeline - pipeline( - // service for converting incoming TcpStream to a SslStream - fn_service(move |stream: actix_rt::net::TcpStream| async move { - SslAcceptorExt::accept_async(&acceptor, stream.into_parts().0).await - .map_err(|e| println!("Openssl error: {}", e)) - })) - // .and_then() combinator chains result of previos service call to argument - /// for next service calll. in this case, on success we chain - /// ssl stream to the `logger` service. - .and_then(fn_service(logger)) - // Next service counts number of connections - .and_then(move |_| { - let num = num.fetch_add(1, Ordering::Relaxed); - println!("got ssl connection {:?}", num); - future::ok(()) - }) - }, - )? - .run() -} -``` +### MSRV +This repo's Minimum Supported Rust Version (MSRV) is 1.46.0. ## License - This project is licensed under either of * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0)) @@ -73,6 +30,5 @@ at your option. ## Code of Conduct -Contribution to the actix-net crate is organized under the terms of the -Contributor Covenant, the maintainer of actix-net, @fafhrd91, promises to -intervene to uphold that code of conduct. +Contribution to the actix-net repo is organized under the terms of the Contributor Covenant. +The Actix team promises to intervene to uphold that code of conduct. From 5285656bdc7baa3a2aaedf3985083a23c1009bfc Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sun, 3 Jan 2021 04:39:37 +0000 Subject: [PATCH 27/73] prepare next beta releases --- actix-codec/CHANGES.md | 2 +- actix-router/CHANGES.md | 2 +- actix-rt/CHANGES.md | 2 +- actix-server/CHANGES.md | 10 ++++++++-- actix-server/Cargo.toml | 4 ++-- actix-service/CHANGES.md | 6 +++++- actix-service/Cargo.toml | 2 +- actix-tls/CHANGES.md | 6 ++++++ actix-tls/Cargo.toml | 6 +++--- actix-tracing/Cargo.toml | 2 +- actix-utils/CHANGES.md | 2 +- actix-utils/Cargo.toml | 2 +- bytestring/CHANGES.md | 2 +- 13 files changed, 32 insertions(+), 16 deletions(-) diff --git a/actix-codec/CHANGES.md b/actix-codec/CHANGES.md index 0c26c085..f6102cbf 100644 --- a/actix-codec/CHANGES.md +++ b/actix-codec/CHANGES.md @@ -1,6 +1,6 @@ # Changes -## Unreleased - 2020-xx-xx +## Unreleased - 2021-xx-xx ## 0.4.0-beta.1 - 2020-12-28 diff --git a/actix-router/CHANGES.md b/actix-router/CHANGES.md index bb148c87..85ac7a78 100644 --- a/actix-router/CHANGES.md +++ b/actix-router/CHANGES.md @@ -1,6 +1,6 @@ # Changes -## Unreleased - 2020-xx-xx +## Unreleased - 2021-xx-xx ## 0.2.5 - 2020-09-20 diff --git a/actix-rt/CHANGES.md b/actix-rt/CHANGES.md index 35409616..ec12e569 100644 --- a/actix-rt/CHANGES.md +++ b/actix-rt/CHANGES.md @@ -1,6 +1,6 @@ # Changes -## Unreleased - 2020-xx-xx +## Unreleased - 2021-xx-xx ## 2.0.0-beta.1 - 2020-12-28 diff --git a/actix-server/CHANGES.md b/actix-server/CHANGES.md index f3b7c93c..d02643e6 100644 --- a/actix-server/CHANGES.md +++ b/actix-server/CHANGES.md @@ -1,7 +1,13 @@ # Changes -## Unreleased - 2020-xx-xx -* Merge `actix-testing` to `actix-server` as `test_server` mod. +## Unreleased - 2021-xx-xx + + +## 2.0.0-beta.2 - 2021-01-03 +* Merge `actix-testing` to `actix-server` as `test_server` mod. [#242] + +[#242]: https://github.com/actix/actix-net/pull/242 + ## 2.0.0-beta.1 - 2020-12-28 * Added explicit info log message on accept queue pause. [#215] diff --git a/actix-server/Cargo.toml b/actix-server/Cargo.toml index 0bdd5d23..2ceeaa21 100644 --- a/actix-server/Cargo.toml +++ b/actix-server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-server" -version = "2.0.0-beta.1" +version = "2.0.0-beta.2" authors = [ "Nikolay Kim ", "fakeshadow <24548779@qq.com>", @@ -25,7 +25,7 @@ default = [] [dependencies] actix-codec = "0.4.0-beta.1" actix-rt = "2.0.0-beta.1" -actix-service = "2.0.0-beta.1" +actix-service = "2.0.0-beta.2" actix-utils = "3.0.0-beta.1" futures-core = { version = "0.3.7", default-features = false } diff --git a/actix-service/CHANGES.md b/actix-service/CHANGES.md index 1cbf414e..bff9bda5 100644 --- a/actix-service/CHANGES.md +++ b/actix-service/CHANGES.md @@ -1,6 +1,10 @@ # Changes -## Unreleased - 2020-xx-xx +## Unreleased - 2021-xx-xx + + +## 2.0.0-beta.2 - 2021-01-03 +* Remove redundant type parameter from `map_config`. ## 2.0.0-beta.1 - 2020-12-28 diff --git a/actix-service/Cargo.toml b/actix-service/Cargo.toml index 708fb5e6..869881e9 100644 --- a/actix-service/Cargo.toml +++ b/actix-service/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-service" -version = "2.0.0-beta.1" +version = "2.0.0-beta.2" authors = [ "Nikolay Kim ", "Rob Ede ", diff --git a/actix-tls/CHANGES.md b/actix-tls/CHANGES.md index dcb477d8..1a7ef7a7 100644 --- a/actix-tls/CHANGES.md +++ b/actix-tls/CHANGES.md @@ -3,6 +3,12 @@ ## Unreleased - 2021-xx-xx +## 3.0.0-beta.2 - 2021-xx-xx +* Depend on stable trust-dns packages. [#204] + +[#204]: https://github.com/actix/actix-net/pull/204 + + ## 3.0.0-beta.1 - 2020-12-29 * Move acceptors under `accept` module. [#238] * Merge `actix-connect` crate under `connect` module. [#238] diff --git a/actix-tls/Cargo.toml b/actix-tls/Cargo.toml index 017f17fe..cfd6960a 100644 --- a/actix-tls/Cargo.toml +++ b/actix-tls/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-tls" -version = "3.0.0-beta.1" +version = "3.0.0-beta.2" authors = ["Nikolay Kim "] description = "TLS acceptor and connector services for Actix ecosystem" keywords = ["network", "tls", "ssl", "async", "transport"] @@ -46,7 +46,7 @@ uri = ["http"] [dependencies] actix-codec = "0.4.0-beta.1" actix-rt = "2.0.0-beta.1" -actix-service = "2.0.0-beta.1" +actix-service = "2.0.0-beta.2" actix-utils = "3.0.0-beta.1" derive_more = "0.99.5" @@ -76,7 +76,7 @@ tls-native-tls = { package = "native-tls", version = "0.2", optional = true } tokio-native-tls = { version = "0.3", optional = true } [dev-dependencies] -actix-server = "2.0.0-beta.1" +actix-server = "2.0.0-beta.2" bytes = "1" env_logger = "0.8" futures-util = { version = "0.3.7", default-features = false, features = ["sink"] } diff --git a/actix-tracing/Cargo.toml b/actix-tracing/Cargo.toml index 720e7ad5..547f2d16 100644 --- a/actix-tracing/Cargo.toml +++ b/actix-tracing/Cargo.toml @@ -16,7 +16,7 @@ name = "actix_tracing" path = "src/lib.rs" [dependencies] -actix-service = "2.0.0-beta.1" +actix-service = "2.0.0-beta.2" futures-util = { version = "0.3.4", default-features = false } tracing = "0.1" diff --git a/actix-utils/CHANGES.md b/actix-utils/CHANGES.md index 6c7025b5..2504f012 100644 --- a/actix-utils/CHANGES.md +++ b/actix-utils/CHANGES.md @@ -1,6 +1,6 @@ # Changes -## Unreleased - 2020-xx-xx +## Unreleased - 2021-xx-xx ## 3.0.0-beta.1 - 2020-12-28 diff --git a/actix-utils/Cargo.toml b/actix-utils/Cargo.toml index 3dded4f0..aab17fca 100644 --- a/actix-utils/Cargo.toml +++ b/actix-utils/Cargo.toml @@ -18,7 +18,7 @@ path = "src/lib.rs" [dependencies] actix-codec = "0.4.0-beta.1" actix-rt = "2.0.0-beta.1" -actix-service = "2.0.0-beta.1" +actix-service = "2.0.0-beta.2" futures-core = { version = "0.3.7", default-features = false } futures-sink = { version = "0.3.7", default-features = false } diff --git a/bytestring/CHANGES.md b/bytestring/CHANGES.md index 90b32769..217eb8d0 100644 --- a/bytestring/CHANGES.md +++ b/bytestring/CHANGES.md @@ -1,6 +1,6 @@ # Changes -## Unreleased - 2020-xx-xx +## Unreleased - 2021-xx-xx ## 1.0.0 - 2020-12-31 From 147c4f4f2cf81ba4b4efa36b7bc16cf0790fab8f Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sun, 3 Jan 2021 04:42:08 +0000 Subject: [PATCH 28/73] test bytestring with ahash --- bytestring/Cargo.toml | 2 +- bytestring/src/lib.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bytestring/Cargo.toml b/bytestring/Cargo.toml index 3fc43fe8..6af76031 100644 --- a/bytestring/Cargo.toml +++ b/bytestring/Cargo.toml @@ -24,4 +24,4 @@ serde = { version = "1.0", optional = true } [dev-dependencies] serde_json = "1.0" -siphasher = "0.3" +ahash = { version = "0.6", default-features = false } diff --git a/bytestring/src/lib.rs b/bytestring/src/lib.rs index 11c5a75a..63ba92ed 100644 --- a/bytestring/src/lib.rs +++ b/bytestring/src/lib.rs @@ -224,7 +224,7 @@ mod test { use alloc::borrow::ToOwned; use core::hash::{Hash, Hasher}; - use siphasher::sip::SipHasher; + use ahash::AHasher; use super::*; @@ -243,10 +243,10 @@ mod test { #[test] fn test_hash() { - let mut hasher1 = SipHasher::default(); + let mut hasher1 = AHasher::default(); "str".hash(&mut hasher1); - let mut hasher2 = SipHasher::default(); + let mut hasher2 = AHasher::default(); let s = ByteString::from_static("str"); s.hash(&mut hasher2); assert_eq!(hasher1.finish(), hasher2.finish()); From a1982bdbadb9171279ae383b6774e06593b35c3a Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Mon, 4 Jan 2021 02:16:57 +0800 Subject: [PATCH 29/73] add actix-rt::task (#245) --- actix-rt/CHANGES.md | 2 ++ actix-rt/Cargo.toml | 2 +- actix-rt/src/lib.rs | 5 +++++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/actix-rt/CHANGES.md b/actix-rt/CHANGES.md index ec12e569..6b15c6ed 100644 --- a/actix-rt/CHANGES.md +++ b/actix-rt/CHANGES.md @@ -1,7 +1,9 @@ # Changes ## Unreleased - 2021-xx-xx +* Add `task` mod with re-export of `tokio::task::{spawn_blocking, yield_now, JoinHandle}` [#245] +[#245]: https://github.com/actix/actix-net/pull/245 ## 2.0.0-beta.1 - 2020-12-28 ### Added diff --git a/actix-rt/Cargo.toml b/actix-rt/Cargo.toml index 317345d4..be70d374 100644 --- a/actix-rt/Cargo.toml +++ b/actix-rt/Cargo.toml @@ -18,4 +18,4 @@ path = "src/lib.rs" [dependencies] actix-macros = "0.1.0" -tokio = { version = "1", features = ["rt", "net", "signal", "sync", "time"] } +tokio = { version = "1", features = ["rt", "net", "parking_lot", "signal", "sync", "time"] } diff --git a/actix-rt/src/lib.rs b/actix-rt/src/lib.rs index 440fa33c..6ccd588f 100644 --- a/actix-rt/src/lib.rs +++ b/actix-rt/src/lib.rs @@ -63,3 +63,8 @@ pub mod time { pub use tokio::time::{sleep, sleep_until, Sleep}; pub use tokio::time::{timeout, Timeout}; } + +/// task management. +pub mod task { + pub use tokio::task::{spawn_blocking, yield_now, JoinHandle}; +} From f751cf5acb206e5ba11f8c8bae216468bde527f8 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sat, 9 Jan 2021 14:13:16 +0000 Subject: [PATCH 30/73] use convert err on forward_ready! (#246) --- actix-macros/src/lib.rs | 2 +- actix-router/CHANGES.md | 3 +++ actix-router/Cargo.toml | 2 +- actix-rt/src/runtime.rs | 2 +- actix-rt/src/system.rs | 4 ++-- actix-server/CHANGES.md | 3 +++ actix-server/src/builder.rs | 5 ----- actix-server/src/test_server.rs | 9 +++------ actix-server/tests/test_server.rs | 8 ++++---- actix-service/CHANGES.md | 3 +++ actix-service/src/fn_service.rs | 4 ++-- actix-service/src/lib.rs | 10 ++++++---- actix-service/src/transform.rs | 8 +++----- actix-tracing/src/lib.rs | 2 +- actix-utils/src/timeout.rs | 4 +--- 15 files changed, 34 insertions(+), 35 deletions(-) diff --git a/actix-macros/src/lib.rs b/actix-macros/src/lib.rs index fc655a64..dd791578 100644 --- a/actix-macros/src/lib.rs +++ b/actix-macros/src/lib.rs @@ -11,7 +11,7 @@ use quote::quote; /// /// ## Usage /// -/// ```rust +/// ``` /// #[actix_rt::main] /// async fn main() { /// println!("Hello world"); diff --git a/actix-router/CHANGES.md b/actix-router/CHANGES.md index 85ac7a78..e9d4c3a2 100644 --- a/actix-router/CHANGES.md +++ b/actix-router/CHANGES.md @@ -1,6 +1,9 @@ # Changes ## Unreleased - 2021-xx-xx +* Use `bytestring` version range compatible with Bytes v1.0. [#246] + +[#246]: https://github.com/actix/actix-net/pull/246 ## 0.2.5 - 2020-09-20 diff --git a/actix-router/Cargo.toml b/actix-router/Cargo.toml index 6fc6ecc8..74e5fdab 100644 --- a/actix-router/Cargo.toml +++ b/actix-router/Cargo.toml @@ -20,7 +20,7 @@ default = ["http"] [dependencies] regex = "1.3.1" serde = "1.0.104" -bytestring = "0.1.2" +bytestring = ">=0.1.5, <2" log = "0.4.8" http = { version = "0.2.2", optional = true } diff --git a/actix-rt/src/runtime.rs b/actix-rt/src/runtime.rs index a72f492c..63653e13 100644 --- a/actix-rt/src/runtime.rs +++ b/actix-rt/src/runtime.rs @@ -41,7 +41,7 @@ impl Runtime { /// /// # Examples /// - /// ```rust,ignore + /// ```ignore /// # use futures::{future, Future, Stream}; /// use actix_rt::Runtime; /// diff --git a/actix-rt/src/system.rs b/actix-rt/src/system.rs index 1fbbc0ee..3a199da7 100644 --- a/actix-rt/src/system.rs +++ b/actix-rt/src/system.rs @@ -70,7 +70,7 @@ impl System { /// /// # Examples /// - /// ```rust,ignore + /// ```ignore /// use tokio::{runtime::Runtime, task::LocalSet}; /// use actix_rt::System; /// use futures_util::future::try_join_all; @@ -139,7 +139,7 @@ impl System { /// /// # Examples /// - /// ```rust,ignore + /// ```ignore /// use tokio::runtime::Runtime; /// use actix_rt::System; /// use futures_util::future::try_join_all; diff --git a/actix-server/CHANGES.md b/actix-server/CHANGES.md index d02643e6..70728064 100644 --- a/actix-server/CHANGES.md +++ b/actix-server/CHANGES.md @@ -1,6 +1,9 @@ # Changes ## Unreleased - 2021-xx-xx +* Hidden `ServerBuilder::start` method has been removed. Use `ServerBuilder::run`. [#246] + +[#246]: https://github.com/actix/actix-net/pull/246 ## 2.0.0-beta.2 - 2021-01-03 diff --git a/actix-server/src/builder.rs b/actix-server/src/builder.rs index 51dd0eda..13c164f1 100644 --- a/actix-server/src/builder.rs +++ b/actix-server/src/builder.rs @@ -252,11 +252,6 @@ impl ServerBuilder { Ok(self) } - #[doc(hidden)] - pub fn start(self) -> Server { - self.run() - } - /// Starts processing incoming connections and return server controller. pub fn run(mut self) -> Server { if self.sockets.is_empty() { diff --git a/actix-server/src/test_server.rs b/actix-server/src/test_server.rs index 627cc675..4b7f7873 100644 --- a/actix-server/src/test_server.rs +++ b/actix-server/src/test_server.rs @@ -12,7 +12,7 @@ use crate::{Server, ServerBuilder, ServiceFactory}; /// /// # Examples /// -/// ```rust +/// ``` /// use actix_service::fn_service; /// use actix_server::TestServer; /// @@ -49,10 +49,7 @@ impl TestServer { // run server in separate thread thread::spawn(move || { let sys = System::new("actix-test-server"); - factory(Server::build()) - .workers(1) - .disable_signals() - .start(); + factory(Server::build()).workers(1).disable_signals().run(); tx.send(System::current()).unwrap(); sys.run() @@ -83,7 +80,7 @@ impl TestServer { .unwrap() .workers(1) .disable_signals() - .start(); + .run(); tx.send((System::current(), local_addr)).unwrap(); }); sys.run() diff --git a/actix-server/tests/test_server.rs b/actix-server/tests/test_server.rs index 2604df74..8d93ba0b 100644 --- a/actix-server/tests/test_server.rs +++ b/actix-server/tests/test_server.rs @@ -28,7 +28,7 @@ fn test_bind() { .disable_signals() .bind("test", addr, move || fn_service(|_| ok::<_, ()>(()))) .unwrap() - .start() + .run() })); let _ = tx.send((srv, actix_rt::System::current())); let _ = sys.run(); @@ -55,7 +55,7 @@ fn test_listen() { .workers(1) .listen("test", lst, move || fn_service(|_| ok::<_, ()>(()))) .unwrap() - .start(); + .run(); let _ = tx.send(actix_rt::System::current()); }); let _ = sys.run(); @@ -94,7 +94,7 @@ fn test_start() { }) }) .unwrap() - .start() + .run() })); let _ = tx.send((srv, actix_rt::System::current())); @@ -173,7 +173,7 @@ fn test_configure() { }) .unwrap() .workers(1) - .start() + .run() })); let _ = tx.send((srv, actix_rt::System::current())); let _ = sys.run(); diff --git a/actix-service/CHANGES.md b/actix-service/CHANGES.md index bff9bda5..53398894 100644 --- a/actix-service/CHANGES.md +++ b/actix-service/CHANGES.md @@ -1,6 +1,9 @@ # Changes ## Unreleased - 2021-xx-xx +* The `forward_ready!` macro converts errors. [#246] + +[#246]: https://github.com/actix/actix-net/pull/246 ## 2.0.0-beta.2 - 2021-01-03 diff --git a/actix-service/src/fn_service.rs b/actix-service/src/fn_service.rs index 9f7d1eb7..5f09c580 100644 --- a/actix-service/src/fn_service.rs +++ b/actix-service/src/fn_service.rs @@ -17,7 +17,7 @@ where /// /// # Example /// -/// ```rust +/// ``` /// use std::io; /// use actix_service::{fn_factory, fn_service, Service, ServiceFactory}; /// use futures_util::future::ok; @@ -67,7 +67,7 @@ where /// /// # Example /// -/// ```rust +/// ``` /// use std::io; /// use actix_service::{fn_factory_with_config, fn_service, Service, ServiceFactory}; /// use futures_util::future::ok; diff --git a/actix-service/src/lib.rs b/actix-service/src/lib.rs index 7bf979e5..b774d0d9 100644 --- a/actix-service/src/lib.rs +++ b/actix-service/src/lib.rs @@ -48,7 +48,7 @@ use self::ready::{err, ok, ready, Ready}; /// replies. You can think about a service as a function with one argument that returns some result /// asynchronously. Conceptually, the operation looks like this: /// -/// ```rust,ignore +/// ```ignore /// async fn(Request) -> Result /// ``` /// @@ -60,7 +60,7 @@ use self::ready::{err, ok, ready, Ready}; /// simple API surfaces. This leads to simpler design of each service, improves test-ability and /// makes composition easier. /// -/// ```rust,ignore +/// ```ignore /// struct MyService; /// /// impl Service for MyService { @@ -78,7 +78,7 @@ use self::ready::{err, ok, ready, Ready}; /// Sometimes it is not necessary to implement the Service trait. For example, the above service /// could be rewritten as a simple function and passed to [fn_service](fn_service()). /// -/// ```rust,ignore +/// ```ignore /// async fn my_service(req: u8) -> Result; /// ``` pub trait Service { @@ -327,7 +327,9 @@ macro_rules! forward_ready { &mut self, cx: &mut ::core::task::Context<'_>, ) -> ::core::task::Poll> { - self.$field.poll_ready(cx) + self.$field + .poll_ready(cx) + .map_err(::core::convert::Into::into) } }; } diff --git a/actix-service/src/transform.rs b/actix-service/src/transform.rs index 76e4547a..7d707d98 100644 --- a/actix-service/src/transform.rs +++ b/actix-service/src/transform.rs @@ -30,7 +30,7 @@ where /// /// For example, timeout transform: /// -/// ```rust,ignore +/// ```ignore /// pub struct Timeout { /// service: S, /// timeout: Duration, @@ -45,9 +45,7 @@ where /// type Error = TimeoutError; /// type Future = TimeoutServiceResponse; /// -/// fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { -/// ready!(self.service.poll_ready(cx)).map_err(TimeoutError::Service) -/// } +/// actix_service::forward_ready!(service); /// /// fn call(&mut self, req: S::Request) -> Self::Future { /// TimeoutServiceResponse { @@ -69,7 +67,7 @@ where /// /// Factory for `Timeout` middleware from the above example could look like this: /// -/// ```rust,,ignore +/// ```ignore /// pub struct TimeoutTransform { /// timeout: Duration, /// } diff --git a/actix-tracing/src/lib.rs b/actix-tracing/src/lib.rs index 6d37d9b3..77a16287 100644 --- a/actix-tracing/src/lib.rs +++ b/actix-tracing/src/lib.rs @@ -94,7 +94,7 @@ where /// is passed in a reference to the request being handled by the service. /// /// For example: -/// ```rust,ignore +/// ```ignore /// let traced_service = trace( /// web_service, /// |req: &Request| Some(span!(Level::INFO, "request", req.id)) diff --git a/actix-utils/src/timeout.rs b/actix-utils/src/timeout.rs index 85e328b9..a308b0db 100644 --- a/actix-utils/src/timeout.rs +++ b/actix-utils/src/timeout.rs @@ -149,9 +149,7 @@ where type Error = TimeoutError; type Future = TimeoutServiceResponse; - fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { - self.service.poll_ready(cx).map_err(TimeoutError::Service) - } + actix_service::forward_ready!(service); fn call(&mut self, request: Req) -> Self::Future { TimeoutServiceResponse { From a95afe2800913faf03fa98389a6f56a32ae79980 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sat, 9 Jan 2021 14:18:20 +0000 Subject: [PATCH 31/73] prepare router release 0.2.6 --- actix-router/CHANGES.md | 3 +++ actix-router/Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/actix-router/CHANGES.md b/actix-router/CHANGES.md index e9d4c3a2..df93e5be 100644 --- a/actix-router/CHANGES.md +++ b/actix-router/CHANGES.md @@ -1,6 +1,9 @@ # Changes ## Unreleased - 2021-xx-xx + + +## 0.2.6 - 2021-01-09 * Use `bytestring` version range compatible with Bytes v1.0. [#246] [#246]: https://github.com/actix/actix-net/pull/246 diff --git a/actix-router/Cargo.toml b/actix-router/Cargo.toml index 74e5fdab..4277cabf 100644 --- a/actix-router/Cargo.toml +++ b/actix-router/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-router" -version = "0.2.5" +version = "0.2.6" authors = ["Nikolay Kim "] description = "Resource path matching library" keywords = ["actix"] From 46bfe5de36b17bf0f262aaf2a32f51ff297d3b08 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sat, 9 Jan 2021 14:28:33 +0000 Subject: [PATCH 32/73] prepare service 2.0.0-beta.3 release --- actix-service/CHANGES.md | 3 +++ actix-service/Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/actix-service/CHANGES.md b/actix-service/CHANGES.md index 53398894..4bf41f0e 100644 --- a/actix-service/CHANGES.md +++ b/actix-service/CHANGES.md @@ -1,6 +1,9 @@ # Changes ## Unreleased - 2021-xx-xx + + +## 2.0.0-beta.3 - 2021-01-09 * The `forward_ready!` macro converts errors. [#246] [#246]: https://github.com/actix/actix-net/pull/246 diff --git a/actix-service/Cargo.toml b/actix-service/Cargo.toml index 869881e9..afce1a41 100644 --- a/actix-service/Cargo.toml +++ b/actix-service/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-service" -version = "2.0.0-beta.2" +version = "2.0.0-beta.3" authors = [ "Nikolay Kim ", "Rob Ede ", From b0a8f8411b9586f4e51b44832c4ccf870d3e2f72 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sat, 9 Jan 2021 14:56:07 +0000 Subject: [PATCH 33/73] prepare macros 0.2.0-beta.1 release --- actix-macros/CHANGES.md | 18 ++++++++++++------ actix-macros/Cargo.toml | 2 +- actix-rt/Cargo.toml | 2 +- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/actix-macros/CHANGES.md b/actix-macros/CHANGES.md index 9f5977c4..065d8e30 100644 --- a/actix-macros/CHANGES.md +++ b/actix-macros/CHANGES.md @@ -1,13 +1,19 @@ -# CHANGES +# Changes -## 0.1.3 - 2020-12-3 +## Unreleased - 2021-xx-xx + + +## 0.2.0-beta.1 - 2021-01-09 +* Remove `actix-reexport` feature. + + +## 0.1.3 - 2020-12-03 +* Add `actix-reexport` feature. [#218] + +[#218]: https://github.com/actix/actix-net/pull/218 -* Add `actix-reexport` feature ## 0.1.2 - 2020-05-18 - -### Changed - * Forward actix_rt::test arguments to test function [#127] [#127]: https://github.com/actix/actix-net/pull/127 diff --git a/actix-macros/Cargo.toml b/actix-macros/Cargo.toml index 1181ace5..d88b0c2d 100644 --- a/actix-macros/Cargo.toml +++ b/actix-macros/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-macros" -version = "0.1.3" +version = "0.2.0-beta.1" authors = ["Nikolay Kim "] description = "Actix runtime macros" repository = "https://github.com/actix/actix-net" diff --git a/actix-rt/Cargo.toml b/actix-rt/Cargo.toml index be70d374..1386e121 100644 --- a/actix-rt/Cargo.toml +++ b/actix-rt/Cargo.toml @@ -16,6 +16,6 @@ name = "actix_rt" path = "src/lib.rs" [dependencies] -actix-macros = "0.1.0" +actix-macros = "0.2.0-beta.1" tokio = { version = "1", features = ["rt", "net", "parking_lot", "signal", "sync", "time"] } From d4c46b7da9db2c14670ce65d5804ed3012361928 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sat, 9 Jan 2021 14:58:15 +0000 Subject: [PATCH 34/73] fix macros code --- actix-macros/src/lib.rs | 27 ++++++++------------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/actix-macros/src/lib.rs b/actix-macros/src/lib.rs index dd791578..60f177fa 100644 --- a/actix-macros/src/lib.rs +++ b/actix-macros/src/lib.rs @@ -36,25 +36,14 @@ pub fn main(_: TokenStream, item: TokenStream) -> TokenStream { sig.asyncness = None; - if cfg!(feature = "actix-reexport") { - (quote! { - #(#attrs)* - #vis #sig { - actix::System::new(stringify!(#name)) - .block_on(async move { #body }) - } - }) - .into() - } else { - (quote! { - #(#attrs)* - #vis #sig { - actix_rt::System::new(stringify!(#name)) - .block_on(async move { #body }) - } - }) - .into() - } + (quote! { + #(#attrs)* + #vis #sig { + actix_rt::System::new(stringify!(#name)) + .block_on(async move { #body }) + } + }) + .into() } /// Marks async test function to be executed by actix runtime. From f07d8077075f4310e593779973c7e975d3e84b1f Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sat, 9 Jan 2021 15:04:55 +0000 Subject: [PATCH 35/73] remove actix-threadpool crate --- Cargo.toml | 2 - actix-threadpool/CHANGES.md | 49 ----------------- actix-threadpool/Cargo.toml | 27 ---------- actix-threadpool/LICENSE-APACHE | 1 - actix-threadpool/LICENSE-MIT | 1 - actix-threadpool/src/lib.rs | 96 --------------------------------- 6 files changed, 176 deletions(-) delete mode 100644 actix-threadpool/CHANGES.md delete mode 100644 actix-threadpool/Cargo.toml delete mode 120000 actix-threadpool/LICENSE-APACHE delete mode 120000 actix-threadpool/LICENSE-MIT delete mode 100644 actix-threadpool/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index be5bf339..78e54d35 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,6 @@ members = [ "actix-rt", "actix-server", "actix-service", - "actix-threadpool", "actix-tls", "actix-tracing", "actix-utils", @@ -20,7 +19,6 @@ actix-router = { path = "actix-router" } actix-rt = { path = "actix-rt" } actix-server = { path = "actix-server" } actix-service = { path = "actix-service" } -actix-threadpool = { path = "actix-threadpool" } actix-tls = { path = "actix-tls" } actix-tracing = { path = "actix-tracing" } actix-utils = { path = "actix-utils" } diff --git a/actix-threadpool/CHANGES.md b/actix-threadpool/CHANGES.md deleted file mode 100644 index 9b481fbf..00000000 --- a/actix-threadpool/CHANGES.md +++ /dev/null @@ -1,49 +0,0 @@ -# Changes - -## [0.3.3] - 2020-07-14 - -### Changed - -* Update parking_lot to 0.11 - -## [0.3.2] - 2020-05-20 - -## Added - -* Implement `std::error::Error` for `BlockingError` [#120] - -[#120]: https://github.com/actix/actix-net/pull/120 - -## [0.3.1] - 2019-12-12 - -### Changed - -* Update parking_lot to 0.10 - -## [0.3.0] - 2019-12-02 - -### Changed - -* Expect `Result` type as a function return type - -## [0.2.0] - 2019-11-21 - -### Changed - -* Migrate to `std::future` - -## [0.1.2] - 2019-08-05 - -### Changed - -* Update `derive_more` to 0.15 - -* Update `parking_lot` to 0.9 - -## [0.1.1] - 2019-06-05 - -* Update parking_lot - -## [0.1.0] - 2019-03-28 - -* Move threadpool to separate crate diff --git a/actix-threadpool/Cargo.toml b/actix-threadpool/Cargo.toml deleted file mode 100644 index 3a633d62..00000000 --- a/actix-threadpool/Cargo.toml +++ /dev/null @@ -1,27 +0,0 @@ -[package] -name = "actix-threadpool" -version = "0.3.3" -authors = ["Nikolay Kim "] -description = "Actix thread pool for sync code" -keywords = ["actix", "network", "framework", "async", "futures"] -homepage = "https://actix.rs" -repository = "https://github.com/actix/actix-net.git" -documentation = "https://docs.rs/actix-threadpool/" -categories = ["network-programming", "asynchronous"] -license = "MIT OR Apache-2.0" -exclude = [".gitignore", ".travis.yml", ".cargo/config", "appveyor.yml"] -edition = "2018" -workspace = ".." - -[lib] -name = "actix_threadpool" -path = "src/lib.rs" - -[dependencies] -derive_more = "0.99.2" -futures-channel = "0.3.7" -parking_lot = "0.11" -lazy_static = "1.3" -log = "0.4" -num_cpus = "1.10" -threadpool = "1.7" diff --git a/actix-threadpool/LICENSE-APACHE b/actix-threadpool/LICENSE-APACHE deleted file mode 120000 index 965b606f..00000000 --- a/actix-threadpool/LICENSE-APACHE +++ /dev/null @@ -1 +0,0 @@ -../LICENSE-APACHE \ No newline at end of file diff --git a/actix-threadpool/LICENSE-MIT b/actix-threadpool/LICENSE-MIT deleted file mode 120000 index 76219eb7..00000000 --- a/actix-threadpool/LICENSE-MIT +++ /dev/null @@ -1 +0,0 @@ -../LICENSE-MIT \ No newline at end of file diff --git a/actix-threadpool/src/lib.rs b/actix-threadpool/src/lib.rs deleted file mode 100644 index 2fda28ef..00000000 --- a/actix-threadpool/src/lib.rs +++ /dev/null @@ -1,96 +0,0 @@ -//! Thread pool for blocking operations - -#![deny(rust_2018_idioms, nonstandard_style)] -#![doc(html_logo_url = "https://actix.rs/img/logo.png")] -#![doc(html_favicon_url = "https://actix.rs/favicon.ico")] - -use std::fmt; -use std::future::Future; -use std::pin::Pin; -use std::task::{Context, Poll}; - -use derive_more::Display; -use futures_channel::oneshot; -use parking_lot::Mutex; -use threadpool::ThreadPool; - -/// Env variable for default cpu pool size. -const ENV_CPU_POOL_VAR: &str = "ACTIX_THREADPOOL"; - -lazy_static::lazy_static! { - pub(crate) static ref DEFAULT_POOL: Mutex = { - let num = std::env::var(ENV_CPU_POOL_VAR) - .map_err(|_| ()) - .and_then(|val| { - val.parse().map_err(|_| log::warn!( - "Can not parse {} value, using default", - ENV_CPU_POOL_VAR, - )) - }) - .unwrap_or_else(|_| num_cpus::get() * 5); - Mutex::new( - threadpool::Builder::new() - .thread_name("actix-web".to_owned()) - .num_threads(num) - .build(), - ) - }; -} - -thread_local! { - static POOL: ThreadPool = { - DEFAULT_POOL.lock().clone() - }; -} - -/// Blocking operation execution error -#[derive(Debug, Display)] -pub enum BlockingError { - #[display(fmt = "{:?}", _0)] - Error(E), - #[display(fmt = "Thread pool is gone")] - Canceled, -} - -impl std::error::Error for BlockingError {} - -/// Execute blocking function on a thread pool, returns future that resolves -/// to result of the function execution. -pub fn run(f: F) -> CpuFuture -where - F: FnOnce() -> Result + Send + 'static, - I: Send + 'static, - E: Send + fmt::Debug + 'static, -{ - let (tx, rx) = oneshot::channel(); - POOL.with(|pool| { - pool.execute(move || { - if !tx.is_canceled() { - let _ = tx.send(f()); - } - }) - }); - - CpuFuture { rx } -} - -/// Blocking operation completion future. It resolves with results -/// of blocking function execution. -pub struct CpuFuture { - rx: oneshot::Receiver>, -} - -impl Future for CpuFuture { - type Output = Result>; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let rx = Pin::new(&mut self.rx); - let res = match rx.poll(cx) { - Poll::Pending => return Poll::Pending, - Poll::Ready(res) => res - .map_err(|_| BlockingError::Canceled) - .and_then(|res| res.map_err(BlockingError::Error)), - }; - Poll::Ready(res) - } -} From 6edf9b8278058712c99da428470ebe87a0be6200 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sat, 9 Jan 2021 15:12:59 +0000 Subject: [PATCH 36/73] prepare rt 2.0.0-beta.2 release --- actix-rt/CHANGES.md | 4 ++++ actix-rt/Cargo.toml | 8 ++++++-- actix-rt/src/lib.rs | 6 ++++-- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/actix-rt/CHANGES.md b/actix-rt/CHANGES.md index 6b15c6ed..efdd58a5 100644 --- a/actix-rt/CHANGES.md +++ b/actix-rt/CHANGES.md @@ -1,10 +1,14 @@ # Changes ## Unreleased - 2021-xx-xx + + +## 2.0.0-beta.2 - 2021-01-09 * Add `task` mod with re-export of `tokio::task::{spawn_blocking, yield_now, JoinHandle}` [#245] [#245]: https://github.com/actix/actix-net/pull/245 + ## 2.0.0-beta.1 - 2020-12-28 ### Added * Add `System::attach_to_tokio` method. [#173] diff --git a/actix-rt/Cargo.toml b/actix-rt/Cargo.toml index 1386e121..f2fe8435 100644 --- a/actix-rt/Cargo.toml +++ b/actix-rt/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-rt" -version = "2.0.0-beta.1" +version = "2.0.0-beta.2" authors = ["Nikolay Kim "] description = "Tokio-based single-thread async runtime for the Actix ecosystem" keywords = ["network", "framework", "async", "futures"] @@ -15,7 +15,11 @@ edition = "2018" name = "actix_rt" path = "src/lib.rs" +[features] +default = ["macros"] +macros = ["actix-macros"] + [dependencies] -actix-macros = "0.2.0-beta.1" +actix-macros = { version = "0.2.0-beta.1", optional = true } tokio = { version = "1", features = ["rt", "net", "parking_lot", "signal", "sync", "time"] } diff --git a/actix-rt/src/lib.rs b/actix-rt/src/lib.rs index 6ccd588f..30fa2d78 100644 --- a/actix-rt/src/lib.rs +++ b/actix-rt/src/lib.rs @@ -7,7 +7,9 @@ use std::future::Future; -#[cfg(not(test))] // Work around for rust-lang/rust#62127 +// Cannot define a main macro when compiled into test harness. +// Workaround for https://github.com/rust-lang/rust/issues/62127. +#[cfg(all(feature = "macros", not(test)))] pub use actix_macros::{main, test}; mod arbiter; @@ -64,7 +66,7 @@ pub mod time { pub use tokio::time::{timeout, Timeout}; } -/// task management. +/// Blocking task management. pub mod task { pub use tokio::task::{spawn_blocking, yield_now, JoinHandle}; } From a2e03700e736cf56a9bbe36c286d4ffc785bbeee Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sat, 9 Jan 2021 15:16:31 +0000 Subject: [PATCH 37/73] update rt changelog --- actix-rt/CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/actix-rt/CHANGES.md b/actix-rt/CHANGES.md index efdd58a5..7b89b2fa 100644 --- a/actix-rt/CHANGES.md +++ b/actix-rt/CHANGES.md @@ -5,6 +5,7 @@ ## 2.0.0-beta.2 - 2021-01-09 * Add `task` mod with re-export of `tokio::task::{spawn_blocking, yield_now, JoinHandle}` [#245] +* Add default "macros" feature to allow faster compile times when using `default-features=false`. [#245]: https://github.com/actix/actix-net/pull/245 From 6112a47529ee636bc83572dcbf78b5d1a8bdb8b0 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sat, 9 Jan 2021 15:19:16 +0000 Subject: [PATCH 38/73] update local deps --- actix-macros/Cargo.toml | 2 +- actix-server/Cargo.toml | 4 ++-- actix-service/Cargo.toml | 2 +- actix-tls/Cargo.toml | 4 ++-- actix-tracing/Cargo.toml | 4 ++-- actix-utils/Cargo.toml | 4 ++-- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/actix-macros/Cargo.toml b/actix-macros/Cargo.toml index d88b0c2d..7392eb12 100644 --- a/actix-macros/Cargo.toml +++ b/actix-macros/Cargo.toml @@ -20,7 +20,7 @@ syn = { version = "^1", features = ["full"] } actix-reexport = [] [dev-dependencies] -actix-rt = "1.0" +actix-rt = "2.0.0-beta.2" futures-util = { version = "0.3", default-features = false } trybuild = "1" diff --git a/actix-server/Cargo.toml b/actix-server/Cargo.toml index 2ceeaa21..ead85de0 100644 --- a/actix-server/Cargo.toml +++ b/actix-server/Cargo.toml @@ -24,8 +24,8 @@ default = [] [dependencies] actix-codec = "0.4.0-beta.1" -actix-rt = "2.0.0-beta.1" -actix-service = "2.0.0-beta.2" +actix-rt = "2.0.0-beta.2" +actix-service = "2.0.0-beta.3" actix-utils = "3.0.0-beta.1" futures-core = { version = "0.3.7", default-features = false } diff --git a/actix-service/Cargo.toml b/actix-service/Cargo.toml index afce1a41..e6cb1250 100644 --- a/actix-service/Cargo.toml +++ b/actix-service/Cargo.toml @@ -24,5 +24,5 @@ futures-core = { version = "0.3.7", default-features = false } pin-project-lite = "0.2" [dev-dependencies] -actix-rt = "1.0.0" +actix-rt = "2.0.0-beta.2" futures-util = { version = "0.3.7", default-features = false } diff --git a/actix-tls/Cargo.toml b/actix-tls/Cargo.toml index cfd6960a..8f23d60c 100644 --- a/actix-tls/Cargo.toml +++ b/actix-tls/Cargo.toml @@ -45,8 +45,8 @@ uri = ["http"] [dependencies] actix-codec = "0.4.0-beta.1" -actix-rt = "2.0.0-beta.1" -actix-service = "2.0.0-beta.2" +actix-rt = "2.0.0-beta.2" +actix-service = "2.0.0-beta.3" actix-utils = "3.0.0-beta.1" derive_more = "0.99.5" diff --git a/actix-tracing/Cargo.toml b/actix-tracing/Cargo.toml index 547f2d16..72f7b702 100644 --- a/actix-tracing/Cargo.toml +++ b/actix-tracing/Cargo.toml @@ -16,12 +16,12 @@ name = "actix_tracing" path = "src/lib.rs" [dependencies] -actix-service = "2.0.0-beta.2" +actix-service = "2.0.0-beta.3" futures-util = { version = "0.3.4", default-features = false } tracing = "0.1" tracing-futures = "0.2" [dev_dependencies] -actix-rt = "1.0" +actix-rt = "2.0.0-beta.2" slab = "0.4" diff --git a/actix-utils/Cargo.toml b/actix-utils/Cargo.toml index aab17fca..f038414c 100644 --- a/actix-utils/Cargo.toml +++ b/actix-utils/Cargo.toml @@ -17,8 +17,8 @@ path = "src/lib.rs" [dependencies] actix-codec = "0.4.0-beta.1" -actix-rt = "2.0.0-beta.1" -actix-service = "2.0.0-beta.2" +actix-rt = "2.0.0-beta.2" +actix-service = "2.0.0-beta.3" futures-core = { version = "0.3.7", default-features = false } futures-sink = { version = "0.3.7", default-features = false } From 874e5f2e505d08c6b5df74b12dc0809e5b5d9a94 Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Fri, 22 Jan 2021 17:33:50 -0800 Subject: [PATCH 39/73] change default name resolver and allow custom resolvers (#248) --- actix-rt/src/arbiter.rs | 55 +---- actix-rt/src/lib.rs | 2 +- actix-rt/tests/integration_tests.rs | 37 ---- actix-server/Cargo.toml | 4 +- actix-tls/CHANGES.md | 10 + actix-tls/Cargo.toml | 15 +- actix-tls/src/accept/nativetls.rs | 13 +- actix-tls/src/accept/openssl.rs | 24 +- actix-tls/src/accept/rustls.rs | 26 ++- actix-tls/src/connect/connect.rs | 119 +++++----- actix-tls/src/connect/connector.rs | 99 +++------ actix-tls/src/connect/error.rs | 7 +- actix-tls/src/connect/mod.rs | 68 ++---- actix-tls/src/connect/resolve.rs | 316 +++++++++++++++------------ actix-tls/src/connect/service.rs | 183 ++++++---------- actix-tls/src/connect/ssl/openssl.rs | 93 ++++---- actix-tls/src/connect/ssl/rustls.rs | 25 +-- actix-tls/tests/test_connect.rs | 64 +++++- actix-utils/Cargo.toml | 2 +- 19 files changed, 513 insertions(+), 649 deletions(-) mode change 100644 => 100755 actix-server/Cargo.toml mode change 100644 => 100755 actix-tls/Cargo.toml mode change 100644 => 100755 actix-tls/src/connect/connect.rs mode change 100644 => 100755 actix-tls/src/connect/connector.rs mode change 100644 => 100755 actix-tls/src/connect/resolve.rs mode change 100644 => 100755 actix-tls/src/connect/service.rs mode change 100644 => 100755 actix-tls/src/connect/ssl/openssl.rs mode change 100644 => 100755 actix-tls/src/connect/ssl/rustls.rs mode change 100644 => 100755 actix-tls/tests/test_connect.rs diff --git a/actix-rt/src/arbiter.rs b/actix-rt/src/arbiter.rs index 7aae7cd2..95b40b25 100644 --- a/actix-rt/src/arbiter.rs +++ b/actix-rt/src/arbiter.rs @@ -9,9 +9,6 @@ use std::{fmt, thread}; use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}; use tokio::sync::oneshot::{channel, error::RecvError as Canceled, Sender}; -// use futures_util::stream::FuturesUnordered; -// use tokio::task::JoinHandle; -// use tokio::stream::StreamExt; use tokio::task::LocalSet; use crate::runtime::Runtime; @@ -19,12 +16,6 @@ use crate::system::System; thread_local!( static ADDR: RefCell> = RefCell::new(None); - // TODO: Commented out code are for Arbiter::local_join function. - // It can be safely removed if this function is not used in actix-*. - // - // /// stores join handle for spawned async tasks. - // static HANDLE: RefCell>> = - // RefCell::new(FuturesUnordered::new()); static STORAGE: RefCell>> = RefCell::new(HashMap::new()); ); @@ -154,11 +145,6 @@ impl Arbiter { where F: Future + 'static, { - // HANDLE.with(|handle| { - // let handle = handle.borrow(); - // handle.push(tokio::task::spawn_local(future)); - // }); - // let _ = tokio::task::spawn_local(CleanupPending); let _ = tokio::task::spawn_local(future); } @@ -277,32 +263,12 @@ impl Arbiter { /// Returns a future that will be completed once all currently spawned futures /// have completed. - #[deprecated(since = "1.2.0", note = "Arbiter::local_join function is removed.")] + #[deprecated(since = "2.0.0", note = "Arbiter::local_join function is removed.")] pub async fn local_join() { - // let handle = HANDLE.with(|fut| std::mem::take(&mut *fut.borrow_mut())); - // async move { - // handle.collect::>().await; - // } unimplemented!("Arbiter::local_join function is removed.") } } -// /// Future used for cleaning-up already finished `JoinHandle`s -// /// from the `PENDING` list so the vector doesn't grow indefinitely -// struct CleanupPending; -// -// impl Future for CleanupPending { -// type Output = (); -// -// fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { -// HANDLE.with(move |handle| { -// recycle_join_handle(&mut *handle.borrow_mut(), cx); -// }); -// -// Poll::Ready(()) -// } -// } - struct ArbiterController { rx: UnboundedReceiver, } @@ -330,11 +296,6 @@ impl Future for ArbiterController { Poll::Ready(Some(item)) => match item { ArbiterCommand::Stop => return Poll::Ready(()), ArbiterCommand::Execute(fut) => { - // HANDLE.with(|handle| { - // let mut handle = handle.borrow_mut(); - // handle.push(tokio::task::spawn_local(fut)); - // recycle_join_handle(&mut *handle, cx); - // }); tokio::task::spawn_local(fut); } ArbiterCommand::ExecuteFn(f) => { @@ -347,20 +308,6 @@ impl Future for ArbiterController { } } -// fn recycle_join_handle(handle: &mut FuturesUnordered>, cx: &mut Context<'_>) { -// let _ = Pin::new(&mut *handle).poll_next(cx); -// -// // Try to recycle more join handles and free up memory. -// // -// // this is a guess. The yield limit for FuturesUnordered is 32. -// // So poll an extra 3 times would make the total poll below 128. -// if handle.len() > 64 { -// (0..3).for_each(|_| { -// let _ = Pin::new(&mut *handle).poll_next(cx); -// }) -// } -// } - #[derive(Debug)] pub(crate) enum SystemCommand { Exit(i32), diff --git a/actix-rt/src/lib.rs b/actix-rt/src/lib.rs index 30fa2d78..6e9d0464 100644 --- a/actix-rt/src/lib.rs +++ b/actix-rt/src/lib.rs @@ -66,7 +66,7 @@ pub mod time { pub use tokio::time::{timeout, Timeout}; } -/// Blocking task management. +/// Task management. pub mod task { pub use tokio::task::{spawn_blocking, yield_now, JoinHandle}; } diff --git a/actix-rt/tests/integration_tests.rs b/actix-rt/tests/integration_tests.rs index 12ceb4ef..f338602d 100644 --- a/actix-rt/tests/integration_tests.rs +++ b/actix-rt/tests/integration_tests.rs @@ -62,43 +62,6 @@ fn join_another_arbiter() { ); } -// #[test] -// fn join_current_arbiter() { -// let time = Duration::from_secs(2); -// -// let instant = Instant::now(); -// actix_rt::System::new("test_join_current_arbiter").block_on(async move { -// actix_rt::spawn(async move { -// tokio::time::delay_for(time).await; -// actix_rt::Arbiter::current().stop(); -// }); -// actix_rt::Arbiter::local_join().await; -// }); -// assert!( -// instant.elapsed() >= time, -// "Join on current arbiter should wait for all spawned futures" -// ); -// -// let large_timer = Duration::from_secs(20); -// let instant = Instant::now(); -// actix_rt::System::new("test_join_current_arbiter").block_on(async move { -// actix_rt::spawn(async move { -// tokio::time::delay_for(time).await; -// actix_rt::Arbiter::current().stop(); -// }); -// let f = actix_rt::Arbiter::local_join(); -// actix_rt::spawn(async move { -// tokio::time::delay_for(large_timer).await; -// actix_rt::Arbiter::current().stop(); -// }); -// f.await; -// }); -// assert!( -// instant.elapsed() < large_timer, -// "local_join should await only for the already spawned futures" -// ); -// } - #[test] fn non_static_block_on() { let string = String::from("test_str"); diff --git a/actix-server/Cargo.toml b/actix-server/Cargo.toml old mode 100644 new mode 100755 index ead85de0..25095039 --- a/actix-server/Cargo.toml +++ b/actix-server/Cargo.toml @@ -24,11 +24,11 @@ default = [] [dependencies] actix-codec = "0.4.0-beta.1" -actix-rt = "2.0.0-beta.2" +actix-rt = { version = "2.0.0-beta.2", default-features = false } actix-service = "2.0.0-beta.3" actix-utils = "3.0.0-beta.1" -futures-core = { version = "0.3.7", default-features = false } +futures-core = { version = "0.3.7", default-features = false, features = ["alloc"] } log = "0.4" mio = { version = "0.7.6", features = ["os-poll", "net"] } num_cpus = "1.13" diff --git a/actix-tls/CHANGES.md b/actix-tls/CHANGES.md index 1a7ef7a7..d73a7830 100644 --- a/actix-tls/CHANGES.md +++ b/actix-tls/CHANGES.md @@ -1,6 +1,16 @@ # Changes ## Unreleased - 2021-xx-xx +* Remove `trust-dns-proto` and `trust-dns-resolver` [#248] +* Use `tokio::net::lookup_host` as simple and basic default resolver [#248] +* Add `Resolve` trait for custom dns resolver. [#248] +* Add `Resolver::new_custom` function to construct custom resolvers. [#248] +* Export `webpki_roots::TLS_SERVER_ROOTS` in `actix_tls::connect` mod and remove + the export from `actix_tls::accept` [#248] +* Remove `ConnectTakeAddrsIter`. `Connect::take_addrs` would return + `ConnectAddrsIter<'static>` as owned iterator. [#248] + +[#248]: https://github.com/actix/actix-net/pull/248 ## 3.0.0-beta.2 - 2021-xx-xx diff --git a/actix-tls/Cargo.toml b/actix-tls/Cargo.toml old mode 100644 new mode 100755 index 8f23d60c..0886b27e --- a/actix-tls/Cargo.toml +++ b/actix-tls/Cargo.toml @@ -29,7 +29,7 @@ default = ["accept", "connect", "uri"] accept = [] # enable connector services -connect = ["trust-dns-proto/tokio-runtime", "trust-dns-resolver/tokio-runtime", "trust-dns-resolver/system-config"] +connect = [] # use openssl impls openssl = ["tls-openssl", "tokio-openssl"] @@ -45,20 +45,15 @@ uri = ["http"] [dependencies] actix-codec = "0.4.0-beta.1" -actix-rt = "2.0.0-beta.2" +actix-rt = { version = "2.0.0-beta.2", default-features = false } actix-service = "2.0.0-beta.3" actix-utils = "3.0.0-beta.1" derive_more = "0.99.5" -either = "1.6" -futures-util = { version = "0.3.7", default-features = false } -http = { version = "0.2.2", optional = true } +futures-core = { version = "0.3.7", default-features = false, features = ["alloc"] } +http = { version = "0.2.3", optional = true } log = "0.4" -# resolver -trust-dns-proto = { version = "0.20.0", default-features = false, optional = true } -trust-dns-resolver = { version = "0.20.0", default-features = false, optional = true } - # openssl tls-openssl = { package = "openssl", version = "0.10", optional = true } tokio-openssl = { version = "0.6", optional = true } @@ -76,8 +71,10 @@ tls-native-tls = { package = "native-tls", version = "0.2", optional = true } tokio-native-tls = { version = "0.3", optional = true } [dev-dependencies] +actix-rt = "2.0.0-beta.2" actix-server = "2.0.0-beta.2" bytes = "1" env_logger = "0.8" futures-util = { version = "0.3.7", default-features = false, features = ["sink"] } log = "0.4" +trust-dns-resolver = "0.20.0" \ No newline at end of file diff --git a/actix-tls/src/accept/nativetls.rs b/actix-tls/src/accept/nativetls.rs index 5d80ce8b..9b2aeefb 100644 --- a/actix-tls/src/accept/nativetls.rs +++ b/actix-tls/src/accept/nativetls.rs @@ -3,7 +3,7 @@ use std::task::{Context, Poll}; use actix_codec::{AsyncRead, AsyncWrite}; use actix_service::{Service, ServiceFactory}; use actix_utils::counter::Counter; -use futures_util::future::{ready, LocalBoxFuture, Ready}; +use futures_core::future::LocalBoxFuture; pub use native_tls::Error; pub use tokio_native_tls::{TlsAcceptor, TlsStream}; @@ -44,15 +44,16 @@ where type Service = NativeTlsAcceptorService; type InitError = (); - type Future = Ready>; + type Future = LocalBoxFuture<'static, Result>; fn new_service(&self, _: ()) -> Self::Future { - MAX_CONN_COUNTER.with(|conns| { - ready(Ok(NativeTlsAcceptorService { + let res = MAX_CONN_COUNTER.with(|conns| { + Ok(NativeTlsAcceptorService { acceptor: self.acceptor.clone(), conns: conns.clone(), - })) - }) + }) + }); + Box::pin(async { res }) } } diff --git a/actix-tls/src/accept/openssl.rs b/actix-tls/src/accept/openssl.rs index efda5c38..5f2d2fc2 100644 --- a/actix-tls/src/accept/openssl.rs +++ b/actix-tls/src/accept/openssl.rs @@ -1,14 +1,13 @@ -use std::future::Future; -use std::pin::Pin; -use std::task::{Context, Poll}; +use std::{ + future::Future, + pin::Pin, + task::{Context, Poll}, +}; use actix_codec::{AsyncRead, AsyncWrite}; use actix_service::{Service, ServiceFactory}; use actix_utils::counter::{Counter, CounterGuard}; -use futures_util::{ - future::{ready, Ready}, - ready, -}; +use futures_core::{future::LocalBoxFuture, ready}; pub use openssl::ssl::{ AlpnError, Error as SslError, HandshakeError, Ssl, SslAcceptor, SslAcceptorBuilder, @@ -50,15 +49,16 @@ where type Config = (); type Service = AcceptorService; type InitError = (); - type Future = Ready>; + type Future = LocalBoxFuture<'static, Result>; fn new_service(&self, _: ()) -> Self::Future { - MAX_CONN_COUNTER.with(|conns| { - ready(Ok(AcceptorService { + let res = MAX_CONN_COUNTER.with(|conns| { + Ok(AcceptorService { acceptor: self.acceptor.clone(), conns: conns.clone(), - })) - }) + }) + }); + Box::pin(async { res }) } } diff --git a/actix-tls/src/accept/rustls.rs b/actix-tls/src/accept/rustls.rs index a6686f44..e7efaa3f 100644 --- a/actix-tls/src/accept/rustls.rs +++ b/actix-tls/src/accept/rustls.rs @@ -1,18 +1,19 @@ -use std::future::Future; -use std::io; -use std::pin::Pin; -use std::sync::Arc; -use std::task::{Context, Poll}; +use std::{ + future::Future, + io, + pin::Pin, + sync::Arc, + task::{Context, Poll}, +}; use actix_codec::{AsyncRead, AsyncWrite}; use actix_service::{Service, ServiceFactory}; use actix_utils::counter::{Counter, CounterGuard}; -use futures_util::future::{ready, Ready}; +use futures_core::future::LocalBoxFuture; use tokio_rustls::{Accept, TlsAcceptor}; pub use rustls::{ServerConfig, Session}; pub use tokio_rustls::server::TlsStream; -pub use webpki_roots::TLS_SERVER_ROOTS; use super::MAX_CONN_COUNTER; @@ -52,15 +53,16 @@ where type Service = AcceptorService; type InitError = (); - type Future = Ready>; + type Future = LocalBoxFuture<'static, Result>; fn new_service(&self, _: ()) -> Self::Future { - MAX_CONN_COUNTER.with(|conns| { - ready(Ok(AcceptorService { + let res = MAX_CONN_COUNTER.with(|conns| { + Ok(AcceptorService { acceptor: self.config.clone().into(), conns: conns.clone(), - })) - }) + }) + }); + Box::pin(async { res }) } } diff --git a/actix-tls/src/connect/connect.rs b/actix-tls/src/connect/connect.rs old mode 100644 new mode 100755 index 37fa9c6e..3928870d --- a/actix-tls/src/connect/connect.rs +++ b/actix-tls/src/connect/connect.rs @@ -1,9 +1,9 @@ -use std::collections::{vec_deque, VecDeque}; -use std::fmt; -use std::iter::{FromIterator, FusedIterator}; -use std::net::SocketAddr; - -use either::Either; +use std::{ + collections::{vec_deque, VecDeque}, + fmt, + iter::{FromIterator, FusedIterator}, + net::SocketAddr, +}; /// Connect request pub trait Address: Unpin + 'static { @@ -39,7 +39,25 @@ impl Address for &'static str { pub struct Connect { pub(crate) req: T, pub(crate) port: u16, - pub(crate) addr: Option>>, + pub(crate) addr: ConnectAddrs, +} + +#[derive(Eq, PartialEq, Debug, Hash)] +pub(crate) enum ConnectAddrs { + One(Option), + Multi(VecDeque), +} + +impl ConnectAddrs { + pub(crate) fn is_none(&self) -> bool { + matches!(*self, Self::One(None)) + } +} + +impl Default for ConnectAddrs { + fn default() -> Self { + Self::One(None) + } } impl Connect { @@ -49,7 +67,7 @@ impl Connect { Connect { req, port: port.unwrap_or(0), - addr: None, + addr: ConnectAddrs::One(None), } } @@ -59,7 +77,7 @@ impl Connect { Connect { req, port: 0, - addr: Some(Either::Left(addr)), + addr: ConnectAddrs::One(Some(addr)), } } @@ -73,9 +91,7 @@ impl Connect { /// Use address. pub fn set_addr(mut self, addr: Option) -> Self { - if let Some(addr) = addr { - self.addr = Some(Either::Left(addr)); - } + self.addr = ConnectAddrs::One(addr); self } @@ -86,9 +102,9 @@ impl Connect { { let mut addrs = VecDeque::from_iter(addrs); self.addr = if addrs.len() < 2 { - addrs.pop_front().map(Either::Left) + ConnectAddrs::One(addrs.pop_front()) } else { - Some(Either::Right(addrs)) + ConnectAddrs::Multi(addrs) }; self } @@ -105,24 +121,18 @@ impl Connect { /// Pre-resolved addresses of the request. pub fn addrs(&self) -> ConnectAddrsIter<'_> { - let inner = match self.addr { - None => Either::Left(None), - Some(Either::Left(addr)) => Either::Left(Some(addr)), - Some(Either::Right(ref addrs)) => Either::Right(addrs.iter()), - }; - - ConnectAddrsIter { inner } + match self.addr { + ConnectAddrs::One(addr) => ConnectAddrsIter::One(addr), + ConnectAddrs::Multi(ref addrs) => ConnectAddrsIter::Multi(addrs.iter()), + } } /// Takes pre-resolved addresses of the request. - pub fn take_addrs(&mut self) -> ConnectTakeAddrsIter { - let inner = match self.addr.take() { - None => Either::Left(None), - Some(Either::Left(addr)) => Either::Left(Some(addr)), - Some(Either::Right(addrs)) => Either::Right(addrs.into_iter()), - }; - - ConnectTakeAddrsIter { inner } + pub fn take_addrs(&mut self) -> ConnectAddrsIter<'static> { + match std::mem::take(&mut self.addr) { + ConnectAddrs::One(addr) => ConnectAddrsIter::One(addr), + ConnectAddrs::Multi(addrs) => ConnectAddrsIter::MultiOwned(addrs.into_iter()), + } } } @@ -140,25 +150,29 @@ impl fmt::Display for Connect { /// Iterator over addresses in a [`Connect`] request. #[derive(Clone)] -pub struct ConnectAddrsIter<'a> { - inner: Either, vec_deque::Iter<'a, SocketAddr>>, +pub enum ConnectAddrsIter<'a> { + One(Option), + Multi(vec_deque::Iter<'a, SocketAddr>), + MultiOwned(vec_deque::IntoIter), } impl Iterator for ConnectAddrsIter<'_> { type Item = SocketAddr; fn next(&mut self) -> Option { - match self.inner { - Either::Left(ref mut opt) => opt.take(), - Either::Right(ref mut iter) => iter.next().copied(), + match *self { + Self::One(ref mut addr) => addr.take(), + Self::Multi(ref mut iter) => iter.next().copied(), + Self::MultiOwned(ref mut iter) => iter.next(), } } fn size_hint(&self) -> (usize, Option) { - match self.inner { - Either::Left(Some(_)) => (1, Some(1)), - Either::Left(None) => (0, Some(0)), - Either::Right(ref iter) => iter.size_hint(), + match *self { + Self::One(None) => (0, Some(0)), + Self::One(Some(_)) => (1, Some(1)), + Self::Multi(ref iter) => iter.size_hint(), + Self::MultiOwned(ref iter) => iter.size_hint(), } } } @@ -173,35 +187,6 @@ impl ExactSizeIterator for ConnectAddrsIter<'_> {} impl FusedIterator for ConnectAddrsIter<'_> {} -/// Owned iterator over addresses in a [`Connect`] request. -#[derive(Debug)] -pub struct ConnectTakeAddrsIter { - inner: Either, vec_deque::IntoIter>, -} - -impl Iterator for ConnectTakeAddrsIter { - type Item = SocketAddr; - - fn next(&mut self) -> Option { - match self.inner { - Either::Left(ref mut opt) => opt.take(), - Either::Right(ref mut iter) => iter.next(), - } - } - - fn size_hint(&self) -> (usize, Option) { - match self.inner { - Either::Left(Some(_)) => (1, Some(1)), - Either::Left(None) => (0, Some(0)), - Either::Right(ref iter) => iter.size_hint(), - } - } -} - -impl ExactSizeIterator for ConnectTakeAddrsIter {} - -impl FusedIterator for ConnectTakeAddrsIter {} - fn parse(host: &str) -> (&str, Option) { let mut parts_iter = host.splitn(2, ':'); if let Some(host) = parts_iter.next() { diff --git a/actix-tls/src/connect/connector.rs b/actix-tls/src/connect/connector.rs old mode 100644 new mode 100755 index a0a6b8b5..fe4ceec6 --- a/actix-tls/src/connect/connector.rs +++ b/actix-tls/src/connect/connector.rs @@ -1,76 +1,50 @@ -use std::collections::VecDeque; -use std::future::Future; -use std::io; -use std::marker::PhantomData; -use std::net::SocketAddr; -use std::pin::Pin; -use std::task::{Context, Poll}; +use std::{ + collections::VecDeque, + future::Future, + io, + net::SocketAddr, + pin::Pin, + task::{Context, Poll}, +}; use actix_rt::net::TcpStream; use actix_service::{Service, ServiceFactory}; -use futures_util::future::{ready, Ready}; +use futures_core::future::LocalBoxFuture; use log::{error, trace}; -use super::connect::{Address, Connect, Connection}; +use super::connect::{Address, Connect, ConnectAddrs, Connection}; use super::error::ConnectError; /// TCP connector service factory -#[derive(Debug)] -pub struct TcpConnectorFactory(PhantomData); - -impl TcpConnectorFactory { - pub fn new() -> Self { - TcpConnectorFactory(PhantomData) - } +#[derive(Copy, Clone, Debug)] +pub struct TcpConnectorFactory; +impl TcpConnectorFactory { /// Create TCP connector service - pub fn service(&self) -> TcpConnector { - TcpConnector(PhantomData) + pub fn service(&self) -> TcpConnector { + TcpConnector } } -impl Default for TcpConnectorFactory { - fn default() -> Self { - TcpConnectorFactory(PhantomData) - } -} - -impl Clone for TcpConnectorFactory { - fn clone(&self) -> Self { - TcpConnectorFactory(PhantomData) - } -} - -impl ServiceFactory> for TcpConnectorFactory { +impl ServiceFactory> for TcpConnectorFactory { type Response = Connection; type Error = ConnectError; type Config = (); - type Service = TcpConnector; + type Service = TcpConnector; type InitError = (); - type Future = Ready>; + type Future = LocalBoxFuture<'static, Result>; fn new_service(&self, _: ()) -> Self::Future { - ready(Ok(self.service())) + let service = self.service(); + Box::pin(async move { Ok(service) }) } } /// TCP connector service -#[derive(Default, Debug)] -pub struct TcpConnector(PhantomData); +#[derive(Copy, Clone, Debug)] +pub struct TcpConnector; -impl TcpConnector { - pub fn new() -> Self { - TcpConnector(PhantomData) - } -} - -impl Clone for TcpConnector { - fn clone(&self) -> Self { - TcpConnector(PhantomData) - } -} - -impl Service> for TcpConnector { +impl Service> for TcpConnector { type Response = Connection; type Error = ConnectError; type Future = TcpConnectorResponse; @@ -81,17 +55,10 @@ impl Service> for TcpConnector { let port = req.port(); let Connect { req, addr, .. } = req; - if let Some(addr) = addr { - TcpConnectorResponse::new(req, port, addr) - } else { - error!("TCP connector: got unresolved address"); - TcpConnectorResponse::Error(Some(ConnectError::Unresolved)) - } + TcpConnectorResponse::new(req, port, addr) } } -type LocalBoxFuture<'a, T> = Pin + 'a>>; - #[doc(hidden)] /// TCP stream connector response future pub enum TcpConnectorResponse { @@ -105,11 +72,7 @@ pub enum TcpConnectorResponse { } impl TcpConnectorResponse { - pub fn new( - req: T, - port: u16, - addr: either::Either>, - ) -> TcpConnectorResponse { + pub(crate) fn new(req: T, port: u16, addr: ConnectAddrs) -> TcpConnectorResponse { trace!( "TCP connector - connecting to {:?} port:{}", req.host(), @@ -117,13 +80,19 @@ impl TcpConnectorResponse { ); match addr { - either::Either::Left(addr) => TcpConnectorResponse::Response { + ConnectAddrs::One(None) => { + error!("TCP connector: got unresolved address"); + TcpConnectorResponse::Error(Some(ConnectError::Unresolved)) + } + ConnectAddrs::One(Some(addr)) => TcpConnectorResponse::Response { req: Some(req), port, addrs: None, stream: Some(Box::pin(TcpStream::connect(addr))), }, - either::Either::Right(addrs) => TcpConnectorResponse::Response { + // when resolver returns multiple socket addr for request they would be popped from + // front end of queue and returns with the first successful tcp connection. + ConnectAddrs::Multi(addrs) => TcpConnectorResponse::Response { req: Some(req), port, addrs: Some(addrs), @@ -165,7 +134,7 @@ impl Future for TcpConnectorResponse { port, ); if addrs.is_none() || addrs.as_ref().unwrap().is_empty() { - return Poll::Ready(Err(err.into())); + return Poll::Ready(Err(ConnectError::Io(err))); } } } diff --git a/actix-tls/src/connect/error.rs b/actix-tls/src/connect/error.rs index 84b363dc..5d8cb9db 100644 --- a/actix-tls/src/connect/error.rs +++ b/actix-tls/src/connect/error.rs @@ -1,13 +1,12 @@ use std::io; -use derive_more::{Display, From}; -use trust_dns_resolver::error::ResolveError; +use derive_more::Display; -#[derive(Debug, From, Display)] +#[derive(Debug, Display)] pub enum ConnectError { /// Failed to resolve the hostname #[display(fmt = "Failed resolving hostname: {}", _0)] - Resolver(ResolveError), + Resolver(Box), /// No dns records #[display(fmt = "No dns records found for the input")] diff --git a/actix-tls/src/connect/mod.rs b/actix-tls/src/connect/mod.rs index 75312c59..c864886d 100644 --- a/actix-tls/src/connect/mod.rs +++ b/actix-tls/src/connect/mod.rs @@ -4,6 +4,17 @@ //! //! * `openssl` - enables TLS support via `openssl` crate //! * `rustls` - enables TLS support via `rustls` crate +//! +//! ## Workflow of connector service: +//! - resolve [`Address`](self::connect::Address) with given [`Resolver`](self::resolve::Resolver) +//! and collect [`SocketAddrs`](std::net::SocketAddr). +//! - establish Tcp connection and return [`TcpStream`](tokio::net::TcpStream). +//! +//! ## Workflow of tls connector services: +//! - Establish [`TcpStream`](tokio::net::TcpStream) with connector service. +//! - Wrap around the stream and do connect handshake with remote address. +//! - Return certain stream type impl [`AsyncRead`](tokio::io::AsyncRead) and +//! [`AsyncWrite`](tokio::io::AsyncWrite) mod connect; mod connector; @@ -14,67 +25,26 @@ pub mod ssl; #[cfg(feature = "uri")] mod uri; -use actix_rt::{net::TcpStream, Arbiter}; +use actix_rt::net::TcpStream; use actix_service::{pipeline, pipeline_factory, Service, ServiceFactory}; -use trust_dns_resolver::config::{ResolverConfig, ResolverOpts}; -use trust_dns_resolver::system_conf::read_system_conf; -use trust_dns_resolver::TokioAsyncResolver as AsyncResolver; - -pub mod resolver { - pub use trust_dns_resolver::config::{ResolverConfig, ResolverOpts}; - pub use trust_dns_resolver::system_conf::read_system_conf; - pub use trust_dns_resolver::{error::ResolveError, AsyncResolver}; -} pub use self::connect::{Address, Connect, Connection}; pub use self::connector::{TcpConnector, TcpConnectorFactory}; pub use self::error::ConnectError; -pub use self::resolve::{Resolver, ResolverFactory}; +pub use self::resolve::{Resolve, Resolver, ResolverFactory}; pub use self::service::{ConnectService, ConnectServiceFactory, TcpConnectService}; -pub async fn start_resolver( - cfg: ResolverConfig, - opts: ResolverOpts, -) -> Result { - Ok(AsyncResolver::tokio(cfg, opts)?) -} - -struct DefaultResolver(AsyncResolver); - -pub(crate) async fn get_default_resolver() -> Result { - if Arbiter::contains_item::() { - Ok(Arbiter::get_item(|item: &DefaultResolver| item.0.clone())) - } else { - let (cfg, opts) = match read_system_conf() { - Ok((cfg, opts)) => (cfg, opts), - Err(e) => { - log::error!("TRust-DNS can not load system config: {}", e); - (ResolverConfig::default(), ResolverOpts::default()) - } - }; - - let resolver = AsyncResolver::tokio(cfg, opts)?; - - Arbiter::set_item(DefaultResolver(resolver.clone())); - Ok(resolver) - } -} - -pub async fn start_default_resolver() -> Result { - get_default_resolver().await -} - /// Create TCP connector service. pub fn new_connector( - resolver: AsyncResolver, + resolver: Resolver, ) -> impl Service, Response = Connection, Error = ConnectError> + Clone { - pipeline(Resolver::new(resolver)).and_then(TcpConnector::new()) + pipeline(resolver).and_then(TcpConnector) } /// Create TCP connector service factory. pub fn new_connector_factory( - resolver: AsyncResolver, + resolver: Resolver, ) -> impl ServiceFactory< Connect, Config = (), @@ -82,14 +52,14 @@ pub fn new_connector_factory( Error = ConnectError, InitError = (), > + Clone { - pipeline_factory(ResolverFactory::new(resolver)).and_then(TcpConnectorFactory::new()) + pipeline_factory(ResolverFactory::new(resolver)).and_then(TcpConnectorFactory) } /// Create connector service with default parameters. pub fn default_connector( ) -> impl Service, Response = Connection, Error = ConnectError> + Clone { - pipeline(Resolver::default()).and_then(TcpConnector::new()) + new_connector(Resolver::Default) } /// Create connector service factory with default parameters. @@ -100,5 +70,5 @@ pub fn default_connector_factory() -> impl ServiceFactory< Error = ConnectError, InitError = (), > + Clone { - pipeline_factory(ResolverFactory::default()).and_then(TcpConnectorFactory::new()) + new_connector_factory(Resolver::Default) } diff --git a/actix-tls/src/connect/resolve.rs b/actix-tls/src/connect/resolve.rs old mode 100644 new mode 100755 index 61535faa..211da387 --- a/actix-tls/src/connect/resolve.rs +++ b/actix-tls/src/connect/resolve.rs @@ -1,184 +1,225 @@ -use std::future::Future; -use std::marker::PhantomData; -use std::net::SocketAddr; -use std::pin::Pin; -use std::task::{Context, Poll}; +use std::{ + future::Future, + io, + net::SocketAddr, + pin::Pin, + rc::Rc, + task::{Context, Poll}, + vec::IntoIter, +}; +use actix_rt::task::{spawn_blocking, JoinHandle}; use actix_service::{Service, ServiceFactory}; -use futures_util::future::{ok, Either, Ready}; +use futures_core::{future::LocalBoxFuture, ready}; use log::trace; -use trust_dns_resolver::TokioAsyncResolver as AsyncResolver; -use trust_dns_resolver::{error::ResolveError, lookup_ip::LookupIp}; use super::connect::{Address, Connect}; use super::error::ConnectError; -use super::get_default_resolver; /// DNS Resolver Service factory -pub struct ResolverFactory { - resolver: Option, - _t: PhantomData, +#[derive(Clone)] +pub struct ResolverFactory { + resolver: Resolver, } -impl ResolverFactory { - /// Create new resolver instance with custom configuration and options. - pub fn new(resolver: AsyncResolver) -> Self { - ResolverFactory { - resolver: Some(resolver), - _t: PhantomData, - } +impl ResolverFactory { + pub fn new(resolver: Resolver) -> Self { + Self { resolver } } - pub fn service(&self) -> Resolver { - Resolver { - resolver: self.resolver.clone(), - _t: PhantomData, - } + pub fn service(&self) -> Resolver { + self.resolver.clone() } } -impl Default for ResolverFactory { - fn default() -> Self { - ResolverFactory { - resolver: None, - _t: PhantomData, - } - } -} - -impl Clone for ResolverFactory { - fn clone(&self) -> Self { - ResolverFactory { - resolver: self.resolver.clone(), - _t: PhantomData, - } - } -} - -impl ServiceFactory> for ResolverFactory { +impl ServiceFactory> for ResolverFactory { type Response = Connect; type Error = ConnectError; type Config = (); - type Service = Resolver; + type Service = Resolver; type InitError = (); - type Future = Ready>; + type Future = LocalBoxFuture<'static, Result>; fn new_service(&self, _: ()) -> Self::Future { - ok(self.service()) + let service = self.resolver.clone(); + Box::pin(async { Ok(service) }) } } /// DNS Resolver Service -pub struct Resolver { - resolver: Option, - _t: PhantomData, +#[derive(Clone)] +pub enum Resolver { + Default, + Custom(Rc), } -impl Resolver { - /// Create new resolver instance with custom configuration and options. - pub fn new(resolver: AsyncResolver) -> Self { - Resolver { - resolver: Some(resolver), - _t: PhantomData, - } +/// trait for custom lookup with self defined resolver. +/// +/// # Example: +/// ```rust +/// use std::net::SocketAddr; +/// +/// use actix_tls::connect::{Resolve, Resolver}; +/// use futures_util::future::LocalBoxFuture; +/// +/// // use trust_dns_resolver as custom resolver. +/// use trust_dns_resolver::TokioAsyncResolver; +/// +/// struct MyResolver { +/// trust_dns: TokioAsyncResolver, +/// }; +/// +/// // impl Resolve trait and convert given host address str and port to SocketAddr. +/// impl Resolve for MyResolver { +/// fn lookup<'a>( +/// &'a self, +/// host: &'a str, +/// port: u16, +/// ) -> LocalBoxFuture<'a, Result, Box>> { +/// Box::pin(async move { +/// let res = self +/// .trust_dns +/// .lookup_ip(host) +/// .await? +/// .iter() +/// .map(|ip| SocketAddr::new(ip, port)) +/// .collect(); +/// Ok(res) +/// }) +/// } +/// } +/// +/// let resolver = MyResolver { +/// trust_dns: TokioAsyncResolver::tokio_from_system_conf().unwrap(), +/// }; +/// +/// // construct custom resolver +/// let resolver = Resolver::new_custom(resolver); +/// +/// // pass custom resolver to connector builder. +/// // connector would then be usable as a service or awc's connector. +/// let connector = actix_tls::connect::new_connector::<&str>(resolver.clone()); +/// +/// // resolver can be passed to connector factory where returned service factory +/// // can be used to construct new connector services. +/// let factory = actix_tls::connect::new_connector_factory::<&str>(resolver); +///``` +pub trait Resolve { + fn lookup<'a>( + &'a self, + host: &'a str, + port: u16, + ) -> LocalBoxFuture<'a, Result, Box>>; +} + +impl Resolver { + /// Constructor for custom Resolve trait object and use it as resolver. + pub fn new_custom(resolver: impl Resolve + 'static) -> Self { + Self::Custom(Rc::new(resolver)) + } + + // look up with default resolver variant. + fn look_up(req: &Connect) -> JoinHandle>> { + let host = req.host(); + // TODO: Connect should always return host with port if possible. + let host = if req + .host() + .splitn(2, ':') + .last() + .and_then(|p| p.parse::().ok()) + .map(|p| p == req.port()) + .unwrap_or(false) + { + host.to_string() + } else { + format!("{}:{}", host, req.port()) + }; + + spawn_blocking(move || std::net::ToSocketAddrs::to_socket_addrs(&host)) } } -impl Default for Resolver { - fn default() -> Self { - Resolver { - resolver: None, - _t: PhantomData, - } - } -} - -impl Clone for Resolver { - fn clone(&self) -> Self { - Resolver { - resolver: self.resolver.clone(), - _t: PhantomData, - } - } -} - -impl Service> for Resolver { +impl Service> for Resolver { type Response = Connect; type Error = ConnectError; - #[allow(clippy::type_complexity)] - type Future = Either< - Pin>>>, - Ready, Self::Error>>, - >; + type Future = ResolverFuture; actix_service::always_ready!(); - fn call(&mut self, mut req: Connect) -> Self::Future { - if req.addr.is_some() { - Either::Right(ok(req)) + fn call(&mut self, req: Connect) -> Self::Future { + if !req.addr.is_none() { + ResolverFuture::Connected(Some(req)) } else if let Ok(ip) = req.host().parse() { - req.addr = Some(either::Either::Left(SocketAddr::new(ip, req.port()))); - Either::Right(ok(req)) + let addr = SocketAddr::new(ip, req.port()); + let req = req.set_addr(Some(addr)); + ResolverFuture::Connected(Some(req)) } else { - let resolver = self.resolver.as_ref().map(AsyncResolver::clone); - Either::Left(Box::pin(async move { - trace!("DNS resolver: resolving host {:?}", req.host()); - let resolver = if let Some(resolver) = resolver { - resolver - } else { - get_default_resolver() - .await - .expect("Failed to get default resolver") - }; - ResolverFuture::new(req, &resolver).await - })) + trace!("DNS resolver: resolving host {:?}", req.host()); + + match self { + Self::Default => { + let fut = Self::look_up(&req); + ResolverFuture::LookUp(fut, Some(req)) + } + + Self::Custom(resolver) => { + let resolver = Rc::clone(&resolver); + ResolverFuture::LookupCustom(Box::pin(async move { + let addrs = resolver + .lookup(req.host(), req.port()) + .await + .map_err(ConnectError::Resolver)?; + + let req = req.set_addrs(addrs); + + if req.addr.is_none() { + Err(ConnectError::NoRecords) + } else { + Ok(req) + } + })) + } + } } } } -type LookupIpFuture = Pin>>>; - -#[doc(hidden)] -/// Resolver future -pub struct ResolverFuture { - req: Option>, - lookup: LookupIpFuture, -} - -impl ResolverFuture { - pub fn new(req: Connect, resolver: &AsyncResolver) -> Self { - let host = if let Some(host) = req.host().splitn(2, ':').next() { - host - } else { - req.host() - }; - - // Clone data to be moved to the lookup future - let host_clone = host.to_owned(); - let resolver_clone = resolver.clone(); - - ResolverFuture { - lookup: Box::pin(async move { - let resolver = resolver_clone; - resolver.lookup_ip(host_clone).await - }), - req: Some(req), - } - } +pub enum ResolverFuture { + Connected(Option>), + LookUp( + JoinHandle>>, + Option>, + ), + LookupCustom(LocalBoxFuture<'static, Result, ConnectError>>), } impl Future for ResolverFuture { type Output = Result, ConnectError>; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let this = self.get_mut(); + match self.get_mut() { + Self::Connected(conn) => Poll::Ready(Ok(conn + .take() + .expect("ResolverFuture polled after finished"))), + Self::LookUp(fut, req) => { + let res = match ready!(Pin::new(fut).poll(cx)) { + Ok(Ok(res)) => Ok(res), + Ok(Err(e)) => Err(ConnectError::Resolver(Box::new(e))), + Err(e) => Err(ConnectError::Io(e.into())), + }; - match Pin::new(&mut this.lookup).poll(cx) { - Poll::Pending => Poll::Pending, - Poll::Ready(Ok(ips)) => { - let req = this.req.take().unwrap(); - let port = req.port(); - let req = req.set_addrs(ips.iter().map(|ip| SocketAddr::new(ip, port))); + let req = req.take().unwrap(); + + let addrs = res.map_err(|e| { + trace!( + "DNS resolver: failed to resolve host {:?} err: {:?}", + req.host(), + e + ); + e + })?; + + let req = req.set_addrs(addrs); trace!( "DNS resolver: host {:?} resolved to {:?}", @@ -192,14 +233,7 @@ impl Future for ResolverFuture { Poll::Ready(Ok(req)) } } - Poll::Ready(Err(e)) => { - trace!( - "DNS resolver: failed to resolve host {:?} err: {}", - this.req.as_ref().unwrap().host(), - e - ); - Poll::Ready(Err(e.into())) - } + Self::LookupCustom(fut) => fut.as_mut().poll(cx), } } } diff --git a/actix-tls/src/connect/service.rs b/actix-tls/src/connect/service.rs old mode 100644 new mode 100755 index 59fe20cc..6fa8a453 --- a/actix-tls/src/connect/service.rs +++ b/actix-tls/src/connect/service.rs @@ -1,42 +1,34 @@ -use std::future::Future; -use std::pin::Pin; -use std::task::{Context, Poll}; +use std::{ + future::Future, + pin::Pin, + task::{Context, Poll}, +}; use actix_rt::net::TcpStream; use actix_service::{Service, ServiceFactory}; -use either::Either; -use futures_util::future::{ok, Ready}; -use trust_dns_resolver::TokioAsyncResolver as AsyncResolver; +use futures_core::{future::LocalBoxFuture, ready}; use super::connect::{Address, Connect, Connection}; use super::connector::{TcpConnector, TcpConnectorFactory}; use super::error::ConnectError; use super::resolve::{Resolver, ResolverFactory}; -pub struct ConnectServiceFactory { - tcp: TcpConnectorFactory, - resolver: ResolverFactory, +pub struct ConnectServiceFactory { + tcp: TcpConnectorFactory, + resolver: ResolverFactory, } -impl ConnectServiceFactory { +impl ConnectServiceFactory { /// Construct new ConnectService factory - pub fn new() -> Self { + pub fn new(resolver: Resolver) -> Self { ConnectServiceFactory { - tcp: TcpConnectorFactory::default(), - resolver: ResolverFactory::default(), - } - } - - /// Construct new connect service with custom dns resolver - pub fn with_resolver(resolver: AsyncResolver) -> Self { - ConnectServiceFactory { - tcp: TcpConnectorFactory::default(), + tcp: TcpConnectorFactory, resolver: ResolverFactory::new(resolver), } } /// Construct new service - pub fn service(&self) -> ConnectService { + pub fn service(&self) -> ConnectService { ConnectService { tcp: self.tcp.service(), resolver: self.resolver.service(), @@ -44,7 +36,7 @@ impl ConnectServiceFactory { } /// Construct new tcp stream service - pub fn tcp_service(&self) -> TcpConnectService { + pub fn tcp_service(&self) -> TcpConnectService { TcpConnectService { tcp: self.tcp.service(), resolver: self.resolver.service(), @@ -52,44 +44,36 @@ impl ConnectServiceFactory { } } -impl Default for ConnectServiceFactory { - fn default() -> Self { - ConnectServiceFactory { - tcp: TcpConnectorFactory::default(), - resolver: ResolverFactory::default(), - } - } -} - -impl Clone for ConnectServiceFactory { +impl Clone for ConnectServiceFactory { fn clone(&self) -> Self { ConnectServiceFactory { - tcp: self.tcp.clone(), + tcp: self.tcp, resolver: self.resolver.clone(), } } } -impl ServiceFactory> for ConnectServiceFactory { +impl ServiceFactory> for ConnectServiceFactory { type Response = Connection; type Error = ConnectError; type Config = (); - type Service = ConnectService; + type Service = ConnectService; type InitError = (); - type Future = Ready>; + type Future = LocalBoxFuture<'static, Result>; fn new_service(&self, _: ()) -> Self::Future { - ok(self.service()) + let service = self.service(); + Box::pin(async move { Ok(service) }) } } #[derive(Clone)] -pub struct ConnectService { - tcp: TcpConnector, - resolver: Resolver, +pub struct ConnectService { + tcp: TcpConnector, + resolver: Resolver, } -impl Service> for ConnectService { +impl Service> for ConnectService { type Response = Connection; type Error = ConnectError; type Future = ConnectServiceResponse; @@ -98,65 +82,67 @@ impl Service> for ConnectService { fn call(&mut self, req: Connect) -> Self::Future { ConnectServiceResponse { - state: ConnectState::Resolve(self.resolver.call(req)), - tcp: self.tcp.clone(), + fut: ConnectFuture::Resolve(self.resolver.call(req)), + tcp: self.tcp, } } } -enum ConnectState { - Resolve( as Service>>::Future), - Connect( as Service>>::Future), +// helper enum to generic over futures of resolve and connect phase. +pub(crate) enum ConnectFuture { + Resolve(>>::Future), + Connect(>>::Future), } -impl ConnectState { - #[allow(clippy::type_complexity)] - fn poll( +// helper enum to contain the future output of ConnectFuture +pub(crate) enum ConnectOutput { + Resolved(Connect), + Connected(Connection), +} + +impl ConnectFuture { + fn poll_connect( &mut self, cx: &mut Context<'_>, - ) -> Either, ConnectError>>, Connect> { + ) -> Poll, ConnectError>> { match self { - ConnectState::Resolve(ref mut fut) => match Pin::new(fut).poll(cx) { - Poll::Pending => Either::Left(Poll::Pending), - Poll::Ready(Ok(res)) => Either::Right(res), - Poll::Ready(Err(err)) => Either::Left(Poll::Ready(Err(err))), - }, - ConnectState::Connect(ref mut fut) => Either::Left(Pin::new(fut).poll(cx)), + ConnectFuture::Resolve(ref mut fut) => { + Pin::new(fut).poll(cx).map_ok(ConnectOutput::Resolved) + } + ConnectFuture::Connect(ref mut fut) => { + Pin::new(fut).poll(cx).map_ok(ConnectOutput::Connected) + } } } } pub struct ConnectServiceResponse { - state: ConnectState, - tcp: TcpConnector, + fut: ConnectFuture, + tcp: TcpConnector, } impl Future for ConnectServiceResponse { type Output = Result, ConnectError>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let res = match self.state.poll(cx) { - Either::Right(res) => { - self.state = ConnectState::Connect(self.tcp.call(res)); - self.state.poll(cx) + loop { + match ready!(self.fut.poll_connect(cx))? { + ConnectOutput::Resolved(res) => { + self.fut = ConnectFuture::Connect(self.tcp.call(res)); + } + ConnectOutput::Connected(res) => return Poll::Ready(Ok(res)), } - Either::Left(res) => return res, - }; - - match res { - Either::Left(res) => res, - Either::Right(_) => panic!(), } } } #[derive(Clone)] -pub struct TcpConnectService { - tcp: TcpConnector, - resolver: Resolver, +pub struct TcpConnectService { + tcp: TcpConnector, + resolver: Resolver, } -impl Service> for TcpConnectService { +impl Service> for TcpConnectService { type Response = TcpStream; type Error = ConnectError; type Future = TcpConnectServiceResponse; @@ -165,61 +151,28 @@ impl Service> for TcpConnectService { fn call(&mut self, req: Connect) -> Self::Future { TcpConnectServiceResponse { - state: TcpConnectState::Resolve(self.resolver.call(req)), - tcp: self.tcp.clone(), + fut: ConnectFuture::Resolve(self.resolver.call(req)), + tcp: self.tcp, } } } -enum TcpConnectState { - Resolve( as Service>>::Future), - Connect( as Service>>::Future), -} - -impl TcpConnectState { - fn poll( - &mut self, - cx: &mut Context<'_>, - ) -> Either>, Connect> { - match self { - TcpConnectState::Resolve(ref mut fut) => match Pin::new(fut).poll(cx) { - Poll::Pending => (), - Poll::Ready(Ok(res)) => return Either::Right(res), - Poll::Ready(Err(err)) => return Either::Left(Poll::Ready(Err(err))), - }, - TcpConnectState::Connect(ref mut fut) => { - if let Poll::Ready(res) = Pin::new(fut).poll(cx) { - return match res { - Ok(conn) => Either::Left(Poll::Ready(Ok(conn.into_parts().0))), - Err(err) => Either::Left(Poll::Ready(Err(err))), - }; - } - } - } - Either::Left(Poll::Pending) - } -} - pub struct TcpConnectServiceResponse { - state: TcpConnectState, - tcp: TcpConnector, + fut: ConnectFuture, + tcp: TcpConnector, } impl Future for TcpConnectServiceResponse { type Output = Result; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let res = match self.state.poll(cx) { - Either::Right(res) => { - self.state = TcpConnectState::Connect(self.tcp.call(res)); - self.state.poll(cx) + loop { + match ready!(self.fut.poll_connect(cx))? { + ConnectOutput::Resolved(res) => { + self.fut = ConnectFuture::Connect(self.tcp.call(res)); + } + ConnectOutput::Connected(conn) => return Poll::Ready(Ok(conn.into_parts().0)), } - Either::Left(res) => return res, - }; - - match res { - Either::Left(res) => res, - Either::Right(_) => panic!(), } } } diff --git a/actix-tls/src/connect/ssl/openssl.rs b/actix-tls/src/connect/ssl/openssl.rs old mode 100644 new mode 100755 index 5193ce37..d5d71edc --- a/actix-tls/src/connect/ssl/openssl.rs +++ b/actix-tls/src/connect/ssl/openssl.rs @@ -1,23 +1,23 @@ -use std::future::Future; -use std::marker::PhantomData; -use std::pin::Pin; -use std::task::{Context, Poll}; -use std::{fmt, io}; +use std::{ + fmt, + future::Future, + io, + pin::Pin, + task::{Context, Poll}, +}; use actix_codec::{AsyncRead, AsyncWrite}; use actix_rt::net::TcpStream; use actix_service::{Service, ServiceFactory}; -use futures_util::{ - future::{ready, Either, Ready}, - ready, -}; +use futures_core::{future::LocalBoxFuture, ready}; use log::trace; + pub use openssl::ssl::{Error as SslError, HandshakeError, SslConnector, SslMethod}; pub use tokio_openssl::SslStream; -use trust_dns_resolver::TokioAsyncResolver as AsyncResolver; +use crate::connect::resolve::Resolve; use crate::connect::{ - Address, Connect, ConnectError, ConnectService, ConnectServiceFactory, Connection, + Address, Connect, ConnectError, ConnectService, ConnectServiceFactory, Connection, Resolver, }; /// OpenSSL connector factory @@ -29,9 +29,7 @@ impl OpensslConnector { pub fn new(connector: SslConnector) -> Self { OpensslConnector { connector } } -} -impl OpensslConnector { pub fn service(connector: SslConnector) -> OpensslConnectorService { OpensslConnectorService { connector } } @@ -55,12 +53,11 @@ where type Config = (); type Service = OpensslConnectorService; type InitError = (); - type Future = Ready>; + type Future = LocalBoxFuture<'static, Result>; fn new_service(&self, _: ()) -> Self::Future { - ready(Ok(OpensslConnectorService { - connector: self.connector.clone(), - })) + let connector = self.connector.clone(); + Box::pin(async { Ok(OpensslConnectorService { connector }) }) } } @@ -83,29 +80,27 @@ where { type Response = Connection>; type Error = io::Error; - #[allow(clippy::type_complexity)] - type Future = Either, Ready>>; + type Future = ConnectAsyncExt; actix_service::always_ready!(); fn call(&mut self, stream: Connection) -> Self::Future { trace!("SSL Handshake start for: {:?}", stream.host()); let (io, stream) = stream.replace(()); - let host = stream.host().to_string(); + let host = stream.host(); - match self.connector.configure() { - Err(e) => Either::Right(ready(Err(io::Error::new(io::ErrorKind::Other, e)))), - Ok(config) => { - let ssl = config - .into_ssl(&host) - .expect("SSL connect configuration was invalid."); + let config = self + .connector + .configure() + .expect("SSL connect configuration was invalid."); - Either::Left(ConnectAsyncExt { - io: Some(SslStream::new(ssl, io).unwrap()), - stream: Some(stream), - _t: PhantomData, - }) - } + let ssl = config + .into_ssl(host) + .expect("SSL connect configuration was invalid."); + + ConnectAsyncExt { + io: Some(SslStream::new(ssl, io).unwrap()), + stream: Some(stream), } } } @@ -113,7 +108,6 @@ where pub struct ConnectAsyncExt { io: Option>, stream: Option>, - _t: PhantomData, } impl Future for ConnectAsyncExt @@ -139,30 +133,30 @@ where } } -pub struct OpensslConnectServiceFactory { - tcp: ConnectServiceFactory, +pub struct OpensslConnectServiceFactory { + tcp: ConnectServiceFactory, openssl: OpensslConnector, } -impl OpensslConnectServiceFactory { +impl OpensslConnectServiceFactory { /// Construct new OpensslConnectService factory pub fn new(connector: SslConnector) -> Self { OpensslConnectServiceFactory { - tcp: ConnectServiceFactory::default(), + tcp: ConnectServiceFactory::new(Resolver::Default), openssl: OpensslConnector::new(connector), } } /// Construct new connect service with custom DNS resolver - pub fn with_resolver(connector: SslConnector, resolver: AsyncResolver) -> Self { + pub fn with_resolver(connector: SslConnector, resolver: impl Resolve + 'static) -> Self { OpensslConnectServiceFactory { - tcp: ConnectServiceFactory::with_resolver(resolver), + tcp: ConnectServiceFactory::new(Resolver::new_custom(resolver)), openssl: OpensslConnector::new(connector), } } /// Construct OpenSSL connect service - pub fn service(&self) -> OpensslConnectService { + pub fn service(&self) -> OpensslConnectService { OpensslConnectService { tcp: self.tcp.service(), openssl: OpensslConnectorService { @@ -172,7 +166,7 @@ impl OpensslConnectServiceFactory { } } -impl Clone for OpensslConnectServiceFactory { +impl Clone for OpensslConnectServiceFactory { fn clone(&self) -> Self { OpensslConnectServiceFactory { tcp: self.tcp.clone(), @@ -181,26 +175,27 @@ impl Clone for OpensslConnectServiceFactory { } } -impl ServiceFactory> for OpensslConnectServiceFactory { +impl ServiceFactory> for OpensslConnectServiceFactory { type Response = SslStream; type Error = ConnectError; type Config = (); - type Service = OpensslConnectService; + type Service = OpensslConnectService; type InitError = (); - type Future = Ready>; + type Future = LocalBoxFuture<'static, Result>; fn new_service(&self, _: ()) -> Self::Future { - ready(Ok(self.service())) + let service = self.service(); + Box::pin(async { Ok(service) }) } } #[derive(Clone)] -pub struct OpensslConnectService { - tcp: ConnectService, +pub struct OpensslConnectService { + tcp: ConnectService, openssl: OpensslConnectorService, } -impl Service> for OpensslConnectService { +impl Service> for OpensslConnectService { type Response = SslStream; type Error = ConnectError; type Future = OpensslConnectServiceResponse; @@ -217,7 +212,7 @@ impl Service> for OpensslConnectService { } pub struct OpensslConnectServiceResponse { - fut1: Option< as Service>>::Future>, + fut1: Option<>>::Future>, fut2: Option<>>::Future>, openssl: OpensslConnectorService, } diff --git a/actix-tls/src/connect/ssl/rustls.rs b/actix-tls/src/connect/ssl/rustls.rs old mode 100644 new mode 100755 index 390ba413..e12ab7ec --- a/actix-tls/src/connect/ssl/rustls.rs +++ b/actix-tls/src/connect/ssl/rustls.rs @@ -1,18 +1,18 @@ -use std::fmt; -use std::future::Future; -use std::pin::Pin; -use std::sync::Arc; -use std::task::{Context, Poll}; +use std::{ + fmt, + future::Future, + pin::Pin, + sync::Arc, + task::{Context, Poll}, +}; pub use rustls::Session; pub use tokio_rustls::{client::TlsStream, rustls::ClientConfig}; +pub use webpki_roots::TLS_SERVER_ROOTS; use actix_codec::{AsyncRead, AsyncWrite}; use actix_service::{Service, ServiceFactory}; -use futures_util::{ - future::{ready, Ready}, - ready, -}; +use futures_core::{future::LocalBoxFuture, ready}; use log::trace; use tokio_rustls::{Connect, TlsConnector}; use webpki::DNSNameRef; @@ -53,12 +53,11 @@ where type Config = (); type Service = RustlsConnectorService; type InitError = (); - type Future = Ready>; + type Future = LocalBoxFuture<'static, Result>; fn new_service(&self, _: ()) -> Self::Future { - ready(Ok(RustlsConnectorService { - connector: self.connector.clone(), - })) + let connector = self.connector.clone(); + Box::pin(async { Ok(RustlsConnectorService { connector }) }) } } diff --git a/actix-tls/tests/test_connect.rs b/actix-tls/tests/test_connect.rs old mode 100644 new mode 100755 index aa773c7f..32d2ac0f --- a/actix-tls/tests/test_connect.rs +++ b/actix-tls/tests/test_connect.rs @@ -1,17 +1,15 @@ use std::io; +use std::net::SocketAddr; use actix_codec::{BytesCodec, Framed}; use actix_rt::net::TcpStream; use actix_server::TestServer; use actix_service::{fn_service, Service, ServiceFactory}; use bytes::Bytes; +use futures_core::future::LocalBoxFuture; use futures_util::sink::SinkExt; -use actix_tls::connect::{ - self as actix_connect, - resolver::{ResolverConfig, ResolverOpts}, - Connect, -}; +use actix_tls::connect::{self as actix_connect, Connect, Resolve, Resolver}; #[cfg(all(feature = "connect", feature = "openssl"))] #[actix_rt::test] @@ -57,14 +55,13 @@ async fn test_static_str() { }) }); - let resolver = actix_connect::start_default_resolver().await.unwrap(); - let mut conn = actix_connect::new_connector(resolver.clone()); + let mut conn = actix_connect::default_connector(); let con = conn.call(Connect::with("10", srv.addr())).await.unwrap(); assert_eq!(con.peer_addr().unwrap(), srv.addr()); let connect = Connect::new(srv.host().to_owned()); - let mut conn = actix_connect::new_connector(resolver); + let mut conn = actix_connect::default_connector(); let con = conn.call(connect).await; assert!(con.is_err()); } @@ -79,10 +76,53 @@ async fn test_new_service() { }) }); - let resolver = - actix_connect::start_resolver(ResolverConfig::default(), ResolverOpts::default()) - .await - .unwrap(); + let factory = actix_connect::default_connector_factory(); + + let mut conn = factory.new_service(()).await.unwrap(); + let con = conn.call(Connect::with("10", srv.addr())).await.unwrap(); + assert_eq!(con.peer_addr().unwrap(), srv.addr()); +} + +#[actix_rt::test] +async fn test_custom_resolver() { + use trust_dns_resolver::TokioAsyncResolver; + + let srv = TestServer::with(|| { + fn_service(|io: TcpStream| async { + let mut framed = Framed::new(io, BytesCodec); + framed.send(Bytes::from_static(b"test")).await?; + Ok::<_, io::Error>(()) + }) + }); + + struct MyResolver { + trust_dns: TokioAsyncResolver, + } + + impl Resolve for MyResolver { + fn lookup<'a>( + &'a self, + host: &'a str, + port: u16, + ) -> LocalBoxFuture<'a, Result, Box>> { + Box::pin(async move { + let res = self + .trust_dns + .lookup_ip(host) + .await? + .iter() + .map(|ip| SocketAddr::new(ip, port)) + .collect(); + Ok(res) + }) + } + } + + let resolver = MyResolver { + trust_dns: TokioAsyncResolver::tokio_from_system_conf().unwrap(), + }; + + let resolver = Resolver::new_custom(resolver); let factory = actix_connect::new_connector_factory(resolver); diff --git a/actix-utils/Cargo.toml b/actix-utils/Cargo.toml index f038414c..c82cf79e 100644 --- a/actix-utils/Cargo.toml +++ b/actix-utils/Cargo.toml @@ -17,7 +17,7 @@ path = "src/lib.rs" [dependencies] actix-codec = "0.4.0-beta.1" -actix-rt = "2.0.0-beta.2" +actix-rt = { version = "2.0.0-beta.2", default-features = false } actix-service = "2.0.0-beta.3" futures-core = { version = "0.3.7", default-features = false } From 636cef8868462c96cce50d1b5ea7d91a9e8dd0a2 Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Fri, 22 Jan 2021 19:06:22 -0800 Subject: [PATCH 40/73] service trait takes shared self reference (#247) --- actix-router/CHANGES.md | 3 ++ actix-router/src/router.rs | 18 +++++++ actix-server/Cargo.toml | 1 + actix-server/src/service.rs | 4 +- actix-service/CHANGES.md | 6 +++ actix-service/src/and_then.rs | 60 +++++++++------------ actix-service/src/apply.rs | 32 ++++++------ actix-service/src/apply_cfg.rs | 78 +++++++++++++--------------- actix-service/src/boxed.rs | 4 +- actix-service/src/fn_service.rs | 32 ++++++------ actix-service/src/lib.rs | 38 +++++++------- actix-service/src/map.rs | 10 ++-- actix-service/src/map_err.rs | 14 ++--- actix-service/src/pipeline.rs | 4 +- actix-service/src/then.rs | 60 +++++++++------------ actix-service/src/transform.rs | 23 ++++---- actix-tls/src/accept/nativetls.rs | 4 +- actix-tls/src/accept/openssl.rs | 4 +- actix-tls/src/accept/rustls.rs | 4 +- actix-tls/src/connect/connector.rs | 2 +- actix-tls/src/connect/resolve.rs | 2 +- actix-tls/src/connect/service.rs | 4 +- actix-tls/src/connect/ssl/openssl.rs | 4 +- actix-tls/src/connect/ssl/rustls.rs | 2 +- actix-tls/tests/test_connect.rs | 17 +++--- actix-tracing/src/lib.rs | 4 +- actix-utils/src/timeout.rs | 10 ++-- 27 files changed, 225 insertions(+), 219 deletions(-) diff --git a/actix-router/CHANGES.md b/actix-router/CHANGES.md index df93e5be..581243fb 100644 --- a/actix-router/CHANGES.md +++ b/actix-router/CHANGES.md @@ -1,6 +1,9 @@ # Changes ## Unreleased - 2021-xx-xx +* Add `Router::recognize_checked` [#247] + +[#247]: https://github.com/actix/actix-net/pull/247 ## 0.2.6 - 2021-01-09 diff --git a/actix-router/src/router.rs b/actix-router/src/router.rs index bcbe61f9..aeb1aa36 100644 --- a/actix-router/src/router.rs +++ b/actix-router/src/router.rs @@ -45,6 +45,24 @@ impl Router { None } + pub fn recognize_checked( + &self, + resource: &mut R, + check: F, + ) -> Option<(&T, ResourceId)> + where + F: Fn(&R, &Option) -> bool, + R: Resource

, + P: ResourcePath, + { + for item in self.0.iter() { + if item.0.match_path_checked(resource, &check, &item.2) { + return Some((&item.1, ResourceId(item.0.id()))); + } + } + None + } + pub fn recognize_mut_checked( &mut self, resource: &mut R, diff --git a/actix-server/Cargo.toml b/actix-server/Cargo.toml index 25095039..c366124a 100755 --- a/actix-server/Cargo.toml +++ b/actix-server/Cargo.toml @@ -36,6 +36,7 @@ slab = "0.4" tokio = { version = "1", features = ["sync"] } [dev-dependencies] +actix-rt = "2.0.0-beta.2" bytes = "1" env_logger = "0.8" futures-util = { version = "0.3.7", default-features = false, features = ["sink"] } diff --git a/actix-server/src/service.rs b/actix-server/src/service.rs index 04b7dce8..63d2c1f5 100644 --- a/actix-server/src/service.rs +++ b/actix-server/src/service.rs @@ -58,11 +58,11 @@ where type Error = (); type Future = Ready>; - fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll> { + fn poll_ready(&self, ctx: &mut Context<'_>) -> Poll> { self.service.poll_ready(ctx).map_err(|_| ()) } - fn call(&mut self, (guard, req): (Option, MioStream)) -> Self::Future { + fn call(&self, (guard, req): (Option, MioStream)) -> Self::Future { ready(match FromStream::from_mio(req) { Ok(stream) => { let f = self.service.call(stream); diff --git a/actix-service/CHANGES.md b/actix-service/CHANGES.md index 4bf41f0e..4a99d60a 100644 --- a/actix-service/CHANGES.md +++ b/actix-service/CHANGES.md @@ -1,6 +1,12 @@ # Changes ## Unreleased - 2021-xx-xx +* `Service::poll_ready` and `Service::call` take `&self`. [#247] +* `apply_fn` and `apply_fn_factory` would take `Fn(Req, &Service)` function type [#247] +* `apply_cfg` and `apply_cfg_factory` would take `Fn(Req, &Service)` function type [#247] +* `fn_service` module would take `Fn(Req)` function type. [#247] + +[#247]: https://github.com/actix/actix-net/pull/247 ## 2.0.0-beta.3 - 2021-01-09 diff --git a/actix-service/src/and_then.rs b/actix-service/src/and_then.rs index fd24cb56..54a132da 100644 --- a/actix-service/src/and_then.rs +++ b/actix-service/src/and_then.rs @@ -1,12 +1,12 @@ -use alloc::rc::Rc; use core::{ - cell::RefCell, future::Future, marker::PhantomData, pin::Pin, task::{Context, Poll}, }; +use alloc::rc::Rc; +use futures_core::ready; use pin_project_lite::pin_project; use super::{Service, ServiceFactory}; @@ -15,7 +15,7 @@ use super::{Service, ServiceFactory}; /// of another service which completes successfully. /// /// This is created by the `Pipeline::and_then` method. -pub(crate) struct AndThenService(Rc>, PhantomData); +pub(crate) struct AndThenService(Rc<(A, B)>, PhantomData); impl AndThenService { /// Create new `AndThen` combinator @@ -24,7 +24,7 @@ impl AndThenService { A: Service, B: Service, { - Self(Rc::new(RefCell::new((a, b))), PhantomData) + Self(Rc::new((a, b)), PhantomData) } } @@ -43,20 +43,20 @@ where type Error = A::Error; type Future = AndThenServiceResponse; - fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { - let mut srv = self.0.borrow_mut(); - let not_ready = !srv.0.poll_ready(cx)?.is_ready(); - if !srv.1.poll_ready(cx)?.is_ready() || not_ready { + fn poll_ready(&self, cx: &mut Context<'_>) -> Poll> { + let (a, b) = &*self.0; + let not_ready = !a.poll_ready(cx)?.is_ready(); + if !b.poll_ready(cx)?.is_ready() || not_ready { Poll::Pending } else { Poll::Ready(Ok(())) } } - fn call(&mut self, req: Req) -> Self::Future { + fn call(&self, req: Req) -> Self::Future { AndThenServiceResponse { state: State::A { - fut: self.0.borrow_mut().0.call(req), + fut: self.0 .0.call(req), b: Some(self.0.clone()), }, } @@ -84,13 +84,12 @@ pin_project! { A { #[pin] fut: A::Future, - b: Option>>, + b: Option>, }, B { #[pin] fut: B::Future, }, - Empty, } } @@ -105,23 +104,14 @@ where let mut this = self.as_mut().project(); match this.state.as_mut().project() { - StateProj::A { fut, b } => match fut.poll(cx)? { - Poll::Ready(res) => { - let b = b.take().unwrap(); - this.state.set(State::Empty); // drop fut A - let fut = b.borrow_mut().1.call(res); - this.state.set(State::B { fut }); - self.poll(cx) - } - Poll::Pending => Poll::Pending, - }, - StateProj::B { fut } => fut.poll(cx).map(|r| { - this.state.set(State::Empty); - r - }), - StateProj::Empty => { - panic!("future must not be polled after it returned `Poll::Ready`") + StateProj::A { fut, b } => { + let res = ready!(fut.poll(cx))?; + let b = b.take().unwrap(); + let fut = b.1.call(res); + this.state.set(State::B { fut }); + self.poll(cx) } + StateProj::B { fut } => fut.poll(cx), } } } @@ -292,12 +282,12 @@ mod tests { type Error = (); type Future = Ready>; - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { + fn poll_ready(&self, _: &mut Context<'_>) -> Poll> { self.0.set(self.0.get() + 1); Poll::Ready(Ok(())) } - fn call(&mut self, req: &'static str) -> Self::Future { + fn call(&self, req: &'static str) -> Self::Future { ok(req) } } @@ -310,12 +300,12 @@ mod tests { type Error = (); type Future = Ready>; - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { + fn poll_ready(&self, _: &mut Context<'_>) -> Poll> { self.0.set(self.0.get() + 1); Poll::Ready(Ok(())) } - fn call(&mut self, req: &'static str) -> Self::Future { + fn call(&self, req: &'static str) -> Self::Future { ok((req, "srv2")) } } @@ -323,7 +313,7 @@ mod tests { #[actix_rt::test] async fn test_poll_ready() { let cnt = Rc::new(Cell::new(0)); - let mut srv = pipeline(Srv1(cnt.clone())).and_then(Srv2(cnt.clone())); + let srv = pipeline(Srv1(cnt.clone())).and_then(Srv2(cnt.clone())); let res = lazy(|cx| srv.poll_ready(cx)).await; assert_eq!(res, Poll::Ready(Ok(()))); assert_eq!(cnt.get(), 2); @@ -332,7 +322,7 @@ mod tests { #[actix_rt::test] async fn test_call() { let cnt = Rc::new(Cell::new(0)); - let mut srv = pipeline(Srv1(cnt.clone())).and_then(Srv2(cnt)); + let srv = pipeline(Srv1(cnt.clone())).and_then(Srv2(cnt)); let res = srv.call("srv1").await; assert!(res.is_ok()); assert_eq!(res.unwrap(), ("srv1", "srv2")); @@ -346,7 +336,7 @@ mod tests { pipeline_factory(fn_factory(move || ready(Ok::<_, ()>(Srv1(cnt2.clone()))))) .and_then(move || ready(Ok(Srv2(cnt.clone())))); - let mut srv = new_srv.new_service(()).await.unwrap(); + let srv = new_srv.new_service(()).await.unwrap(); let res = srv.call("srv1").await; assert!(res.is_ok()); assert_eq!(res.unwrap(), ("srv1", "srv2")); diff --git a/actix-service/src/apply.rs b/actix-service/src/apply.rs index 9b0c4025..9a7e27d2 100644 --- a/actix-service/src/apply.rs +++ b/actix-service/src/apply.rs @@ -20,7 +20,7 @@ pub fn apply_fn( where I: IntoService, S: Service, - F: FnMut(Req, &mut S) -> Fut, + F: Fn(Req, &S) -> Fut, Fut: Future>, { Apply::new(service.into_service(), wrap_fn) @@ -36,7 +36,7 @@ pub fn apply_fn_factory( where I: IntoServiceFactory, SF: ServiceFactory, - F: FnMut(Req, &mut SF::Service) -> Fut + Clone, + F: Fn(Req, &SF::Service) -> Fut + Clone, Fut: Future>, { ApplyFactory::new(service.into_factory(), f) @@ -57,7 +57,7 @@ where impl Apply where S: Service, - F: FnMut(Req, &mut S) -> Fut, + F: Fn(Req, &S) -> Fut, Fut: Future>, { /// Create new `Apply` combinator @@ -73,7 +73,7 @@ where impl Clone for Apply where S: Service + Clone, - F: FnMut(Req, &mut S) -> Fut + Clone, + F: Fn(Req, &S) -> Fut + Clone, Fut: Future>, { fn clone(&self) -> Self { @@ -88,7 +88,7 @@ where impl Service for Apply where S: Service, - F: FnMut(Req, &mut S) -> Fut, + F: Fn(Req, &S) -> Fut, Fut: Future>, { type Response = Res; @@ -97,8 +97,8 @@ where crate::forward_ready!(service); - fn call(&mut self, req: Req) -> Self::Future { - (self.wrap_fn)(req, &mut self.service) + fn call(&self, req: Req) -> Self::Future { + (self.wrap_fn)(req, &self.service) } } @@ -112,7 +112,7 @@ pub struct ApplyFactory { impl ApplyFactory where SF: ServiceFactory, - F: FnMut(Req, &mut SF::Service) -> Fut + Clone, + F: Fn(Req, &SF::Service) -> Fut + Clone, Fut: Future>, { /// Create new `ApplyFactory` new service instance @@ -128,7 +128,7 @@ where impl Clone for ApplyFactory where SF: ServiceFactory + Clone, - F: FnMut(Req, &mut SF::Service) -> Fut + Clone, + F: Fn(Req, &SF::Service) -> Fut + Clone, Fut: Future>, { fn clone(&self) -> Self { @@ -144,7 +144,7 @@ impl ServiceFactory for ApplyFactory where SF: ServiceFactory, - F: FnMut(Req, &mut SF::Service) -> Fut + Clone, + F: Fn(Req, &SF::Service) -> Fut + Clone, Fut: Future>, { type Response = Res; @@ -165,7 +165,7 @@ pin_project! { pub struct ApplyServiceFactoryResponse where SF: ServiceFactory, - F: FnMut(Req, &mut SF::Service) -> Fut, + F: Fn(Req, &SF::Service) -> Fut, Fut: Future>, { #[pin] @@ -178,7 +178,7 @@ pin_project! { impl ApplyServiceFactoryResponse where SF: ServiceFactory, - F: FnMut(Req, &mut SF::Service) -> Fut, + F: Fn(Req, &SF::Service) -> Fut, Fut: Future>, { fn new(fut: SF::Future, wrap_fn: F) -> Self { @@ -194,7 +194,7 @@ impl Future for ApplyServiceFactoryResponse where SF: ServiceFactory, - F: FnMut(Req, &mut SF::Service) -> Fut, + F: Fn(Req, &SF::Service) -> Fut, Fut: Future>, { type Output = Result, SF::InitError>; @@ -226,14 +226,14 @@ mod tests { crate::always_ready!(); - fn call(&mut self, _: ()) -> Self::Future { + fn call(&self, _: ()) -> Self::Future { ok(()) } } #[actix_rt::test] async fn test_call() { - let mut srv = pipeline(apply_fn(Srv, |req: &'static str, srv| { + let srv = pipeline(apply_fn(Srv, |req: &'static str, srv| { let fut = srv.call(()); async move { fut.await.unwrap(); @@ -261,7 +261,7 @@ mod tests { }, )); - let mut srv = new_srv.new_service(()).await.unwrap(); + let srv = new_srv.new_service(()).await.unwrap(); assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(()))); diff --git a/actix-service/src/apply_cfg.rs b/actix-service/src/apply_cfg.rs index 3e111231..276efc0f 100644 --- a/actix-service/src/apply_cfg.rs +++ b/actix-service/src/apply_cfg.rs @@ -1,17 +1,17 @@ -use alloc::rc::Rc; use core::{ - cell::RefCell, future::Future, marker::PhantomData, pin::Pin, task::{Context, Poll}, }; +use alloc::rc::Rc; +use futures_core::ready; use pin_project_lite::pin_project; use crate::{Service, ServiceFactory}; -/// Convert `Fn(Config, &mut Service1) -> Future` fn to a service factory. +/// Convert `Fn(Config, &Service1) -> Future` fn to a service factory. pub fn apply_cfg( srv: S1, f: F, @@ -26,17 +26,17 @@ pub fn apply_cfg( > + Clone where S1: Service, - F: FnMut(Cfg, &mut S1) -> Fut, + F: Fn(Cfg, &S1) -> Fut, Fut: Future>, S2: Service, { ApplyConfigService { - srv: Rc::new(RefCell::new((srv, f))), + srv: Rc::new((srv, f)), _phantom: PhantomData, } } -/// Convert `Fn(Config, &mut ServiceFactory1) -> Future` fn to a service factory. +/// Convert `Fn(Config, &ServiceFactory1) -> Future` fn to a service factory. /// /// Service1 get constructed from `T` factory. pub fn apply_cfg_factory( @@ -52,33 +52,33 @@ pub fn apply_cfg_factory( > + Clone where SF: ServiceFactory, - F: FnMut(Cfg, &mut SF::Service) -> Fut, + F: Fn(Cfg, &SF::Service) -> Fut, SF::InitError: From, Fut: Future>, S: Service, { ApplyConfigServiceFactory { - srv: Rc::new(RefCell::new((factory, f))), + srv: Rc::new((factory, f)), _phantom: PhantomData, } } -/// Convert `Fn(Config, &mut Server) -> Future` fn to NewService\ +/// Convert `Fn(Config, &Server) -> Future` fn to NewService\ struct ApplyConfigService where S1: Service, - F: FnMut(Cfg, &mut S1) -> Fut, + F: Fn(Cfg, &S1) -> Fut, Fut: Future>, S2: Service, { - srv: Rc>, + srv: Rc<(S1, F)>, _phantom: PhantomData<(Cfg, Req, Fut, S2)>, } impl Clone for ApplyConfigService where S1: Service, - F: FnMut(Cfg, &mut S1) -> Fut, + F: Fn(Cfg, &S1) -> Fut, Fut: Future>, S2: Service, { @@ -94,20 +94,20 @@ impl ServiceFactory for ApplyConfigService where S1: Service, - F: FnMut(Cfg, &mut S1) -> Fut, + F: Fn(Cfg, &S1) -> Fut, Fut: Future>, S2: Service, { - type Config = Cfg; type Response = S2::Response; type Error = S2::Error; + type Config = Cfg; type Service = S2; type InitError = Err; type Future = Fut; fn new_service(&self, cfg: Cfg) -> Self::Future { - let (t, f) = &mut *self.srv.borrow_mut(); + let (t, f) = &*self.srv; f(cfg, t) } } @@ -116,18 +116,18 @@ where struct ApplyConfigServiceFactory where SF: ServiceFactory, - F: FnMut(Cfg, &mut SF::Service) -> Fut, + F: Fn(Cfg, &SF::Service) -> Fut, Fut: Future>, S: Service, { - srv: Rc>, + srv: Rc<(SF, F)>, _phantom: PhantomData<(Cfg, Req, Fut, S)>, } impl Clone for ApplyConfigServiceFactory where SF: ServiceFactory, - F: FnMut(Cfg, &mut SF::Service) -> Fut, + F: Fn(Cfg, &SF::Service) -> Fut, Fut: Future>, S: Service, { @@ -144,13 +144,13 @@ impl ServiceFactory where SF: ServiceFactory, SF::InitError: From, - F: FnMut(Cfg, &mut SF::Service) -> Fut, + F: Fn(Cfg, &SF::Service) -> Fut, Fut: Future>, S: Service, { - type Config = Cfg; type Response = S::Response; type Error = S::Error; + type Config = Cfg; type Service = S; type InitError = SF::InitError; @@ -161,7 +161,7 @@ where cfg: Some(cfg), store: self.srv.clone(), state: State::A { - fut: self.srv.borrow().0.new_service(()), + fut: self.srv.0.new_service(()), }, } } @@ -172,12 +172,12 @@ pin_project! { where SF: ServiceFactory, SF::InitError: From, - F: FnMut(Cfg, &mut SF::Service) -> Fut, + F: Fn(Cfg, &SF::Service) -> Fut, Fut: Future>, S: Service, { cfg: Option, - store: Rc>, + store: Rc<(SF, F)>, #[pin] state: State, } @@ -203,7 +203,7 @@ impl Future where SF: ServiceFactory, SF::InitError: From, - F: FnMut(Cfg, &mut SF::Service) -> Fut, + F: Fn(Cfg, &SF::Service) -> Fut, Fut: Future>, S: Service, { @@ -213,24 +213,20 @@ where let mut this = self.as_mut().project(); match this.state.as_mut().project() { - StateProj::A { fut } => match fut.poll(cx)? { - Poll::Pending => Poll::Pending, - Poll::Ready(svc) => { - this.state.set(State::B { svc }); - self.poll(cx) + StateProj::A { fut } => { + let svc = ready!(fut.poll(cx))?; + this.state.set(State::B { svc }); + self.poll(cx) + } + StateProj::B { svc } => { + ready!(svc.poll_ready(cx))?; + { + let (_, f) = &**this.store; + let fut = f(this.cfg.take().unwrap(), svc); + this.state.set(State::C { fut }); } - }, - StateProj::B { svc } => match svc.poll_ready(cx)? { - Poll::Ready(_) => { - { - let (_, f) = &mut *this.store.borrow_mut(); - let fut = f(this.cfg.take().unwrap(), svc); - this.state.set(State::C { fut }); - } - self.poll(cx) - } - Poll::Pending => Poll::Pending, - }, + self.poll(cx) + } StateProj::C { fut } => fut.poll(cx), } } diff --git a/actix-service/src/boxed.rs b/actix-service/src/boxed.rs index 5c4557df..6ad2eaf4 100644 --- a/actix-service/src/boxed.rs +++ b/actix-service/src/boxed.rs @@ -131,11 +131,11 @@ where type Error = Err; type Future = BoxFuture>; - fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll> { + fn poll_ready(&self, ctx: &mut Context<'_>) -> Poll> { self.0.poll_ready(ctx) } - fn call(&mut self, req: Req) -> Self::Future { + fn call(&self, req: Req) -> Self::Future { Box::pin(self.0.call(req)) } } diff --git a/actix-service/src/fn_service.rs b/actix-service/src/fn_service.rs index 5f09c580..230f437b 100644 --- a/actix-service/src/fn_service.rs +++ b/actix-service/src/fn_service.rs @@ -7,7 +7,7 @@ pub fn fn_service( f: F, ) -> FnServiceFactory where - F: FnMut(Req) -> Fut + Clone, + F: Fn(Req) -> Fut + Clone, Fut: Future>, { FnServiceFactory::new(f) @@ -39,7 +39,7 @@ where /// }); /// /// // construct new service -/// let mut srv = factory.new_service(()).await?; +/// let srv = factory.new_service(()).await?; /// /// // now we can use `div` service /// let result = srv.call((10, 20)).await?; @@ -81,7 +81,7 @@ where /// }); /// /// // construct new service with config argument -/// let mut srv = factory.new_service(10).await?; +/// let srv = factory.new_service(10).await?; /// /// let result = srv.call(10).await?; /// assert_eq!(result, 100); @@ -132,7 +132,7 @@ where impl Service for FnService where - F: FnMut(Req) -> Fut, + F: Fn(Req) -> Fut, Fut: Future>, { type Response = Res; @@ -141,14 +141,14 @@ where crate::always_ready!(); - fn call(&mut self, req: Req) -> Self::Future { + fn call(&self, req: Req) -> Self::Future { (self.f)(req) } } impl IntoService, Req> for F where - F: FnMut(Req) -> Fut, + F: Fn(Req) -> Fut, Fut: Future>, { fn into_service(self) -> FnService { @@ -158,7 +158,7 @@ where pub struct FnServiceFactory where - F: FnMut(Req) -> Fut, + F: Fn(Req) -> Fut, Fut: Future>, { f: F, @@ -167,7 +167,7 @@ where impl FnServiceFactory where - F: FnMut(Req) -> Fut + Clone, + F: Fn(Req) -> Fut + Clone, Fut: Future>, { fn new(f: F) -> Self { @@ -177,7 +177,7 @@ where impl Clone for FnServiceFactory where - F: FnMut(Req) -> Fut + Clone, + F: Fn(Req) -> Fut + Clone, Fut: Future>, { fn clone(&self) -> Self { @@ -187,7 +187,7 @@ where impl Service for FnServiceFactory where - F: FnMut(Req) -> Fut + Clone, + F: Fn(Req) -> Fut + Clone, Fut: Future>, { type Response = Res; @@ -196,7 +196,7 @@ where crate::always_ready!(); - fn call(&mut self, req: Req) -> Self::Future { + fn call(&self, req: Req) -> Self::Future { (self.f)(req) } } @@ -204,7 +204,7 @@ where impl ServiceFactory for FnServiceFactory where - F: FnMut(Req) -> Fut + Clone, + F: Fn(Req) -> Fut + Clone, Fut: Future>, { type Response = Res; @@ -318,8 +318,8 @@ where { type Response = Srv::Response; type Error = Srv::Error; - type Service = Srv; type Config = Cfg; + type Service = Srv; type InitError = Err; type Future = Fut; @@ -364,7 +364,7 @@ mod tests { async fn test_fn_service() { let new_srv = fn_service(|()| ok::<_, ()>("srv")); - let mut srv = new_srv.new_service(()).await.unwrap(); + let srv = new_srv.new_service(()).await.unwrap(); let res = srv.call(()).await; assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(()))); assert!(res.is_ok()); @@ -373,7 +373,7 @@ mod tests { #[actix_rt::test] async fn test_fn_service_service() { - let mut srv = fn_service(|()| ok::<_, ()>("srv")); + let srv = fn_service(|()| ok::<_, ()>("srv")); let res = srv.call(()).await; assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(()))); @@ -387,7 +387,7 @@ mod tests { ok::<_, ()>(fn_service(move |()| ok::<_, ()>(("srv", cfg)))) }); - let mut srv = new_srv.new_service(1).await.unwrap(); + let srv = new_srv.new_service(1).await.unwrap(); let res = srv.call(()).await; assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(()))); assert!(res.is_ok()); diff --git a/actix-service/src/lib.rs b/actix-service/src/lib.rs index b774d0d9..7c3a271c 100644 --- a/actix-service/src/lib.rs +++ b/actix-service/src/lib.rs @@ -69,9 +69,9 @@ use self::ready::{err, ok, ready, Ready}; /// type Error = MyError; /// type Future = Pin>>>; /// -/// fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { ... } +/// fn poll_ready(&self, cx: &mut Context<'_>) -> Poll> { ... } /// -/// fn call(&mut self, req: Self::Request) -> Self::Future { ... } +/// fn call(&self, req: Self::Request) -> Self::Future { ... } /// } /// ``` /// @@ -104,7 +104,7 @@ pub trait Service { /// # Notes /// 1. `.poll_ready()` might be called on different task from actual service call. /// 1. In case of chained services, `.poll_ready()` get called for all services at once. - fn poll_ready(&mut self, ctx: &mut task::Context<'_>) -> Poll>; + fn poll_ready(&self, ctx: &mut task::Context<'_>) -> Poll>; /// Process the request and return the response asynchronously. /// @@ -115,7 +115,7 @@ pub trait Service { /// /// Calling `call` without calling `poll_ready` is permitted. The /// implementation must be resilient to this fact. - fn call(&mut self, req: Req) -> Self::Future; + fn call(&self, req: Req) -> Self::Future; } /// Factory for creating `Service`s. @@ -158,11 +158,11 @@ where type Error = S::Error; type Future = S::Future; - fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll> { + fn poll_ready(&self, ctx: &mut Context<'_>) -> Poll> { (**self).poll_ready(ctx) } - fn call(&mut self, request: Req) -> S::Future { + fn call(&self, request: Req) -> S::Future { (**self).call(request) } } @@ -175,11 +175,11 @@ where type Error = S::Error; type Future = S::Future; - fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll> { + fn poll_ready(&self, ctx: &mut Context<'_>) -> Poll> { (**self).poll_ready(ctx) } - fn call(&mut self, request: Req) -> S::Future { + fn call(&self, request: Req) -> S::Future { (**self).call(request) } } @@ -192,12 +192,12 @@ where type Error = S::Error; type Future = S::Future; - fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll> { - self.borrow_mut().poll_ready(ctx) + fn poll_ready(&self, ctx: &mut Context<'_>) -> Poll> { + self.borrow().poll_ready(ctx) } - fn call(&mut self, request: Req) -> S::Future { - self.borrow_mut().call(request) + fn call(&self, request: Req) -> S::Future { + self.borrow().call(request) } } @@ -209,12 +209,12 @@ where type Error = S::Error; type Future = S::Future; - fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll> { - self.borrow_mut().poll_ready(ctx) + fn poll_ready(&self, ctx: &mut Context<'_>) -> Poll> { + self.borrow().poll_ready(ctx) } - fn call(&mut self, request: Req) -> S::Future { - (&mut (**self).borrow_mut()).call(request) + fn call(&self, request: Req) -> S::Future { + self.borrow().call(request) } } @@ -311,8 +311,9 @@ pub mod dev { #[macro_export] macro_rules! always_ready { () => { + #[inline] fn poll_ready( - &mut self, + &self, _: &mut ::core::task::Context<'_>, ) -> ::core::task::Poll> { Poll::Ready(Ok(())) @@ -323,8 +324,9 @@ macro_rules! always_ready { #[macro_export] macro_rules! forward_ready { ($field:ident) => { + #[inline] fn poll_ready( - &mut self, + &self, cx: &mut ::core::task::Context<'_>, ) -> ::core::task::Poll> { self.$field diff --git a/actix-service/src/map.rs b/actix-service/src/map.rs index 0599a1d8..12fd4395 100644 --- a/actix-service/src/map.rs +++ b/actix-service/src/map.rs @@ -58,7 +58,7 @@ where crate::forward_ready!(service); - fn call(&mut self, req: Req) -> Self::Future { + fn call(&self, req: Req) -> Self::Future { MapFuture::new(self.service.call(req), self.f.clone()) } } @@ -215,21 +215,21 @@ mod tests { crate::always_ready!(); - fn call(&mut self, _: ()) -> Self::Future { + fn call(&self, _: ()) -> Self::Future { ok(()) } } #[actix_rt::test] async fn test_poll_ready() { - let mut srv = Srv.map(|_| "ok"); + let srv = Srv.map(|_| "ok"); let res = lazy(|cx| srv.poll_ready(cx)).await; assert_eq!(res, Poll::Ready(Ok(()))); } #[actix_rt::test] async fn test_call() { - let mut srv = Srv.map(|_| "ok"); + let srv = Srv.map(|_| "ok"); let res = srv.call(()).await; assert!(res.is_ok()); assert_eq!(res.unwrap(), "ok"); @@ -238,7 +238,7 @@ mod tests { #[actix_rt::test] async fn test_new_service() { let new_srv = (|| ok::<_, ()>(Srv)).into_factory().map(|_| "ok"); - let mut srv = new_srv.new_service(&()).await.unwrap(); + let srv = new_srv.new_service(&()).await.unwrap(); let res = srv.call(()).await; assert!(res.is_ok()); assert_eq!(res.unwrap(), ("ok")); diff --git a/actix-service/src/map_err.rs b/actix-service/src/map_err.rs index 944056c2..ff25c4f7 100644 --- a/actix-service/src/map_err.rs +++ b/actix-service/src/map_err.rs @@ -57,11 +57,11 @@ where type Error = E; type Future = MapErrFuture; - fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll> { + fn poll_ready(&self, ctx: &mut Context<'_>) -> Poll> { self.service.poll_ready(ctx).map_err(&self.f) } - fn call(&mut self, req: Req) -> Self::Future { + fn call(&self, req: Req) -> Self::Future { MapErrFuture::new(self.service.call(req), self.f.clone()) } } @@ -218,25 +218,25 @@ mod tests { type Error = (); type Future = Ready>; - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { + fn poll_ready(&self, _: &mut Context<'_>) -> Poll> { Poll::Ready(Err(())) } - fn call(&mut self, _: ()) -> Self::Future { + fn call(&self, _: ()) -> Self::Future { err(()) } } #[actix_rt::test] async fn test_poll_ready() { - let mut srv = Srv.map_err(|_| "error"); + let srv = Srv.map_err(|_| "error"); let res = lazy(|cx| srv.poll_ready(cx)).await; assert_eq!(res, Poll::Ready(Err("error"))); } #[actix_rt::test] async fn test_call() { - let mut srv = Srv.map_err(|_| "error"); + let srv = Srv.map_err(|_| "error"); let res = srv.call(()).await; assert!(res.is_err()); assert_eq!(res.err().unwrap(), "error"); @@ -245,7 +245,7 @@ mod tests { #[actix_rt::test] async fn test_new_service() { let new_srv = (|| ok::<_, ()>(Srv)).into_factory().map_err(|_| "error"); - let mut srv = new_srv.new_service(&()).await.unwrap(); + let srv = new_srv.new_service(&()).await.unwrap(); let res = srv.call(()).await; assert!(res.is_err()); assert_eq!(res.err().unwrap(), "error"); diff --git a/actix-service/src/pipeline.rs b/actix-service/src/pipeline.rs index 580d7b4c..0ec43f0d 100644 --- a/actix-service/src/pipeline.rs +++ b/actix-service/src/pipeline.rs @@ -146,12 +146,12 @@ impl, Req> Service for Pipeline { type Future = S::Future; #[inline] - fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll> { + fn poll_ready(&self, ctx: &mut Context<'_>) -> Poll> { self.service.poll_ready(ctx) } #[inline] - fn call(&mut self, req: Req) -> Self::Future { + fn call(&self, req: Req) -> Self::Future { self.service.call(req) } } diff --git a/actix-service/src/then.rs b/actix-service/src/then.rs index 060ca9c7..9cd311ad 100644 --- a/actix-service/src/then.rs +++ b/actix-service/src/then.rs @@ -1,12 +1,12 @@ -use alloc::rc::Rc; use core::{ - cell::RefCell, future::Future, marker::PhantomData, pin::Pin, task::{Context, Poll}, }; +use alloc::rc::Rc; +use futures_core::ready; use pin_project_lite::pin_project; use super::{Service, ServiceFactory}; @@ -15,7 +15,7 @@ use super::{Service, ServiceFactory}; /// another service. /// /// This is created by the `Pipeline::then` method. -pub(crate) struct ThenService(Rc>, PhantomData); +pub(crate) struct ThenService(Rc<(A, B)>, PhantomData); impl ThenService { /// Create new `.then()` combinator @@ -24,7 +24,7 @@ impl ThenService { A: Service, B: Service, Error = A::Error>, { - Self(Rc::new(RefCell::new((a, b))), PhantomData) + Self(Rc::new((a, b)), PhantomData) } } @@ -43,20 +43,20 @@ where type Error = B::Error; type Future = ThenServiceResponse; - fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { - let mut srv = self.0.borrow_mut(); - let not_ready = !srv.0.poll_ready(cx)?.is_ready(); - if !srv.1.poll_ready(cx)?.is_ready() || not_ready { + fn poll_ready(&self, cx: &mut Context<'_>) -> Poll> { + let (a, b) = &*self.0; + let not_ready = !a.poll_ready(cx)?.is_ready(); + if !b.poll_ready(cx)?.is_ready() || not_ready { Poll::Pending } else { Poll::Ready(Ok(())) } } - fn call(&mut self, req: Req) -> Self::Future { + fn call(&self, req: Req) -> Self::Future { ThenServiceResponse { state: State::A { - fut: self.0.borrow_mut().0.call(req), + fut: self.0 .0.call(req), b: Some(self.0.clone()), }, } @@ -81,9 +81,8 @@ pin_project! { A: Service, B: Service>, { - A { #[pin] fut: A::Future, b: Option>> }, + A { #[pin] fut: A::Future, b: Option> }, B { #[pin] fut: B::Future }, - Empty, } } @@ -98,23 +97,14 @@ where let mut this = self.as_mut().project(); match this.state.as_mut().project() { - StateProj::A { fut, b } => match fut.poll(cx) { - Poll::Ready(res) => { - let b = b.take().unwrap(); - this.state.set(State::Empty); // drop fut A - let fut = b.borrow_mut().1.call(res); - this.state.set(State::B { fut }); - self.poll(cx) - } - Poll::Pending => Poll::Pending, - }, - StateProj::B { fut } => fut.poll(cx).map(|r| { - this.state.set(State::Empty); - r - }), - StateProj::Empty => { - panic!("future must not be polled after it returned `Poll::Ready`") + StateProj::A { fut, b } => { + let res = ready!(fut.poll(cx)); + let b = b.take().unwrap(); + let fut = b.1.call(res); + this.state.set(State::B { fut }); + self.poll(cx) } + StateProj::B { fut } => fut.poll(cx), } } } @@ -266,12 +256,12 @@ mod tests { type Error = (); type Future = Ready>; - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { + fn poll_ready(&self, _: &mut Context<'_>) -> Poll> { self.0.set(self.0.get() + 1); Poll::Ready(Ok(())) } - fn call(&mut self, req: Result<&'static str, &'static str>) -> Self::Future { + fn call(&self, req: Result<&'static str, &'static str>) -> Self::Future { match req { Ok(msg) => ok(msg), Err(_) => err(()), @@ -286,12 +276,12 @@ mod tests { type Error = (); type Future = Ready>; - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { + fn poll_ready(&self, _: &mut Context<'_>) -> Poll> { self.0.set(self.0.get() + 1); Poll::Ready(Err(())) } - fn call(&mut self, req: Result<&'static str, ()>) -> Self::Future { + fn call(&self, req: Result<&'static str, ()>) -> Self::Future { match req { Ok(msg) => ok((msg, "ok")), Err(()) => ok(("srv2", "err")), @@ -302,7 +292,7 @@ mod tests { #[actix_rt::test] async fn test_poll_ready() { let cnt = Rc::new(Cell::new(0)); - let mut srv = pipeline(Srv1(cnt.clone())).then(Srv2(cnt.clone())); + let srv = pipeline(Srv1(cnt.clone())).then(Srv2(cnt.clone())); let res = lazy(|cx| srv.poll_ready(cx)).await; assert_eq!(res, Poll::Ready(Err(()))); assert_eq!(cnt.get(), 2); @@ -311,7 +301,7 @@ mod tests { #[actix_rt::test] async fn test_call() { let cnt = Rc::new(Cell::new(0)); - let mut srv = pipeline(Srv1(cnt.clone())).then(Srv2(cnt)); + let srv = pipeline(Srv1(cnt.clone())).then(Srv2(cnt)); let res = srv.call(Ok("srv1")).await; assert!(res.is_ok()); @@ -328,7 +318,7 @@ mod tests { let cnt2 = cnt.clone(); let blank = move || ready(Ok::<_, ()>(Srv1(cnt2.clone()))); let factory = pipeline_factory(blank).then(move || ready(Ok(Srv2(cnt.clone())))); - let mut srv = factory.new_service(&()).await.unwrap(); + let srv = factory.new_service(&()).await.unwrap(); let res = srv.call(Ok("srv1")).await; assert!(res.is_ok()); assert_eq!(res.unwrap(), ("srv1", "ok")); diff --git a/actix-service/src/transform.rs b/actix-service/src/transform.rs index 7d707d98..d5cbcd88 100644 --- a/actix-service/src/transform.rs +++ b/actix-service/src/transform.rs @@ -1,4 +1,3 @@ -use alloc::{rc::Rc, sync::Arc}; use core::{ future::Future, marker::PhantomData, @@ -6,6 +5,8 @@ use core::{ task::{Context, Poll}, }; +use alloc::{rc::Rc, sync::Arc}; +use futures_core::ready; use pin_project_lite::pin_project; use crate::transform_err::TransformMapInitErr; @@ -47,7 +48,7 @@ where /// /// actix_service::forward_ready!(service); /// -/// fn call(&mut self, req: S::Request) -> Self::Future { +/// fn call(&self, req: S::Request) -> Self::Future { /// TimeoutServiceResponse { /// fut: self.service.call(req), /// sleep: Delay::new(clock::now() + self.timeout), @@ -127,8 +128,8 @@ where { type Response = T::Response; type Error = T::Error; - type InitError = T::InitError; type Transform = T::Transform; + type InitError = T::InitError; type Future = T::Future; fn new_transform(&self, service: S) -> T::Future { @@ -142,8 +143,8 @@ where { type Response = T::Response; type Error = T::Error; - type InitError = T::InitError; type Transform = T::Transform; + type InitError = T::InitError; type Future = T::Future; fn new_transform(&self, service: S) -> T::Future { @@ -229,14 +230,12 @@ where let mut this = self.as_mut().project(); match this.state.as_mut().project() { - ApplyTransformFutureStateProj::A { fut } => match fut.poll(cx)? { - Poll::Ready(srv) => { - let fut = this.store.0.new_transform(srv); - this.state.set(ApplyTransformFutureState::B { fut }); - self.poll(cx) - } - Poll::Pending => Poll::Pending, - }, + ApplyTransformFutureStateProj::A { fut } => { + let srv = ready!(fut.poll(cx))?; + let fut = this.store.0.new_transform(srv); + this.state.set(ApplyTransformFutureState::B { fut }); + self.poll(cx) + } ApplyTransformFutureStateProj::B { fut } => fut.poll(cx), } } diff --git a/actix-tls/src/accept/nativetls.rs b/actix-tls/src/accept/nativetls.rs index 9b2aeefb..73090de6 100644 --- a/actix-tls/src/accept/nativetls.rs +++ b/actix-tls/src/accept/nativetls.rs @@ -79,7 +79,7 @@ where type Error = Error; type Future = LocalBoxFuture<'static, Result, Error>>; - fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + fn poll_ready(&self, cx: &mut Context<'_>) -> Poll> { if self.conns.available(cx) { Poll::Ready(Ok(())) } else { @@ -87,7 +87,7 @@ where } } - fn call(&mut self, io: T) -> Self::Future { + fn call(&self, io: T) -> Self::Future { let guard = self.conns.get(); let this = self.clone(); Box::pin(async move { diff --git a/actix-tls/src/accept/openssl.rs b/actix-tls/src/accept/openssl.rs index 5f2d2fc2..8ca88578 100644 --- a/actix-tls/src/accept/openssl.rs +++ b/actix-tls/src/accept/openssl.rs @@ -75,7 +75,7 @@ where type Error = SslError; type Future = AcceptorServiceResponse; - fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll> { + fn poll_ready(&self, ctx: &mut Context<'_>) -> Poll> { if self.conns.available(ctx) { Poll::Ready(Ok(())) } else { @@ -83,7 +83,7 @@ where } } - fn call(&mut self, io: T) -> Self::Future { + fn call(&self, io: T) -> Self::Future { let ssl_ctx = self.acceptor.context(); let ssl = Ssl::new(ssl_ctx).expect("Provided SSL acceptor was invalid."); AcceptorServiceResponse { diff --git a/actix-tls/src/accept/rustls.rs b/actix-tls/src/accept/rustls.rs index e7efaa3f..c65d4657 100644 --- a/actix-tls/src/accept/rustls.rs +++ b/actix-tls/src/accept/rustls.rs @@ -80,7 +80,7 @@ where type Error = io::Error; type Future = AcceptorServiceFut; - fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + fn poll_ready(&self, cx: &mut Context<'_>) -> Poll> { if self.conns.available(cx) { Poll::Ready(Ok(())) } else { @@ -88,7 +88,7 @@ where } } - fn call(&mut self, req: T) -> Self::Future { + fn call(&self, req: T) -> Self::Future { AcceptorServiceFut { _guard: self.conns.get(), fut: self.acceptor.accept(req), diff --git a/actix-tls/src/connect/connector.rs b/actix-tls/src/connect/connector.rs index fe4ceec6..dc4fbc72 100755 --- a/actix-tls/src/connect/connector.rs +++ b/actix-tls/src/connect/connector.rs @@ -51,7 +51,7 @@ impl Service> for TcpConnector { actix_service::always_ready!(); - fn call(&mut self, req: Connect) -> Self::Future { + fn call(&self, req: Connect) -> Self::Future { let port = req.port(); let Connect { req, addr, .. } = req; diff --git a/actix-tls/src/connect/resolve.rs b/actix-tls/src/connect/resolve.rs index 211da387..a672b971 100755 --- a/actix-tls/src/connect/resolve.rs +++ b/actix-tls/src/connect/resolve.rs @@ -146,7 +146,7 @@ impl Service> for Resolver { actix_service::always_ready!(); - fn call(&mut self, req: Connect) -> Self::Future { + fn call(&self, req: Connect) -> Self::Future { if !req.addr.is_none() { ResolverFuture::Connected(Some(req)) } else if let Ok(ip) = req.host().parse() { diff --git a/actix-tls/src/connect/service.rs b/actix-tls/src/connect/service.rs index 6fa8a453..98765ca1 100755 --- a/actix-tls/src/connect/service.rs +++ b/actix-tls/src/connect/service.rs @@ -80,7 +80,7 @@ impl Service> for ConnectService { actix_service::always_ready!(); - fn call(&mut self, req: Connect) -> Self::Future { + fn call(&self, req: Connect) -> Self::Future { ConnectServiceResponse { fut: ConnectFuture::Resolve(self.resolver.call(req)), tcp: self.tcp, @@ -149,7 +149,7 @@ impl Service> for TcpConnectService { actix_service::always_ready!(); - fn call(&mut self, req: Connect) -> Self::Future { + fn call(&self, req: Connect) -> Self::Future { TcpConnectServiceResponse { fut: ConnectFuture::Resolve(self.resolver.call(req)), tcp: self.tcp, diff --git a/actix-tls/src/connect/ssl/openssl.rs b/actix-tls/src/connect/ssl/openssl.rs index d5d71edc..0006163d 100755 --- a/actix-tls/src/connect/ssl/openssl.rs +++ b/actix-tls/src/connect/ssl/openssl.rs @@ -84,7 +84,7 @@ where actix_service::always_ready!(); - fn call(&mut self, stream: Connection) -> Self::Future { + fn call(&self, stream: Connection) -> Self::Future { trace!("SSL Handshake start for: {:?}", stream.host()); let (io, stream) = stream.replace(()); let host = stream.host(); @@ -202,7 +202,7 @@ impl Service> for OpensslConnectService { actix_service::always_ready!(); - fn call(&mut self, req: Connect) -> Self::Future { + fn call(&self, req: Connect) -> Self::Future { OpensslConnectServiceResponse { fut1: Some(self.tcp.call(req)), fut2: None, diff --git a/actix-tls/src/connect/ssl/rustls.rs b/actix-tls/src/connect/ssl/rustls.rs index e12ab7ec..9d7623ee 100755 --- a/actix-tls/src/connect/ssl/rustls.rs +++ b/actix-tls/src/connect/ssl/rustls.rs @@ -84,7 +84,7 @@ where actix_service::always_ready!(); - fn call(&mut self, stream: Connection) -> Self::Future { + fn call(&self, stream: Connection) -> Self::Future { trace!("SSL Handshake start for: {:?}", stream.host()); let (io, stream) = stream.replace(()); let host = DNSNameRef::try_from_ascii_str(stream.host()) diff --git a/actix-tls/tests/test_connect.rs b/actix-tls/tests/test_connect.rs index 32d2ac0f..392c76c6 100755 --- a/actix-tls/tests/test_connect.rs +++ b/actix-tls/tests/test_connect.rs @@ -22,7 +22,7 @@ async fn test_string() { }) }); - let mut conn = actix_connect::default_connector(); + let conn = actix_connect::default_connector(); let addr = format!("localhost:{}", srv.port()); let con = conn.call(addr.into()).await.unwrap(); assert_eq!(con.peer_addr().unwrap(), srv.addr()); @@ -39,7 +39,7 @@ async fn test_rustls_string() { }) }); - let mut conn = actix_connect::default_connector(); + let conn = actix_connect::default_connector(); let addr = format!("localhost:{}", srv.port()); let con = conn.call(addr.into()).await.unwrap(); assert_eq!(con.peer_addr().unwrap(), srv.addr()); @@ -55,13 +55,14 @@ async fn test_static_str() { }) }); - let mut conn = actix_connect::default_connector(); + let conn = actix_connect::default_connector(); let con = conn.call(Connect::with("10", srv.addr())).await.unwrap(); assert_eq!(con.peer_addr().unwrap(), srv.addr()); let connect = Connect::new(srv.host().to_owned()); - let mut conn = actix_connect::default_connector(); + + let conn = actix_connect::default_connector(); let con = conn.call(connect).await; assert!(con.is_err()); } @@ -78,7 +79,7 @@ async fn test_new_service() { let factory = actix_connect::default_connector_factory(); - let mut conn = factory.new_service(()).await.unwrap(); + let conn = factory.new_service(()).await.unwrap(); let con = conn.call(Connect::with("10", srv.addr())).await.unwrap(); assert_eq!(con.peer_addr().unwrap(), srv.addr()); } @@ -126,7 +127,7 @@ async fn test_custom_resolver() { let factory = actix_connect::new_connector_factory(resolver); - let mut conn = factory.new_service(()).await.unwrap(); + let conn = factory.new_service(()).await.unwrap(); let con = conn.call(Connect::with("10", srv.addr())).await.unwrap(); assert_eq!(con.peer_addr().unwrap(), srv.addr()); } @@ -144,7 +145,7 @@ async fn test_openssl_uri() { }) }); - let mut conn = actix_connect::default_connector(); + let conn = actix_connect::default_connector(); let addr = http::Uri::try_from(format!("https://localhost:{}", srv.port())).unwrap(); let con = conn.call(addr.into()).await.unwrap(); assert_eq!(con.peer_addr().unwrap(), srv.addr()); @@ -163,7 +164,7 @@ async fn test_rustls_uri() { }) }); - let mut conn = actix_connect::default_connector(); + let conn = actix_connect::default_connector(); let addr = http::Uri::try_from(format!("https://localhost:{}", srv.port())).unwrap(); let con = conn.call(addr.into()).await.unwrap(); assert_eq!(con.peer_addr().unwrap(), srv.addr()); diff --git a/actix-tracing/src/lib.rs b/actix-tracing/src/lib.rs index 77a16287..b34f40d6 100644 --- a/actix-tracing/src/lib.rs +++ b/actix-tracing/src/lib.rs @@ -37,7 +37,7 @@ where actix_service::forward_ready!(inner); - fn call(&mut self, req: Req) -> Self::Future { + fn call(&self, req: Req) -> Self::Future { let span = (self.make_span)(&req); let _enter = span.as_ref().map(|s| s.enter()); @@ -229,7 +229,7 @@ mod test { let span_svc = span!(Level::TRACE, "span_svc"); let trace_service_factory = trace(service_factory, |_: &&str| Some(span_svc.clone())); - let mut service = trace_service_factory.new_service(()).await.unwrap(); + let service = trace_service_factory.new_service(()).await.unwrap(); service.call("boo").await.unwrap(); let id = span_svc.id().unwrap().into_u64(); diff --git a/actix-utils/src/timeout.rs b/actix-utils/src/timeout.rs index a308b0db..9304e5f6 100644 --- a/actix-utils/src/timeout.rs +++ b/actix-utils/src/timeout.rs @@ -151,7 +151,7 @@ where actix_service::forward_ready!(service); - fn call(&mut self, request: Req) -> Self::Future { + fn call(&self, request: Req) -> Self::Future { TimeoutServiceResponse { fut: self.service.call(request), sleep: sleep(self.timeout), @@ -213,7 +213,7 @@ mod tests { actix_service::always_ready!(); - fn call(&mut self, _: ()) -> Self::Future { + fn call(&self, _: ()) -> Self::Future { let sleep = actix_rt::time::sleep(self.0); Box::pin(async move { sleep.await; @@ -227,7 +227,7 @@ mod tests { let resolution = Duration::from_millis(100); let wait_time = Duration::from_millis(50); - let mut timeout = TimeoutService::new(resolution, SleepService(wait_time)); + let timeout = TimeoutService::new(resolution, SleepService(wait_time)); assert_eq!(timeout.call(()).await, Ok(())); } @@ -236,7 +236,7 @@ mod tests { let resolution = Duration::from_millis(100); let wait_time = Duration::from_millis(500); - let mut timeout = TimeoutService::new(resolution, SleepService(wait_time)); + let timeout = TimeoutService::new(resolution, SleepService(wait_time)); assert_eq!(timeout.call(()).await, Err(TimeoutError::Timeout)); } @@ -249,7 +249,7 @@ mod tests { Timeout::new(resolution), fn_factory(|| async { Ok::<_, ()>(SleepService(wait_time)) }), ); - let mut srv = timeout.new_service(&()).await.unwrap(); + let srv = timeout.new_service(&()).await.unwrap(); assert_eq!(srv.call(()).await, Err(TimeoutError::Timeout)); } From eaefe21b9841664c3242563b20a4fbe3ef25aba1 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Tue, 26 Jan 2021 08:05:19 +0000 Subject: [PATCH 41/73] add tests for custom resolver --- actix-service/src/and_then.rs | 2 +- actix-service/src/apply_cfg.rs | 2 +- actix-service/src/then.rs | 2 +- actix-service/src/transform.rs | 2 +- actix-tls/CHANGES.md | 9 +- actix-tls/Cargo.toml | 10 +- actix-tls/src/connect/connect.rs | 184 +++++++++++++++++------------ actix-tls/src/connect/connector.rs | 50 ++++---- actix-tls/src/connect/mod.rs | 24 ++-- actix-tls/src/connect/resolve.rs | 36 +++--- actix-tls/src/connect/uri.rs | 50 ++++---- actix-tls/tests/test_connect.rs | 62 ++-------- actix-tls/tests/test_resolvers.rs | 78 ++++++++++++ 13 files changed, 294 insertions(+), 217 deletions(-) create mode 100644 actix-tls/tests/test_resolvers.rs diff --git a/actix-service/src/and_then.rs b/actix-service/src/and_then.rs index 54a132da..e3b293ea 100644 --- a/actix-service/src/and_then.rs +++ b/actix-service/src/and_then.rs @@ -1,3 +1,4 @@ +use alloc::rc::Rc; use core::{ future::Future, marker::PhantomData, @@ -5,7 +6,6 @@ use core::{ task::{Context, Poll}, }; -use alloc::rc::Rc; use futures_core::ready; use pin_project_lite::pin_project; diff --git a/actix-service/src/apply_cfg.rs b/actix-service/src/apply_cfg.rs index 276efc0f..25fc5fc2 100644 --- a/actix-service/src/apply_cfg.rs +++ b/actix-service/src/apply_cfg.rs @@ -1,3 +1,4 @@ +use alloc::rc::Rc; use core::{ future::Future, marker::PhantomData, @@ -5,7 +6,6 @@ use core::{ task::{Context, Poll}, }; -use alloc::rc::Rc; use futures_core::ready; use pin_project_lite::pin_project; diff --git a/actix-service/src/then.rs b/actix-service/src/then.rs index 9cd311ad..c9428824 100644 --- a/actix-service/src/then.rs +++ b/actix-service/src/then.rs @@ -1,3 +1,4 @@ +use alloc::rc::Rc; use core::{ future::Future, marker::PhantomData, @@ -5,7 +6,6 @@ use core::{ task::{Context, Poll}, }; -use alloc::rc::Rc; use futures_core::ready; use pin_project_lite::pin_project; diff --git a/actix-service/src/transform.rs b/actix-service/src/transform.rs index d5cbcd88..7f477e54 100644 --- a/actix-service/src/transform.rs +++ b/actix-service/src/transform.rs @@ -1,3 +1,4 @@ +use alloc::{rc::Rc, sync::Arc}; use core::{ future::Future, marker::PhantomData, @@ -5,7 +6,6 @@ use core::{ task::{Context, Poll}, }; -use alloc::{rc::Rc, sync::Arc}; use futures_core::ready; use pin_project_lite::pin_project; diff --git a/actix-tls/CHANGES.md b/actix-tls/CHANGES.md index d73a7830..11a1a410 100644 --- a/actix-tls/CHANGES.md +++ b/actix-tls/CHANGES.md @@ -1,14 +1,15 @@ # Changes ## Unreleased - 2021-xx-xx -* Remove `trust-dns-proto` and `trust-dns-resolver` [#248] -* Use `tokio::net::lookup_host` as simple and basic default resolver [#248] +* Remove `trust-dns-proto` and `trust-dns-resolver`. [#248] +* Use `std::net::ToSocketAddrs` as simple and basic default resolver. [#248] * Add `Resolve` trait for custom dns resolver. [#248] * Add `Resolver::new_custom` function to construct custom resolvers. [#248] * Export `webpki_roots::TLS_SERVER_ROOTS` in `actix_tls::connect` mod and remove the export from `actix_tls::accept` [#248] -* Remove `ConnectTakeAddrsIter`. `Connect::take_addrs` would return - `ConnectAddrsIter<'static>` as owned iterator. [#248] +* Remove `ConnectTakeAddrsIter`. `Connect::take_addrs` now returns `ConnectAddrsIter<'static>` + as owned iterator. [#248] +* Rename `Address::{host => hostname}` to more accurately describe which URL segment is returned. [#248]: https://github.com/actix/actix-net/pull/248 diff --git a/actix-tls/Cargo.toml b/actix-tls/Cargo.toml index 0886b27e..c31cded0 100755 --- a/actix-tls/Cargo.toml +++ b/actix-tls/Cargo.toml @@ -18,10 +18,6 @@ features = ["openssl", "rustls", "native-tls", "accept", "connect", "uri"] name = "actix_tls" path = "src/lib.rs" -[[example]] -name = "basic" -required-features = ["accept", "rustls"] - [features] default = ["accept", "connect", "uri"] @@ -77,4 +73,8 @@ bytes = "1" env_logger = "0.8" futures-util = { version = "0.3.7", default-features = false, features = ["sink"] } log = "0.4" -trust-dns-resolver = "0.20.0" \ No newline at end of file +trust-dns-resolver = "0.20.0" + +[[example]] +name = "basic" +required-features = ["accept", "rustls"] diff --git a/actix-tls/src/connect/connect.rs b/actix-tls/src/connect/connect.rs index 3928870d..ab7e03dc 100755 --- a/actix-tls/src/connect/connect.rs +++ b/actix-tls/src/connect/connect.rs @@ -1,135 +1,147 @@ use std::{ collections::{vec_deque, VecDeque}, fmt, - iter::{FromIterator, FusedIterator}, + iter::{self, FromIterator as _}, + mem, net::SocketAddr, }; -/// Connect request +/// Parse a host into parts (hostname and port). pub trait Address: Unpin + 'static { - /// Host name of the request - fn host(&self) -> &str; + /// Get hostname part. + fn hostname(&self) -> &str; - /// Port of the request - fn port(&self) -> Option; + /// Get optional port part. + fn port(&self) -> Option { + None + } } impl Address for String { - fn host(&self) -> &str { + fn hostname(&self) -> &str { &self } - - fn port(&self) -> Option { - None - } } impl Address for &'static str { - fn host(&self) -> &str { + fn hostname(&self) -> &str { self } - - fn port(&self) -> Option { - None - } } -/// Connect request -#[derive(Eq, PartialEq, Debug, Hash)] +#[derive(Debug, Eq, PartialEq, Hash)] +pub(crate) enum ConnectAddrs { + None, + One(SocketAddr), + Multi(VecDeque), +} + +impl ConnectAddrs { + pub(crate) fn is_none(&self) -> bool { + matches!(self, Self::None) + } + + pub(crate) fn is_some(&self) -> bool { + !self.is_none() + } +} + +impl Default for ConnectAddrs { + fn default() -> Self { + Self::None + } +} + +impl From> for ConnectAddrs { + fn from(addr: Option) -> Self { + match addr { + Some(addr) => ConnectAddrs::One(addr), + None => ConnectAddrs::None, + } + } +} + +/// Connection info. +#[derive(Debug, PartialEq, Eq, Hash)] pub struct Connect { pub(crate) req: T, pub(crate) port: u16, pub(crate) addr: ConnectAddrs, } -#[derive(Eq, PartialEq, Debug, Hash)] -pub(crate) enum ConnectAddrs { - One(Option), - Multi(VecDeque), -} - -impl ConnectAddrs { - pub(crate) fn is_none(&self) -> bool { - matches!(*self, Self::One(None)) - } -} - -impl Default for ConnectAddrs { - fn default() -> Self { - Self::One(None) - } -} - impl Connect { /// Create `Connect` instance by splitting the string by ':' and convert the second part to u16 pub fn new(req: T) -> Connect { - let (_, port) = parse(req.host()); + let (_, port) = parse_host(req.hostname()); + Connect { req, port: port.unwrap_or(0), - addr: ConnectAddrs::One(None), + addr: ConnectAddrs::None, } } /// Create new `Connect` instance from host and address. Connector skips name resolution stage /// for such connect messages. - pub fn with(req: T, addr: SocketAddr) -> Connect { + pub fn with_addr(req: T, addr: SocketAddr) -> Connect { Connect { req, port: 0, - addr: ConnectAddrs::One(Some(addr)), + addr: ConnectAddrs::One(addr), } } /// Use port if address does not provide one. /// - /// By default it set to 0 + /// Default value is 0. pub fn set_port(mut self, port: u16) -> Self { self.port = port; self } - /// Use address. + /// Set address. pub fn set_addr(mut self, addr: Option) -> Self { - self.addr = ConnectAddrs::One(addr); + self.addr = ConnectAddrs::from(addr); self } - /// Use addresses. + /// Set list of addresses. pub fn set_addrs(mut self, addrs: I) -> Self where I: IntoIterator, { let mut addrs = VecDeque::from_iter(addrs); self.addr = if addrs.len() < 2 { - ConnectAddrs::One(addrs.pop_front()) + ConnectAddrs::from(addrs.pop_front()) } else { ConnectAddrs::Multi(addrs) }; self } - /// Host name - pub fn host(&self) -> &str { - self.req.host() + /// Get hostname. + pub fn hostname(&self) -> &str { + self.req.hostname() } - /// Port of the request + /// Get request port. pub fn port(&self) -> u16 { self.req.port().unwrap_or(self.port) } - /// Pre-resolved addresses of the request. + /// Get resolved request addresses. pub fn addrs(&self) -> ConnectAddrsIter<'_> { match self.addr { + ConnectAddrs::None => ConnectAddrsIter::None, ConnectAddrs::One(addr) => ConnectAddrsIter::One(addr), ConnectAddrs::Multi(ref addrs) => ConnectAddrsIter::Multi(addrs.iter()), } } - /// Takes pre-resolved addresses of the request. + /// Take resolved request addresses. pub fn take_addrs(&mut self) -> ConnectAddrsIter<'static> { - match std::mem::take(&mut self.addr) { + match mem::take(&mut self.addr) { + ConnectAddrs::None => ConnectAddrsIter::None, ConnectAddrs::One(addr) => ConnectAddrsIter::One(addr), ConnectAddrs::Multi(addrs) => ConnectAddrsIter::MultiOwned(addrs.into_iter()), } @@ -144,14 +156,15 @@ impl From for Connect { impl fmt::Display for Connect { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}:{}", self.host(), self.port()) + write!(f, "{}:{}", self.hostname(), self.port()) } } /// Iterator over addresses in a [`Connect`] request. #[derive(Clone)] pub enum ConnectAddrsIter<'a> { - One(Option), + None, + One(SocketAddr), Multi(vec_deque::Iter<'a, SocketAddr>), MultiOwned(vec_deque::IntoIter), } @@ -161,7 +174,8 @@ impl Iterator for ConnectAddrsIter<'_> { fn next(&mut self) -> Option { match *self { - Self::One(ref mut addr) => addr.take(), + Self::None => None, + Self::One(addr) => Some(addr), Self::Multi(ref mut iter) => iter.next().copied(), Self::MultiOwned(ref mut iter) => iter.next(), } @@ -169,8 +183,8 @@ impl Iterator for ConnectAddrsIter<'_> { fn size_hint(&self) -> (usize, Option) { match *self { - Self::One(None) => (0, Some(0)), - Self::One(Some(_)) => (1, Some(1)), + Self::None => (0, Some(0)), + Self::One(_) => (1, Some(1)), Self::Multi(ref iter) => iter.size_hint(), Self::MultiOwned(ref iter) => iter.size_hint(), } @@ -183,23 +197,9 @@ impl fmt::Debug for ConnectAddrsIter<'_> { } } -impl ExactSizeIterator for ConnectAddrsIter<'_> {} +impl iter::ExactSizeIterator for ConnectAddrsIter<'_> {} -impl FusedIterator for ConnectAddrsIter<'_> {} - -fn parse(host: &str) -> (&str, Option) { - let mut parts_iter = host.splitn(2, ':'); - if let Some(host) = parts_iter.next() { - let port_str = parts_iter.next().unwrap_or(""); - if let Ok(port) = port_str.parse::() { - (host, Some(port)) - } else { - (host, None) - } - } else { - (host, None) - } -} +impl iter::FusedIterator for ConnectAddrsIter<'_> {} pub struct Connection { io: U, @@ -224,25 +224,25 @@ impl Connection { } /// Replace inclosed object, return new Stream and old object - pub fn replace(self, io: Y) -> (U, Connection) { + pub fn replace_io(self, io: Y) -> (U, Connection) { (self.io, Connection { io, req: self.req }) } /// Returns a shared reference to the underlying stream. - pub fn get_ref(&self) -> &U { + pub fn io_ref(&self) -> &U { &self.io } /// Returns a mutable reference to the underlying stream. - pub fn get_mut(&mut self) -> &mut U { + pub fn io_mut(&mut self) -> &mut U { &mut self.io } } impl Connection { - /// Get request + /// Get hostname. pub fn host(&self) -> &str { - &self.req.host() + self.req.hostname() } } @@ -265,3 +265,31 @@ impl fmt::Debug for Connection { write!(f, "Stream {{{:?}}}", self.io) } } + +fn parse_host(host: &str) -> (&str, Option) { + let mut parts_iter = host.splitn(2, ':'); + + match parts_iter.next() { + Some(hostname) => { + let port_str = parts_iter.next().unwrap_or(""); + let port = port_str.parse::().ok(); + (hostname, port) + } + + None => (host, None), + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_host_parser() { + assert_eq!(parse_host("example.com"), ("example.com", None)); + assert_eq!(parse_host("example.com:8080"), ("example.com", Some(8080))); + assert_eq!(parse_host("example:8080"), ("example", Some(8080))); + assert_eq!(parse_host("example.com:false"), ("example.com", None)); + assert_eq!(parse_host("example.com:false:false"), ("example.com", None)); + } +} diff --git a/actix-tls/src/connect/connector.rs b/actix-tls/src/connect/connector.rs index dc4fbc72..5284eff4 100755 --- a/actix-tls/src/connect/connector.rs +++ b/actix-tls/src/connect/connector.rs @@ -9,14 +9,14 @@ use std::{ use actix_rt::net::TcpStream; use actix_service::{Service, ServiceFactory}; -use futures_core::future::LocalBoxFuture; +use futures_core::{future::LocalBoxFuture, ready}; use log::{error, trace}; use super::connect::{Address, Connect, ConnectAddrs, Connection}; use super::error::ConnectError; /// TCP connector service factory -#[derive(Copy, Clone, Debug)] +#[derive(Debug, Copy, Clone)] pub struct TcpConnectorFactory; impl TcpConnectorFactory { @@ -41,7 +41,7 @@ impl ServiceFactory> for TcpConnectorFactory { } /// TCP connector service -#[derive(Copy, Clone, Debug)] +#[derive(Debug, Copy, Clone)] pub struct TcpConnector; impl Service> for TcpConnector { @@ -59,7 +59,6 @@ impl Service> for TcpConnector { } } -#[doc(hidden)] /// TCP stream connector response future pub enum TcpConnectorResponse { Response { @@ -73,23 +72,27 @@ pub enum TcpConnectorResponse { impl TcpConnectorResponse { pub(crate) fn new(req: T, port: u16, addr: ConnectAddrs) -> TcpConnectorResponse { + if addr.is_none() { + error!("TCP connector: unresolved connection address"); + return TcpConnectorResponse::Error(Some(ConnectError::Unresolved)); + } + trace!( - "TCP connector - connecting to {:?} port:{}", - req.host(), + "TCP connector: connecting to {} on port {}", + req.hostname(), port ); match addr { - ConnectAddrs::One(None) => { - error!("TCP connector: got unresolved address"); - TcpConnectorResponse::Error(Some(ConnectError::Unresolved)) - } - ConnectAddrs::One(Some(addr)) => TcpConnectorResponse::Response { + ConnectAddrs::None => unreachable!("none variant already checked"), + + ConnectAddrs::One(addr) => TcpConnectorResponse::Response { req: Some(req), port, addrs: None, stream: Some(Box::pin(TcpStream::connect(addr))), }, + // when resolver returns multiple socket addr for request they would be popped from // front end of queue and returns with the first successful tcp connection. ConnectAddrs::Multi(addrs) => TcpConnectorResponse::Response { @@ -106,10 +109,9 @@ impl Future for TcpConnectorResponse { type Output = Result, ConnectError>; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let this = self.get_mut(); - match this { - TcpConnectorResponse::Error(e) => Poll::Ready(Err(e.take().unwrap())), - // connect + match self.get_mut() { + TcpConnectorResponse::Error(err) => Poll::Ready(Err(err.take().unwrap())), + TcpConnectorResponse::Response { req, port, @@ -117,22 +119,24 @@ impl Future for TcpConnectorResponse { stream, } => loop { if let Some(new) = stream.as_mut() { - match new.as_mut().poll(cx) { - Poll::Ready(Ok(sock)) => { + match ready!(new.as_mut().poll(cx)) { + Ok(sock) => { let req = req.take().unwrap(); trace!( - "TCP connector - successfully connected to connecting to {:?} - {:?}", - req.host(), sock.peer_addr() + "TCP connector: successfully connected to {:?} - {:?}", + req.hostname(), + sock.peer_addr() ); return Poll::Ready(Ok(Connection::new(sock, req))); } - Poll::Pending => return Poll::Pending, - Poll::Ready(Err(err)) => { + + Err(err) => { trace!( - "TCP connector - failed to connect to connecting to {:?} port: {}", - req.as_ref().unwrap().host(), + "TCP connector: failed to connect to {:?} port: {}", + req.as_ref().unwrap().hostname(), port, ); + if addrs.is_none() || addrs.as_ref().unwrap().is_empty() { return Poll::Ready(Err(ConnectError::Io(err))); } diff --git a/actix-tls/src/connect/mod.rs b/actix-tls/src/connect/mod.rs index c864886d..4010e3cb 100644 --- a/actix-tls/src/connect/mod.rs +++ b/actix-tls/src/connect/mod.rs @@ -1,21 +1,21 @@ -//! TCP connector service for Actix ecosystem. +//! TCP connector services for Actix ecosystem. //! -//! ## Package feature +//! # Stages of the TCP connector service: +//! - Resolve [`Address`] with given [`Resolver`] and collect list of socket addresses. +//! - Establish TCP connection and return [`TcpStream`]. //! +//! # Stages of TLS connector services: +//! - Establish [`TcpStream`] with connector service. +//! - Wrap the stream and perform connect handshake with remote peer. +//! - Return certain stream type that impls `AsyncRead` and `AsyncWrite`. +//! +//! # Package feature //! * `openssl` - enables TLS support via `openssl` crate //! * `rustls` - enables TLS support via `rustls` crate //! -//! ## Workflow of connector service: -//! - resolve [`Address`](self::connect::Address) with given [`Resolver`](self::resolve::Resolver) -//! and collect [`SocketAddrs`](std::net::SocketAddr). -//! - establish Tcp connection and return [`TcpStream`](tokio::net::TcpStream). -//! -//! ## Workflow of tls connector services: -//! - Establish [`TcpStream`](tokio::net::TcpStream) with connector service. -//! - Wrap around the stream and do connect handshake with remote address. -//! - Return certain stream type impl [`AsyncRead`](tokio::io::AsyncRead) and -//! [`AsyncWrite`](tokio::io::AsyncWrite) +//! [`TcpStream`]: actix_rt::net::TcpStream +#[allow(clippy::module_inception)] mod connect; mod connector; mod error; diff --git a/actix-tls/src/connect/resolve.rs b/actix-tls/src/connect/resolve.rs index a672b971..32e442bf 100755 --- a/actix-tls/src/connect/resolve.rs +++ b/actix-tls/src/connect/resolve.rs @@ -16,7 +16,7 @@ use log::trace; use super::connect::{Address, Connect}; use super::error::ConnectError; -/// DNS Resolver Service factory +/// DNS Resolver Service Factory #[derive(Clone)] pub struct ResolverFactory { resolver: Resolver, @@ -53,16 +53,16 @@ pub enum Resolver { Custom(Rc), } -/// trait for custom lookup with self defined resolver. +/// An interface for custom async DNS resolvers. /// -/// # Example: +/// # Usage /// ```rust /// use std::net::SocketAddr; /// /// use actix_tls::connect::{Resolve, Resolver}; /// use futures_util::future::LocalBoxFuture; /// -/// // use trust_dns_resolver as custom resolver. +/// // use trust-dns async tokio resolver /// use trust_dns_resolver::TokioAsyncResolver; /// /// struct MyResolver { @@ -103,7 +103,7 @@ pub enum Resolver { /// // resolver can be passed to connector factory where returned service factory /// // can be used to construct new connector services. /// let factory = actix_tls::connect::new_connector_factory::<&str>(resolver); -///``` +/// ``` pub trait Resolve { fn lookup<'a>( &'a self, @@ -120,10 +120,10 @@ impl Resolver { // look up with default resolver variant. fn look_up(req: &Connect) -> JoinHandle>> { - let host = req.host(); + let host = req.hostname(); // TODO: Connect should always return host with port if possible. let host = if req - .host() + .hostname() .splitn(2, ':') .last() .and_then(|p| p.parse::().ok()) @@ -135,6 +135,7 @@ impl Resolver { format!("{}:{}", host, req.port()) }; + // run blocking DNS lookup in thread pool spawn_blocking(move || std::net::ToSocketAddrs::to_socket_addrs(&host)) } } @@ -147,14 +148,14 @@ impl Service> for Resolver { actix_service::always_ready!(); fn call(&self, req: Connect) -> Self::Future { - if !req.addr.is_none() { + if req.addr.is_some() { ResolverFuture::Connected(Some(req)) - } else if let Ok(ip) = req.host().parse() { + } else if let Ok(ip) = req.hostname().parse() { let addr = SocketAddr::new(ip, req.port()); let req = req.set_addr(Some(addr)); ResolverFuture::Connected(Some(req)) } else { - trace!("DNS resolver: resolving host {:?}", req.host()); + trace!("DNS resolver: resolving host {:?}", req.hostname()); match self { Self::Default => { @@ -166,7 +167,7 @@ impl Service> for Resolver { let resolver = Rc::clone(&resolver); ResolverFuture::LookupCustom(Box::pin(async move { let addrs = resolver - .lookup(req.host(), req.port()) + .lookup(req.hostname(), req.port()) .await .map_err(ConnectError::Resolver)?; @@ -201,6 +202,7 @@ impl Future for ResolverFuture { Self::Connected(conn) => Poll::Ready(Ok(conn .take() .expect("ResolverFuture polled after finished"))), + Self::LookUp(fut, req) => { let res = match ready!(Pin::new(fut).poll(cx)) { Ok(Ok(res)) => Ok(res), @@ -210,20 +212,21 @@ impl Future for ResolverFuture { let req = req.take().unwrap(); - let addrs = res.map_err(|e| { + let addrs = res.map_err(|err| { trace!( "DNS resolver: failed to resolve host {:?} err: {:?}", - req.host(), - e + req.hostname(), + err ); - e + + err })?; let req = req.set_addrs(addrs); trace!( "DNS resolver: host {:?} resolved to {:?}", - req.host(), + req.hostname(), req.addrs() ); @@ -233,6 +236,7 @@ impl Future for ResolverFuture { Poll::Ready(Ok(req)) } } + Self::LookupCustom(fut) => fut.as_mut().poll(cx), } } diff --git a/actix-tls/src/connect/uri.rs b/actix-tls/src/connect/uri.rs index b208a8b3..2d54b618 100644 --- a/actix-tls/src/connect/uri.rs +++ b/actix-tls/src/connect/uri.rs @@ -3,35 +3,41 @@ use http::Uri; use super::Address; impl Address for Uri { - fn host(&self) -> &str { + fn hostname(&self) -> &str { self.host().unwrap_or("") } fn port(&self) -> Option { - if let Some(port) = self.port_u16() { - Some(port) - } else { - port(self.scheme_str()) + match self.port_u16() { + Some(port) => Some(port), + None => scheme_to_port(self.scheme_str()), } } } -// TODO: load data from file -fn port(scheme: Option<&str>) -> Option { - if let Some(scheme) = scheme { - match scheme { - "http" => Some(80), - "https" => Some(443), - "ws" => Some(80), - "wss" => Some(443), - "amqp" => Some(5672), - "amqps" => Some(5671), - "sb" => Some(5671), - "mqtt" => Some(1883), - "mqtts" => Some(8883), - _ => None, - } - } else { - None +// Get port from well-known URL schemes. +fn scheme_to_port(scheme: Option<&str>) -> Option { + match scheme { + // HTTP + Some("http") => Some(80), + Some("https") => Some(443), + + // WebSockets + Some("ws") => Some(80), + Some("wss") => Some(443), + + // Advanced Message Queuing Protocol (AMQP) + Some("amqp") => Some(5672), + Some("amqps") => Some(5671), + + // Message Queuing Telemetry Transport (MQTT) + Some("mqtt") => Some(1883), + Some("mqtts") => Some(8883), + + // File Transfer Protocol (FTP) + Some("ftp") => Some(1883), + Some("ftps") => Some(990), + + _ => None, } } diff --git a/actix-tls/tests/test_connect.rs b/actix-tls/tests/test_connect.rs index 392c76c6..7ee7afda 100755 --- a/actix-tls/tests/test_connect.rs +++ b/actix-tls/tests/test_connect.rs @@ -1,15 +1,13 @@ use std::io; -use std::net::SocketAddr; use actix_codec::{BytesCodec, Framed}; use actix_rt::net::TcpStream; use actix_server::TestServer; use actix_service::{fn_service, Service, ServiceFactory}; use bytes::Bytes; -use futures_core::future::LocalBoxFuture; use futures_util::sink::SinkExt; -use actix_tls::connect::{self as actix_connect, Connect, Resolve, Resolver}; +use actix_tls::connect::{self as actix_connect, Connect}; #[cfg(all(feature = "connect", feature = "openssl"))] #[actix_rt::test] @@ -57,7 +55,10 @@ async fn test_static_str() { let conn = actix_connect::default_connector(); - let con = conn.call(Connect::with("10", srv.addr())).await.unwrap(); + let con = conn + .call(Connect::with_addr("10", srv.addr())) + .await + .unwrap(); assert_eq!(con.peer_addr().unwrap(), srv.addr()); let connect = Connect::new(srv.host().to_owned()); @@ -80,55 +81,10 @@ async fn test_new_service() { let factory = actix_connect::default_connector_factory(); let conn = factory.new_service(()).await.unwrap(); - let con = conn.call(Connect::with("10", srv.addr())).await.unwrap(); - assert_eq!(con.peer_addr().unwrap(), srv.addr()); -} - -#[actix_rt::test] -async fn test_custom_resolver() { - use trust_dns_resolver::TokioAsyncResolver; - - let srv = TestServer::with(|| { - fn_service(|io: TcpStream| async { - let mut framed = Framed::new(io, BytesCodec); - framed.send(Bytes::from_static(b"test")).await?; - Ok::<_, io::Error>(()) - }) - }); - - struct MyResolver { - trust_dns: TokioAsyncResolver, - } - - impl Resolve for MyResolver { - fn lookup<'a>( - &'a self, - host: &'a str, - port: u16, - ) -> LocalBoxFuture<'a, Result, Box>> { - Box::pin(async move { - let res = self - .trust_dns - .lookup_ip(host) - .await? - .iter() - .map(|ip| SocketAddr::new(ip, port)) - .collect(); - Ok(res) - }) - } - } - - let resolver = MyResolver { - trust_dns: TokioAsyncResolver::tokio_from_system_conf().unwrap(), - }; - - let resolver = Resolver::new_custom(resolver); - - let factory = actix_connect::new_connector_factory(resolver); - - let conn = factory.new_service(()).await.unwrap(); - let con = conn.call(Connect::with("10", srv.addr())).await.unwrap(); + let con = conn + .call(Connect::with_addr("10", srv.addr())) + .await + .unwrap(); assert_eq!(con.peer_addr().unwrap(), srv.addr()); } diff --git a/actix-tls/tests/test_resolvers.rs b/actix-tls/tests/test_resolvers.rs new file mode 100644 index 00000000..0f49c486 --- /dev/null +++ b/actix-tls/tests/test_resolvers.rs @@ -0,0 +1,78 @@ +use std::{ + io, + net::{Ipv4Addr, SocketAddr}, +}; + +use actix_rt::net::TcpStream; +use actix_server::TestServer; +use actix_service::{fn_service, Service, ServiceFactory}; +use futures_core::future::LocalBoxFuture; + +use actix_tls::connect::{new_connector_factory, Connect, Resolve, Resolver}; + +#[actix_rt::test] +async fn custom_resolver() { + /// Always resolves to localhost with the given port. + struct LocalOnlyResolver; + + impl Resolve for LocalOnlyResolver { + fn lookup<'a>( + &'a self, + _host: &'a str, + port: u16, + ) -> LocalBoxFuture<'a, Result, Box>> { + Box::pin(async move { + let local = format!("127.0.0.1:{}", port).parse().unwrap(); + Ok(vec![local]) + }) + } + } + + let addr = LocalOnlyResolver.lookup("example.com", 8080).await.unwrap()[0]; + assert_eq!(addr, SocketAddr::new(Ipv4Addr::LOCALHOST.into(), 8080)) +} + +#[actix_rt::test] +async fn custom_resolver_connect() { + use trust_dns_resolver::TokioAsyncResolver; + + let srv = + TestServer::with(|| fn_service(|_io: TcpStream| async { Ok::<_, io::Error>(()) })); + + struct MyResolver { + trust_dns: TokioAsyncResolver, + } + + impl Resolve for MyResolver { + fn lookup<'a>( + &'a self, + host: &'a str, + port: u16, + ) -> LocalBoxFuture<'a, Result, Box>> { + Box::pin(async move { + let res = self + .trust_dns + .lookup_ip(host) + .await? + .iter() + .map(|ip| SocketAddr::new(ip, port)) + .collect(); + Ok(res) + }) + } + } + + let resolver = MyResolver { + trust_dns: TokioAsyncResolver::tokio_from_system_conf().unwrap(), + }; + + let resolver = Resolver::new_custom(resolver); + let factory = new_connector_factory(resolver); + + let conn = factory.new_service(()).await.unwrap(); + let con = conn + .call(Connect::with_addr("example.com", srv.addr())) + .await + .unwrap(); + assert_eq!(con.peer_addr().unwrap(), srv.addr()); +} From cff9deb729ac44ed1601342f83b3d0d7715b3820 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Tue, 26 Jan 2021 09:45:43 +0000 Subject: [PATCH 42/73] attribute nits --- actix-router/src/path.rs | 16 ++++++++-------- actix-router/src/resource.rs | 2 +- actix-server/src/builder.rs | 4 ++-- actix-utils/src/task.rs | 8 ++++---- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/actix-router/src/path.rs b/actix-router/src/path.rs index 0998c614..76fd97da 100644 --- a/actix-router/src/path.rs +++ b/actix-router/src/path.rs @@ -50,20 +50,20 @@ impl Path { } } - #[inline] /// Get reference to inner path instance + #[inline] pub fn get_ref(&self) -> &T { &self.path } - #[inline] /// Get mutable reference to inner path instance + #[inline] pub fn get_mut(&mut self) -> &mut T { &mut self.path } - #[inline] /// Path + #[inline] pub fn path(&self) -> &str { let skip = self.skip as usize; let path = self.path.path(); @@ -74,23 +74,23 @@ impl Path { } } - #[inline] /// Set new path + #[inline] pub fn set(&mut self, path: T) { self.skip = 0; self.path = path; self.segments.clear(); } - #[inline] /// Reset state + #[inline] pub fn reset(&mut self) { self.skip = 0; self.segments.clear(); } - #[inline] /// Skip first `n` chars in path + #[inline] pub fn skip(&mut self, n: u16) { self.skip += n; } @@ -109,14 +109,14 @@ impl Path { self.segments.push((name, PathItem::Static(value))); } - #[inline] /// Check if there are any matched patterns + #[inline] pub fn is_empty(&self) -> bool { self.segments.is_empty() } - #[inline] /// Check number of extracted parameters + #[inline] pub fn len(&self) -> usize { self.segments.len() } diff --git a/actix-router/src/resource.rs b/actix-router/src/resource.rs index 2ab4ceb5..3808bc25 100644 --- a/actix-router/src/resource.rs +++ b/actix-router/src/resource.rs @@ -154,8 +154,8 @@ impl ResourceDef { &self.pattern } - #[inline] /// Check if path matches this pattern. + #[inline] pub fn is_match(&self, path: &str) -> bool { match self.tp { PatternType::Static(ref s) => s == path, diff --git a/actix-server/src/builder.rs b/actix-server/src/builder.rs index 13c164f1..08ab2d09 100644 --- a/actix-server/src/builder.rs +++ b/actix-server/src/builder.rs @@ -177,8 +177,8 @@ impl ServerBuilder { Ok(self) } - #[cfg(unix)] /// Add new unix domain service to the server. + #[cfg(unix)] pub fn bind_uds(self, name: N, addr: U, factory: F) -> io::Result where F: ServiceFactory, @@ -198,10 +198,10 @@ impl ServerBuilder { self.listen_uds(name, lst, factory) } - #[cfg(unix)] /// Add new unix domain service to the server. /// Useful when running as a systemd service and /// a socket FD can be acquired using the systemd crate. + #[cfg(unix)] pub fn listen_uds>( mut self, name: N, diff --git a/actix-utils/src/task.rs b/actix-utils/src/task.rs index 8f85f5e4..2a3469cf 100644 --- a/actix-utils/src/task.rs +++ b/actix-utils/src/task.rs @@ -40,16 +40,16 @@ impl LocalWaker { since = "2.1.0", note = "In favor of `wake`. State of the register doesn't matter at `wake` up" )] - #[inline] /// Check if waker has been registered. + #[inline] pub fn is_registered(&self) -> bool { unsafe { (*self.waker.get()).is_some() } } - #[inline] /// Registers the waker to be notified on calls to `wake`. /// /// Returns `true` if waker was registered before. + #[inline] pub fn register(&self, waker: &Waker) -> bool { unsafe { let w = self.waker.get(); @@ -58,20 +58,20 @@ impl LocalWaker { } } - #[inline] /// Calls `wake` on the last `Waker` passed to `register`. /// /// If `register` has not been called yet, then this does nothing. + #[inline] pub fn wake(&self) { if let Some(waker) = self.take() { waker.wake(); } } - #[inline] /// Returns the last `Waker` passed to `register`, so that the user can wake it. /// /// If a waker has not been registered, this returns `None`. + #[inline] pub fn take(&self) -> Option { unsafe { (*self.waker.get()).take() } } From 45edff625e194ff21223f67e41cd2fb5b5e007c8 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Tue, 26 Jan 2021 09:46:14 +0000 Subject: [PATCH 43/73] add rt tests and doc tests --- actix-rt/Cargo.toml | 4 ++ actix-rt/src/arbiter.rs | 48 +++++++------ actix-rt/src/builder.rs | 78 ++++++++++----------- actix-rt/src/lib.rs | 12 ++-- actix-rt/src/runtime.rs | 80 +++++++++------------ actix-rt/src/system.rs | 81 +++++++++------------- actix-rt/tests/integration_tests.rs | 103 +++++++++++++++++++++++++++- 7 files changed, 247 insertions(+), 159 deletions(-) diff --git a/actix-rt/Cargo.toml b/actix-rt/Cargo.toml index f2fe8435..5bc79416 100644 --- a/actix-rt/Cargo.toml +++ b/actix-rt/Cargo.toml @@ -23,3 +23,7 @@ macros = ["actix-macros"] actix-macros = { version = "0.2.0-beta.1", optional = true } tokio = { version = "1", features = ["rt", "net", "parking_lot", "signal", "sync", "time"] } + +[dev-dependencies] +tokio = { version = "1", features = ["full"] } +futures-util = { version = "0.3.7", default-features = true, features = ["alloc"] } diff --git a/actix-rt/src/arbiter.rs b/actix-rt/src/arbiter.rs index 95b40b25..48d11b36 100644 --- a/actix-rt/src/arbiter.rs +++ b/actix-rt/src/arbiter.rs @@ -1,26 +1,32 @@ -use std::any::{Any, TypeId}; -use std::cell::RefCell; -use std::collections::HashMap; -use std::future::Future; -use std::pin::Pin; -use std::sync::atomic::{AtomicUsize, Ordering}; -use std::task::{Context, Poll}; -use std::{fmt, thread}; +use std::{ + any::{Any, TypeId}, + cell::RefCell, + collections::HashMap, + fmt, + future::Future, + pin::Pin, + sync::atomic::{AtomicUsize, Ordering}, + task::{Context, Poll}, + thread, +}; -use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}; -use tokio::sync::oneshot::{channel, error::RecvError as Canceled, Sender}; -use tokio::task::LocalSet; +use tokio::{ + sync::{ + mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}, + oneshot::{channel, error::RecvError as Canceled, Sender}, + }, + task::LocalSet, +}; -use crate::runtime::Runtime; -use crate::system::System; +use crate::{runtime::Runtime, system::System}; + +pub(crate) static COUNT: AtomicUsize = AtomicUsize::new(0); thread_local!( static ADDR: RefCell> = RefCell::new(None); static STORAGE: RefCell>> = RefCell::new(HashMap::new()); ); -pub(crate) static COUNT: AtomicUsize = AtomicUsize::new(0); - pub(crate) enum ArbiterCommand { Stop, Execute(Box + Unpin + Send>), @@ -37,10 +43,10 @@ impl fmt::Debug for ArbiterCommand { } } +/// Arbiters provide an asynchronous execution environment for actors, functions and futures. When +/// an Arbiter is created, it spawns a new OS thread, and hosts an event loop. Some Arbiter +/// functions execute on the current thread. #[derive(Debug)] -/// Arbiters provide an asynchronous execution environment for actors, functions -/// and futures. When an Arbiter is created, it spawns a new OS thread, and -/// hosts an event loop. Some Arbiter functions execute on the current thread. pub struct Arbiter { sender: UnboundedSender, thread_handle: Option>, @@ -125,7 +131,7 @@ impl Arbiter { // unregister arbiter let _ = System::current() .sys() - .send(SystemCommand::UnregisterArbiter(id)); + .send(SystemCommand::DeregisterArbiter(id)); } }) .unwrap_or_else(|err| { @@ -312,7 +318,7 @@ impl Future for ArbiterController { pub(crate) enum SystemCommand { Exit(i32), RegisterArbiter(usize, Arbiter), - UnregisterArbiter(usize), + DeregisterArbiter(usize), } #[derive(Debug)] @@ -353,7 +359,7 @@ impl Future for SystemArbiter { SystemCommand::RegisterArbiter(name, hnd) => { self.arbiters.insert(name, hnd); } - SystemCommand::UnregisterArbiter(name) => { + SystemCommand::DeregisterArbiter(name) => { self.arbiters.remove(&name); } }, diff --git a/actix-rt/src/builder.rs b/actix-rt/src/builder.rs index ff7b0e06..c43af7c5 100644 --- a/actix-rt/src/builder.rs +++ b/actix-rt/src/builder.rs @@ -1,22 +1,25 @@ -use std::borrow::Cow; -use std::future::Future; -use std::io; +use std::{borrow::Cow, future::Future, io}; -use tokio::sync::mpsc::unbounded_channel; -use tokio::sync::oneshot::{channel, Receiver}; -use tokio::task::LocalSet; +use tokio::{ + sync::{ + mpsc::unbounded_channel, + oneshot::{channel, Receiver}, + }, + task::LocalSet, +}; -use crate::arbiter::{Arbiter, SystemArbiter}; -use crate::runtime::Runtime; -use crate::system::System; +use crate::{ + arbiter::{Arbiter, SystemArbiter}, + runtime::Runtime, + system::System, +}; -/// Builder struct for a actix runtime. +/// Builder an actix runtime. /// -/// Either use `Builder::build` to create a system and start actors. -/// Alternatively, use `Builder::run` to start the tokio runtime and -/// run a function in its context. +/// Either use `Builder::build` to create a system and start actors. Alternatively, use +/// `Builder::run` to start the Tokio runtime and run a function in its context. pub struct Builder { - /// Name of the System. Defaults to "actix" if unset. + /// Name of the System. Defaults to "actix-rt" if unset. name: Cow<'static, str>, /// Whether the Arbiter will stop the whole System on uncaught panic. Defaults to false. @@ -26,13 +29,13 @@ pub struct Builder { impl Builder { pub(crate) fn new() -> Self { Builder { - name: Cow::Borrowed("actix"), + name: Cow::Borrowed("actix-rt"), stop_on_panic: false, } } /// Sets the name of the System. - pub fn name>(mut self, name: T) -> Self { + pub fn name(mut self, name: impl Into) -> Self { self.name = Cow::Owned(name.into()); self } @@ -48,7 +51,7 @@ impl Builder { /// Create new System. /// - /// This method panics if it can not create tokio runtime + /// This method panics if it can not create Tokio runtime pub fn build(self) -> SystemRunner { self.create_runtime(|| {}) } @@ -60,9 +63,8 @@ impl Builder { self.create_async_runtime(local) } - /// This function will start tokio runtime and will finish once the - /// `System::stop()` message get called. - /// Function `f` get called within tokio runtime context. + /// This function will start Tokio runtime and will finish once the `System::stop()` message + /// is called. Function `f` is called within Tokio runtime context. pub fn run(self, f: F) -> io::Result<()> where F: FnOnce(), @@ -71,7 +73,7 @@ impl Builder { } fn create_async_runtime(self, local: &LocalSet) -> AsyncSystemRunner { - let (stop_tx, stop) = channel(); + let (stop_tx, stop_rx) = channel(); let (sys_sender, sys_receiver) = unbounded_channel(); let system = @@ -83,7 +85,7 @@ impl Builder { // start the system arbiter let _ = local.spawn_local(arb); - AsyncSystemRunner { stop, system } + AsyncSystemRunner { system, stop_rx } } fn create_runtime(self, f: F) -> SystemRunner @@ -115,31 +117,29 @@ impl Builder { #[derive(Debug)] pub(crate) struct AsyncSystemRunner { - stop: Receiver, system: System, + stop_rx: Receiver, } impl AsyncSystemRunner { - /// This function will start event loop and returns a future that - /// resolves once the `System::stop()` function is called. - pub(crate) fn run_nonblocking(self) -> impl Future> + Send { - let AsyncSystemRunner { stop, .. } = self; + /// This function will start event loop and returns a future that resolves once the + /// `System::stop()` function is called. + pub(crate) async fn run(self) -> Result<(), io::Error> { + let AsyncSystemRunner { stop_rx: stop, .. } = self; // run loop - async { - match stop.await { - Ok(code) => { - if code != 0 { - Err(io::Error::new( - io::ErrorKind::Other, - format!("Non-zero exit code: {}", code), - )) - } else { - Ok(()) - } + match stop.await { + Ok(code) => { + if code != 0 { + Err(io::Error::new( + io::ErrorKind::Other, + format!("Non-zero exit code: {}", code), + )) + } else { + Ok(()) } - Err(e) => Err(io::Error::new(io::ErrorKind::Other, e)), } + Err(e) => Err(io::Error::new(io::ErrorKind::Other, e)), } } } diff --git a/actix-rt/src/lib.rs b/actix-rt/src/lib.rs index 6e9d0464..2151952e 100644 --- a/actix-rt/src/lib.rs +++ b/actix-rt/src/lib.rs @@ -2,6 +2,7 @@ #![deny(rust_2018_idioms, nonstandard_style)] #![allow(clippy::type_complexity)] +#![warn(missing_docs)] #![doc(html_logo_url = "https://actix.rs/img/logo.png")] #![doc(html_favicon_url = "https://actix.rs/favicon.ico")] @@ -25,7 +26,6 @@ pub use self::system::System; /// Spawns a future on the current arbiter. /// /// # Panics -/// /// This function panics if actix system is not running. #[inline] pub fn spawn(f: F) @@ -39,13 +39,15 @@ where pub mod signal { #[cfg(unix)] pub mod unix { + //! Unix specific signals. pub use tokio::signal::unix::*; } pub use tokio::signal::ctrl_c; } -/// TCP/UDP/Unix bindings pub mod net { + //! TCP/UDP/Unix bindings + pub use tokio::net::UdpSocket; pub use tokio::net::{TcpListener, TcpStream}; @@ -58,15 +60,17 @@ pub mod net { pub use self::unix::*; } -/// Utilities for tracking time. pub mod time { + //! Utilities for tracking time. + pub use tokio::time::Instant; pub use tokio::time::{interval, interval_at, Interval}; pub use tokio::time::{sleep, sleep_until, Sleep}; pub use tokio::time::{timeout, Timeout}; } -/// Task management. pub mod task { + //! Task management. + pub use tokio::task::{spawn_blocking, yield_now, JoinHandle}; } diff --git a/actix-rt/src/runtime.rs b/actix-rt/src/runtime.rs index 63653e13..c7f611ed 100644 --- a/actix-rt/src/runtime.rs +++ b/actix-rt/src/runtime.rs @@ -1,24 +1,21 @@ -use std::future::Future; -use std::io; -use tokio::{runtime, task::LocalSet}; +use std::{future::Future, io}; -/// Single-threaded runtime provides a way to start reactor -/// and runtime on the current thread. +use tokio::task::{JoinHandle, LocalSet}; + +/// Single-threaded runtime provides a way to start reactor and runtime on the current thread. /// -/// See [module level][mod] documentation for more details. -/// -/// [mod]: crate +/// See [crate root][crate] documentation for more details. #[derive(Debug)] pub struct Runtime { local: LocalSet, - rt: runtime::Runtime, + rt: tokio::runtime::Runtime, } impl Runtime { - #[allow(clippy::new_ret_no_self)] /// Returns a new runtime initialized with default configuration values. + #[allow(clippy::new_ret_no_self)] pub fn new() -> io::Result { - let rt = runtime::Builder::new_current_thread() + let rt = tokio::runtime::Builder::new_current_thread() .enable_io() .enable_time() .build()?; @@ -29,62 +26,53 @@ impl Runtime { }) } - pub(super) fn local(&self) -> &LocalSet { + /// Reference to local task set. + pub(crate) fn local(&self) -> &LocalSet { &self.local } - /// Spawn a future onto the single-threaded runtime. + /// Offload a future onto the single-threaded runtime. /// - /// See [module level][mod] documentation for more details. + /// The returned join handle can be used to await the future's result. /// - /// [mod]: crate + /// See [crate root][crate] documentation for more details. /// /// # Examples - /// - /// ```ignore - /// # use futures::{future, Future, Stream}; - /// use actix_rt::Runtime; - /// - /// # fn dox() { - /// // Create the runtime - /// let rt = Runtime::new().unwrap(); + /// ``` + /// let rt = actix_rt::Runtime::new().unwrap(); /// /// // Spawn a future onto the runtime - /// rt.spawn(future::lazy(|_| { + /// let handle = rt.spawn(async { /// println!("running on the runtime"); - /// })); - /// # } - /// # pub fn main() {} + /// 42 + /// }); + /// + /// assert_eq!(rt.block_on(handle).unwrap(), 42); /// ``` /// /// # Panics - /// - /// This function panics if the spawn fails. Failure occurs if the executor - /// is currently at capacity and is unable to spawn a new future. - pub fn spawn(&self, future: F) -> &Self + /// This function panics if the spawn fails. Failure occurs if the executor is currently at + /// capacity and is unable to spawn a new future. + pub fn spawn(&self, future: F) -> JoinHandle where - F: Future + 'static, + F: Future + 'static, { - self.local.spawn_local(future); - self + self.local.spawn_local(future) } - /// Runs the provided future, blocking the current thread until the future - /// completes. + /// Runs the provided future, blocking the current thread until the future completes. /// - /// This function can be used to synchronously block the current thread - /// until the provided `future` has resolved either successfully or with an - /// error. The result of the future is then returned from this function - /// call. + /// This function can be used to synchronously block the current thread until the provided + /// `future` has resolved either successfully or with an error. The result of the future is + /// then returned from this function call. /// - /// Note that this function will **also** execute any spawned futures on the - /// current thread, but will **not** block until these other spawned futures - /// have completed. Once the function returns, any uncompleted futures - /// remain pending in the `Runtime` instance. These futures will not run + /// Note that this function will also execute any spawned futures on the current thread, but + /// will not block until these other spawned futures have completed. Once the function returns, + /// any uncompleted futures remain pending in the `Runtime` instance. These futures will not run /// until `block_on` or `run` is called again. /// - /// The caller is responsible for ensuring that other spawned futures - /// complete execution by calling `block_on` or `run`. + /// The caller is responsible for ensuring that other spawned futures complete execution by + /// calling `block_on` or `run`. pub fn block_on(&self, f: F) -> F::Output where F: Future, diff --git a/actix-rt/src/system.rs b/actix-rt/src/system.rs index 3a199da7..262f60a6 100644 --- a/actix-rt/src/system.rs +++ b/actix-rt/src/system.rs @@ -1,13 +1,16 @@ -use std::cell::RefCell; -use std::future::Future; -use std::io; -use std::sync::atomic::{AtomicUsize, Ordering}; +use std::{ + cell::RefCell, + future::Future, + io, + sync::atomic::{AtomicUsize, Ordering}, +}; -use tokio::sync::mpsc::UnboundedSender; -use tokio::task::LocalSet; +use tokio::{sync::mpsc::UnboundedSender, task::LocalSet}; -use crate::arbiter::{Arbiter, SystemCommand}; -use crate::builder::{Builder, SystemRunner}; +use crate::{ + arbiter::{Arbiter, SystemCommand}, + builder::{Builder, SystemRunner}, +}; static SYSTEM_COUNT: AtomicUsize = AtomicUsize::new(0); @@ -43,16 +46,15 @@ impl System { /// Build a new system with a customized tokio runtime. /// - /// This allows to customize the runtime. See struct level docs on - /// `Builder` for more information. + /// This allows to customize the runtime. See [`Builder`] for more information. pub fn builder() -> Builder { Builder::new() } - #[allow(clippy::new_ret_no_self)] /// Create new system. /// /// This method panics if it can not create tokio runtime + #[allow(clippy::new_ret_no_self)] pub fn new>(name: T) -> SystemRunner { Self::builder().name(name).build() } @@ -64,13 +66,10 @@ impl System { /// Note: This method uses provided `LocalSet` to create a `System` future only. /// All the [`Arbiter`]s will be started in separate threads using their own tokio `Runtime`s. /// It means that using this method currently it is impossible to make `actix-rt` work in the - /// alternative `tokio` `Runtime`s (e.g. provided by [`tokio_compat`]). - /// - /// [`tokio_compat`]: https://crates.io/crates/tokio-compat + /// alternative Tokio runtimes such as those provided by `tokio_compat`. /// /// # Examples - /// - /// ```ignore + /// ``` /// use tokio::{runtime::Runtime, task::LocalSet}; /// use actix_rt::System; /// use futures_util::future::try_join_all; @@ -78,14 +77,14 @@ impl System { /// async fn run_application() { /// let first_task = tokio::spawn(async { /// // ... - /// # println!("One task"); - /// # Ok::<(),()>(()) + /// # println!("One task"); + /// # Ok::<(),()>(()) /// }); /// /// let second_task = tokio::spawn(async { /// // ... - /// # println!("Another task"); - /// # Ok::<(),()>(()) + /// # println!("Another task"); + /// # Ok::<(),()>(()) /// }); /// /// try_join_all(vec![first_task, second_task]) @@ -93,14 +92,12 @@ impl System { /// .expect("Some of the futures finished unexpectedly"); /// } /// - /// /// let runtime = tokio::runtime::Builder::new_multi_thread() /// .worker_threads(2) /// .enable_all() /// .build() /// .unwrap(); /// - /// /// let actix_system_task = LocalSet::new(); /// let sys = System::run_in_tokio("actix-main-system", &actix_system_task); /// actix_system_task.spawn_local(sys); @@ -112,34 +109,28 @@ impl System { name: T, local: &LocalSet, ) -> impl Future> { - Self::builder() - .name(name) - .build_async(local) - .run_nonblocking() + Self::builder().name(name).build_async(local).run() } - /// Consume the provided tokio Runtime and start the `System` in it. + /// Consume the provided Tokio Runtime and start the `System` in it. /// This method will create a `LocalSet` object and occupy the current thread /// for the created `System` exclusively. All the other asynchronous tasks that /// should be executed as well must be aggregated into one future, provided as the last /// argument to this method. /// /// Note: This method uses provided `Runtime` to create a `System` future only. - /// All the [`Arbiter`]s will be started in separate threads using their own tokio `Runtime`s. + /// All the [`Arbiter`]s will be started in separate threads using their own Tokio `Runtime`s. /// It means that using this method currently it is impossible to make `actix-rt` work in the - /// alternative `tokio` `Runtime`s (e.g. provided by `tokio_compat`). - /// - /// [`tokio_compat`]: https://crates.io/crates/tokio-compat + /// alternative Tokio runtimes such as those provided by `tokio_compat`. /// /// # Arguments /// /// - `name`: Name of the System - /// - `runtime`: A tokio Runtime to run the system in. + /// - `runtime`: A Tokio Runtime to run the system in. /// - `rest_operations`: A future to be executed in the runtime along with the System. /// /// # Examples - /// - /// ```ignore + /// ``` /// use tokio::runtime::Runtime; /// use actix_rt::System; /// use futures_util::future::try_join_all; @@ -172,14 +163,11 @@ impl System { /// let rest_operations = run_application(); /// System::attach_to_tokio("actix-main-system", runtime, rest_operations); /// ``` - pub fn attach_to_tokio( + pub fn attach_to_tokio( name: impl Into, runtime: tokio::runtime::Runtime, rest_operations: Fut, - ) -> R - where - Fut: std::future::Future, - { + ) -> Fut::Output { let actix_system_task = LocalSet::new(); let sys = System::run_in_tokio(name.into(), &actix_system_task); actix_system_task.spawn_local(sys); @@ -195,7 +183,7 @@ impl System { }) } - /// Check if current system is set, i.e., as already been started. + /// Check if current system has started. pub fn is_set() -> bool { CURRENT.with(|cell| cell.borrow().is_some()) } @@ -219,12 +207,12 @@ impl System { }) } - /// System id + /// Numeric system ID. pub fn id(&self) -> usize { self.id } - /// Stop the system + /// Stop the system (with code 0). pub fn stop(&self) { self.stop_with_code(0) } @@ -240,18 +228,17 @@ impl System { /// Return status of 'stop_on_panic' option which controls whether the System is stopped when an /// uncaught panic is thrown from a worker thread. - pub fn stop_on_panic(&self) -> bool { + pub(crate) fn stop_on_panic(&self) -> bool { self.stop_on_panic } - /// System arbiter + /// Get shared reference to system arbiter. pub fn arbiter(&self) -> &Arbiter { &self.arbiter } - /// This function will start tokio runtime and will finish once the - /// `System::stop()` message get called. - /// Function `f` get called within tokio runtime context. + /// This function will start tokio runtime and will finish once the `System::stop()` message + /// is called. Function `f` is called within tokio runtime context. pub fn run(f: F) -> io::Result<()> where F: FnOnce(), diff --git a/actix-rt/tests/integration_tests.rs b/actix-rt/tests/integration_tests.rs index f338602d..fd579827 100644 --- a/actix-rt/tests/integration_tests.rs +++ b/actix-rt/tests/integration_tests.rs @@ -1,8 +1,10 @@ use std::time::{Duration, Instant}; +use futures_util::future::try_join_all; + #[test] fn await_for_timer() { - let time = Duration::from_secs(2); + let time = Duration::from_secs(1); let instant = Instant::now(); actix_rt::System::new("test_wait_timer").block_on(async move { tokio::time::sleep(time).await; @@ -15,7 +17,7 @@ fn await_for_timer() { #[test] fn join_another_arbiter() { - let time = Duration::from_secs(2); + let time = Duration::from_secs(1); let instant = Instant::now(); actix_rt::System::new("test_join_another_arbiter").block_on(async move { let mut arbiter = actix_rt::Arbiter::new(); @@ -87,3 +89,100 @@ fn non_static_block_on() { }) .unwrap(); } + +#[test] +fn wait_for_spawns() { + let rt = actix_rt::Runtime::new().unwrap(); + + let handle = rt.spawn(async { + println!("running on the runtime"); + // assertion panic is caught at task boundary + assert_eq!(1, 2); + }); + + assert!(rt.block_on(handle).is_err()); +} + +#[test] +fn run_in_existing_tokio() { + use actix_rt::System; + use futures_util::future::try_join_all; + use tokio::task::LocalSet; + + async fn run_application() { + let first_task = tokio::spawn(async { + println!("One task"); + Ok::<(), ()>(()) + }); + + let second_task = tokio::spawn(async { + println!("Another task"); + Ok::<(), ()>(()) + }); + + try_join_all(vec![first_task, second_task]) + .await + .expect("Some of the futures finished unexpectedly"); + } + + let runtime = tokio::runtime::Builder::new_multi_thread() + .worker_threads(2) + .enable_all() + .build() + .unwrap(); + + let actix_local_set = LocalSet::new(); + let sys = System::run_in_tokio("actix-main-system", &actix_local_set); + actix_local_set.spawn_local(sys); + + let rest_operations = run_application(); + runtime.block_on(actix_local_set.run_until(rest_operations)); +} + +async fn run_application() -> usize { + let first_task = tokio::spawn(async { + println!("One task"); + Ok::<(), ()>(()) + }); + + let second_task = tokio::spawn(async { + println!("Another task"); + Ok::<(), ()>(()) + }); + + let tasks = try_join_all(vec![first_task, second_task]) + .await + .expect("Some of the futures finished unexpectedly"); + + tasks.len() +} + +#[test] +fn attack_to_tokio() { + use actix_rt::System; + + let runtime = tokio::runtime::Builder::new_multi_thread() + .worker_threads(2) + .enable_all() + .build() + .unwrap(); + + let rest_operations = run_application(); + let res = System::attach_to_tokio("actix-main-system", runtime, rest_operations); + + assert_eq!(res, 2); +} + +#[tokio::test] +async fn attack_to_tokio_macro() { + use actix_rt::System; + + let rest_operations = run_application(); + let res = System::attach_to_tokio( + "actix-main-system", + tokio::runtime::Runtime::handle(&self), + rest_operations, + ); + + assert_eq!(res, 2); +} From a633d2353c5f9281a5a3ba274fef1f4f0147d4cf Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Wed, 27 Jan 2021 11:23:28 +0000 Subject: [PATCH 44/73] fix addr iterator --- actix-tls/src/connect/connect.rs | 39 +++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/actix-tls/src/connect/connect.rs b/actix-tls/src/connect/connect.rs index ab7e03dc..9e5d417f 100755 --- a/actix-tls/src/connect/connect.rs +++ b/actix-tls/src/connect/connect.rs @@ -175,7 +175,10 @@ impl Iterator for ConnectAddrsIter<'_> { fn next(&mut self) -> Option { match *self { Self::None => None, - Self::One(addr) => Some(addr), + Self::One(addr) => { + *self = Self::None; + Some(addr) + } Self::Multi(ref mut iter) => iter.next().copied(), Self::MultiOwned(ref mut iter) => iter.next(), } @@ -282,6 +285,8 @@ fn parse_host(host: &str) -> (&str, Option) { #[cfg(test)] mod tests { + use std::net::{IpAddr, Ipv4Addr}; + use super::*; #[test] @@ -292,4 +297,36 @@ mod tests { assert_eq!(parse_host("example.com:false"), ("example.com", None)); assert_eq!(parse_host("example.com:false:false"), ("example.com", None)); } + + #[test] + fn test_addr_iter_multi() { + let localhost = SocketAddr::from((IpAddr::from(Ipv4Addr::LOCALHOST), 8080)); + let unspecified = SocketAddr::from((IpAddr::from(Ipv4Addr::UNSPECIFIED), 8080)); + + let mut addrs = VecDeque::new(); + addrs.push_back(localhost); + addrs.push_back(unspecified); + + let mut iter = ConnectAddrsIter::Multi(addrs.iter()); + assert_eq!(iter.next(), Some(localhost)); + assert_eq!(iter.next(), Some(unspecified)); + assert_eq!(iter.next(), None); + + let mut iter = ConnectAddrsIter::MultiOwned(addrs.into_iter()); + assert_eq!(iter.next(), Some(localhost)); + assert_eq!(iter.next(), Some(unspecified)); + assert_eq!(iter.next(), None); + } + + #[test] + fn test_addr_iter_single() { + let localhost = SocketAddr::from((IpAddr::from(Ipv4Addr::LOCALHOST), 8080)); + + let mut iter = ConnectAddrsIter::One(localhost); + assert_eq!(iter.next(), Some(localhost)); + assert_eq!(iter.next(), None); + + let mut iter = ConnectAddrsIter::None; + assert_eq!(iter.next(), None); + } } From feac376c17f563e79b45be9bee3690b008c54ee7 Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Thu, 28 Jan 2021 02:31:57 -0800 Subject: [PATCH 45/73] fix actix-tls build (#252) --- actix-rt/tests/integration_tests.rs | 14 -------------- actix-tls/src/connect/ssl/openssl.rs | 4 ++-- actix-tls/src/connect/ssl/rustls.rs | 4 ++-- 3 files changed, 4 insertions(+), 18 deletions(-) diff --git a/actix-rt/tests/integration_tests.rs b/actix-rt/tests/integration_tests.rs index fd579827..225fd53b 100644 --- a/actix-rt/tests/integration_tests.rs +++ b/actix-rt/tests/integration_tests.rs @@ -172,17 +172,3 @@ fn attack_to_tokio() { assert_eq!(res, 2); } - -#[tokio::test] -async fn attack_to_tokio_macro() { - use actix_rt::System; - - let rest_operations = run_application(); - let res = System::attach_to_tokio( - "actix-main-system", - tokio::runtime::Runtime::handle(&self), - rest_operations, - ); - - assert_eq!(res, 2); -} diff --git a/actix-tls/src/connect/ssl/openssl.rs b/actix-tls/src/connect/ssl/openssl.rs index 0006163d..b1c53f56 100755 --- a/actix-tls/src/connect/ssl/openssl.rs +++ b/actix-tls/src/connect/ssl/openssl.rs @@ -86,7 +86,7 @@ where fn call(&self, stream: Connection) -> Self::Future { trace!("SSL Handshake start for: {:?}", stream.host()); - let (io, stream) = stream.replace(()); + let (io, stream) = stream.replace_io(()); let host = stream.host(); let config = self @@ -123,7 +123,7 @@ where Ok(_) => { let stream = this.stream.take().unwrap(); trace!("SSL Handshake success: {:?}", stream.host()); - Poll::Ready(Ok(stream.replace(this.io.take().unwrap()).1)) + Poll::Ready(Ok(stream.replace_io(this.io.take().unwrap()).1)) } Err(e) => { trace!("SSL Handshake error: {:?}", e); diff --git a/actix-tls/src/connect/ssl/rustls.rs b/actix-tls/src/connect/ssl/rustls.rs index 9d7623ee..9bb5f80f 100755 --- a/actix-tls/src/connect/ssl/rustls.rs +++ b/actix-tls/src/connect/ssl/rustls.rs @@ -86,7 +86,7 @@ where fn call(&self, stream: Connection) -> Self::Future { trace!("SSL Handshake start for: {:?}", stream.host()); - let (io, stream) = stream.replace(()); + let (io, stream) = stream.replace_io(()); let host = DNSNameRef::try_from_ascii_str(stream.host()) .expect("rustls currently only handles hostname-based connections. See https://github.com/briansmith/webpki/issues/54"); ConnectAsyncExt { @@ -113,6 +113,6 @@ where let stream = ready!(Pin::new(&mut this.fut).poll(cx))?; let s = this.stream.take().unwrap(); trace!("SSL Handshake success: {:?}", s.host()); - Poll::Ready(Ok(s.replace(stream).1)) + Poll::Ready(Ok(s.replace_io(stream).1)) } } From ba39c8436d5ec3512e140040fa8930b31acd4458 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Fri, 29 Jan 2021 02:21:06 +0000 Subject: [PATCH 46/73] remove tokio runners (#253) --- actix-rt/CHANGES.md | 9 ++ actix-rt/Cargo.toml | 1 + actix-rt/src/arbiter.rs | 121 +++++++++---------------- actix-rt/src/builder.rs | 61 +------------ actix-rt/src/lib.rs | 10 ++- actix-rt/src/system.rs | 131 ++-------------------------- actix-rt/tests/integration_tests.rs | 91 ++++++------------- actix-server/src/accept.rs | 2 +- actix-server/src/builder.rs | 12 +-- actix-server/src/worker.rs | 2 +- 10 files changed, 103 insertions(+), 337 deletions(-) diff --git a/actix-rt/CHANGES.md b/actix-rt/CHANGES.md index 7b89b2fa..c38373dd 100644 --- a/actix-rt/CHANGES.md +++ b/actix-rt/CHANGES.md @@ -2,6 +2,15 @@ ## Unreleased - 2021-xx-xx +* Remove `run_in_tokio`, `attach_to_tokio` and `AsyncSystemRunner`. [#253] +* Return `JoinHandle` from `actix_rt::spawn`. [#253] +* Remove old `Arbiter::spawn`. Implementation is now inlined into `actix_rt::spawn`. [#253] +* Rename `Arbiter::{send => spawn}` and `Arbiter::{exec_fn => spawn_fn}`. [#253] +* Remove `Arbiter::exec`. [#253] +* Remove deprecated `Arbiter::local_join` and `Arbiter::is_running`. [#253] + +[#253]: https://github.com/actix/actix-net/pull/253 + ## 2.0.0-beta.2 - 2021-01-09 * Add `task` mod with re-export of `tokio::task::{spawn_blocking, yield_now, JoinHandle}` [#245] diff --git a/actix-rt/Cargo.toml b/actix-rt/Cargo.toml index 5bc79416..f5a6ba6a 100644 --- a/actix-rt/Cargo.toml +++ b/actix-rt/Cargo.toml @@ -22,6 +22,7 @@ macros = ["actix-macros"] [dependencies] actix-macros = { version = "0.2.0-beta.1", optional = true } +futures-core = { version = "0.3", default-features = false } tokio = { version = "1", features = ["rt", "net", "parking_lot", "signal", "sync", "time"] } [dev-dependencies] diff --git a/actix-rt/src/arbiter.rs b/actix-rt/src/arbiter.rs index 48d11b36..fde3cd1c 100644 --- a/actix-rt/src/arbiter.rs +++ b/actix-rt/src/arbiter.rs @@ -10,10 +10,11 @@ use std::{ thread, }; +use futures_core::ready; use tokio::{ sync::{ mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}, - oneshot::{channel, error::RecvError as Canceled, Sender}, + oneshot::Sender, }, task::LocalSet, }; @@ -86,12 +87,6 @@ impl Arbiter { }) } - /// Check if current arbiter is running. - #[deprecated(note = "Thread local variables for running state of Arbiter is removed")] - pub fn is_running() -> bool { - false - } - /// Stop arbiter from continuing it's event loop. pub fn stop(&self) { let _ = self.sender.send(ArbiterCommand::Stop); @@ -121,16 +116,16 @@ impl Arbiter { // register arbiter let _ = System::current() - .sys() + .tx() .send(SystemCommand::RegisterArbiter(id, arb)); // start arbiter controller // run loop rt.block_on(ArbiterController { rx }); - // unregister arbiter + // deregister arbiter let _ = System::current() - .sys() + .tx() .send(SystemCommand::DeregisterArbiter(id)); } }) @@ -144,67 +139,35 @@ impl Arbiter { } } - /// Spawn a future on the current thread. This does not create a new Arbiter - /// or Arbiter address, it is simply a helper for spawning futures on the current - /// thread. - pub fn spawn(future: F) + /// Send a future to the Arbiter's thread and spawn it. + /// + /// If you require a result, include a response channel in the future. + /// + /// Returns true if future was sent successfully and false if the Arbiter has died. + pub fn spawn(&self, future: Fut) -> bool where - F: Future + 'static, + Fut: Future + Unpin + Send + 'static, { - let _ = tokio::task::spawn_local(future); + match self.sender.send(ArbiterCommand::Execute(Box::new(future))) { + Ok(_) => true, + Err(_) => false, + } } - /// Executes a future on the current thread. This does not create a new Arbiter - /// or Arbiter address, it is simply a helper for executing futures on the current - /// thread. - pub fn spawn_fn(f: F) - where - F: FnOnce() -> R + 'static, - R: Future + 'static, - { - Arbiter::spawn(async { - f(); - }) - } - - /// Send a future to the Arbiter's thread, and spawn it. - pub fn send(&self, future: F) - where - F: Future + Send + Unpin + 'static, - { - let _ = self.sender.send(ArbiterCommand::Execute(Box::new(future))); - } - - /// Send a function to the Arbiter's thread, and execute it. Any result from the function - /// is discarded. - pub fn exec_fn(&self, f: F) + /// Send a function to the Arbiter's thread and execute it. + /// + /// Any result from the function is discarded. If you require a result, include a response + /// channel in the function. + /// + /// Returns true if function was sent successfully and false if the Arbiter has died. + pub fn spawn_fn(&self, f: F) -> bool where F: FnOnce() + Send + 'static, { - let _ = self - .sender - .send(ArbiterCommand::ExecuteFn(Box::new(move || { - f(); - }))); - } - - /// Send a function to the Arbiter's thread. This function will be executed asynchronously. - /// A future is created, and when resolved will contain the result of the function sent - /// to the Arbiters thread. - pub fn exec(&self, f: F) -> impl Future> - where - F: FnOnce() -> R + Send + 'static, - R: Send + 'static, - { - let (tx, rx) = channel(); - let _ = self - .sender - .send(ArbiterCommand::ExecuteFn(Box::new(move || { - if !tx.is_closed() { - let _ = tx.send(f()); - } - }))); - rx + match self.sender.send(ArbiterCommand::ExecuteFn(Box::new(f))) { + Ok(_) => true, + Err(_) => false, + } } /// Set item to arbiter storage @@ -266,13 +229,6 @@ impl Arbiter { Ok(()) } } - - /// Returns a future that will be completed once all currently spawned futures - /// have completed. - #[deprecated(since = "2.0.0", note = "Arbiter::local_join function is removed.")] - pub async fn local_join() { - unimplemented!("Arbiter::local_join function is removed.") - } } struct ArbiterController { @@ -281,6 +237,7 @@ struct ArbiterController { impl Drop for ArbiterController { fn drop(&mut self) { + // panics can only occur with spawn_fn calls if thread::panicking() { if System::current().stop_on_panic() { eprintln!("Panic in Arbiter thread, shutting down system."); @@ -296,10 +253,14 @@ impl Future for ArbiterController { type Output = (); fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + // process all items currently buffered in channel loop { - match Pin::new(&mut self.rx).poll_recv(cx) { - Poll::Ready(None) => return Poll::Ready(()), - Poll::Ready(Some(item)) => match item { + match ready!(Pin::new(&mut self.rx).poll_recv(cx)) { + // channel closed; no more messages can be received + None => return Poll::Ready(()), + + // process arbiter command + Some(item) => match item { ArbiterCommand::Stop => return Poll::Ready(()), ArbiterCommand::Execute(fut) => { tokio::task::spawn_local(fut); @@ -308,7 +269,6 @@ impl Future for ArbiterController { f.call_box(); } }, - Poll::Pending => return Poll::Pending, } } } @@ -342,10 +302,14 @@ impl Future for SystemArbiter { type Output = (); fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + // process all items currently buffered in channel loop { - match Pin::new(&mut self.commands).poll_recv(cx) { - Poll::Ready(None) => return Poll::Ready(()), - Poll::Ready(Some(cmd)) => match cmd { + match ready!(Pin::new(&mut self.commands).poll_recv(cx)) { + // channel closed; no more messages can be received + None => return Poll::Ready(()), + + // process system command + Some(cmd) => match cmd { SystemCommand::Exit(code) => { // stop arbiters for arb in self.arbiters.values() { @@ -363,7 +327,6 @@ impl Future for SystemArbiter { self.arbiters.remove(&name); } }, - Poll::Pending => return Poll::Pending, } } } diff --git a/actix-rt/src/builder.rs b/actix-rt/src/builder.rs index c43af7c5..56cfcb91 100644 --- a/actix-rt/src/builder.rs +++ b/actix-rt/src/builder.rs @@ -1,11 +1,8 @@ use std::{borrow::Cow, future::Future, io}; -use tokio::{ - sync::{ - mpsc::unbounded_channel, - oneshot::{channel, Receiver}, - }, - task::LocalSet, +use tokio::sync::{ + mpsc::unbounded_channel, + oneshot::{channel, Receiver}, }; use crate::{ @@ -56,13 +53,6 @@ impl Builder { self.create_runtime(|| {}) } - /// Create new System that can run asynchronously. - /// - /// This method panics if it cannot start the system arbiter - pub(crate) fn build_async(self, local: &LocalSet) -> AsyncSystemRunner { - self.create_async_runtime(local) - } - /// This function will start Tokio runtime and will finish once the `System::stop()` message /// is called. Function `f` is called within Tokio runtime context. pub fn run(self, f: F) -> io::Result<()> @@ -72,22 +62,6 @@ impl Builder { self.create_runtime(f).run() } - fn create_async_runtime(self, local: &LocalSet) -> AsyncSystemRunner { - let (stop_tx, stop_rx) = channel(); - let (sys_sender, sys_receiver) = unbounded_channel(); - - let system = - System::construct(sys_sender, Arbiter::new_system(local), self.stop_on_panic); - - // system arbiter - let arb = SystemArbiter::new(stop_tx, sys_receiver); - - // start the system arbiter - let _ = local.spawn_local(arb); - - AsyncSystemRunner { system, stop_rx } - } - fn create_runtime(self, f: F) -> SystemRunner where F: FnOnce(), @@ -115,35 +89,6 @@ impl Builder { } } -#[derive(Debug)] -pub(crate) struct AsyncSystemRunner { - system: System, - stop_rx: Receiver, -} - -impl AsyncSystemRunner { - /// This function will start event loop and returns a future that resolves once the - /// `System::stop()` function is called. - pub(crate) async fn run(self) -> Result<(), io::Error> { - let AsyncSystemRunner { stop_rx: stop, .. } = self; - - // run loop - match stop.await { - Ok(code) => { - if code != 0 { - Err(io::Error::new( - io::ErrorKind::Other, - format!("Non-zero exit code: {}", code), - )) - } else { - Ok(()) - } - } - Err(e) => Err(io::Error::new(io::ErrorKind::Other, e)), - } - } -} - /// Helper object that runs System's event loop #[must_use = "SystemRunner must be run"] #[derive(Debug)] diff --git a/actix-rt/src/lib.rs b/actix-rt/src/lib.rs index 2151952e..c2222a79 100644 --- a/actix-rt/src/lib.rs +++ b/actix-rt/src/lib.rs @@ -8,6 +8,8 @@ use std::future::Future; +use tokio::task::JoinHandle; + // Cannot define a main macro when compiled into test harness. // Workaround for https://github.com/rust-lang/rust/issues/62127. #[cfg(all(feature = "macros", not(test)))] @@ -26,13 +28,13 @@ pub use self::system::System; /// Spawns a future on the current arbiter. /// /// # Panics -/// This function panics if actix system is not running. +/// Panics if Actix system is not running. #[inline] -pub fn spawn(f: F) +pub fn spawn(f: Fut) -> JoinHandle<()> where - F: Future + 'static, + Fut: Future + 'static, { - Arbiter::spawn(f) + tokio::task::spawn_local(f) } /// Asynchronous signal handling diff --git a/actix-rt/src/system.rs b/actix-rt/src/system.rs index 262f60a6..64080a63 100644 --- a/actix-rt/src/system.rs +++ b/actix-rt/src/system.rs @@ -1,11 +1,10 @@ use std::{ cell::RefCell, - future::Future, io, sync::atomic::{AtomicUsize, Ordering}, }; -use tokio::{sync::mpsc::UnboundedSender, task::LocalSet}; +use tokio::sync::mpsc::UnboundedSender; use crate::{ arbiter::{Arbiter, SystemCommand}, @@ -18,7 +17,7 @@ static SYSTEM_COUNT: AtomicUsize = AtomicUsize::new(0); #[derive(Clone, Debug)] pub struct System { id: usize, - sys: UnboundedSender, + tx: UnboundedSender, arbiter: Arbiter, stop_on_panic: bool, } @@ -35,7 +34,7 @@ impl System { stop_on_panic: bool, ) -> Self { let sys = System { - sys, + tx: sys, arbiter, stop_on_panic, id: SYSTEM_COUNT.fetch_add(1, Ordering::SeqCst), @@ -55,126 +54,10 @@ impl System { /// /// This method panics if it can not create tokio runtime #[allow(clippy::new_ret_no_self)] - pub fn new>(name: T) -> SystemRunner { + pub fn new(name: impl Into) -> SystemRunner { Self::builder().name(name).build() } - /// Create new system using provided tokio `LocalSet`. - /// - /// This method panics if it can not spawn system arbiter - /// - /// Note: This method uses provided `LocalSet` to create a `System` future only. - /// All the [`Arbiter`]s will be started in separate threads using their own tokio `Runtime`s. - /// It means that using this method currently it is impossible to make `actix-rt` work in the - /// alternative Tokio runtimes such as those provided by `tokio_compat`. - /// - /// # Examples - /// ``` - /// use tokio::{runtime::Runtime, task::LocalSet}; - /// use actix_rt::System; - /// use futures_util::future::try_join_all; - /// - /// async fn run_application() { - /// let first_task = tokio::spawn(async { - /// // ... - /// # println!("One task"); - /// # Ok::<(),()>(()) - /// }); - /// - /// let second_task = tokio::spawn(async { - /// // ... - /// # println!("Another task"); - /// # Ok::<(),()>(()) - /// }); - /// - /// try_join_all(vec![first_task, second_task]) - /// .await - /// .expect("Some of the futures finished unexpectedly"); - /// } - /// - /// let runtime = tokio::runtime::Builder::new_multi_thread() - /// .worker_threads(2) - /// .enable_all() - /// .build() - /// .unwrap(); - /// - /// let actix_system_task = LocalSet::new(); - /// let sys = System::run_in_tokio("actix-main-system", &actix_system_task); - /// actix_system_task.spawn_local(sys); - /// - /// let rest_operations = run_application(); - /// runtime.block_on(actix_system_task.run_until(rest_operations)); - /// ``` - pub fn run_in_tokio>( - name: T, - local: &LocalSet, - ) -> impl Future> { - Self::builder().name(name).build_async(local).run() - } - - /// Consume the provided Tokio Runtime and start the `System` in it. - /// This method will create a `LocalSet` object and occupy the current thread - /// for the created `System` exclusively. All the other asynchronous tasks that - /// should be executed as well must be aggregated into one future, provided as the last - /// argument to this method. - /// - /// Note: This method uses provided `Runtime` to create a `System` future only. - /// All the [`Arbiter`]s will be started in separate threads using their own Tokio `Runtime`s. - /// It means that using this method currently it is impossible to make `actix-rt` work in the - /// alternative Tokio runtimes such as those provided by `tokio_compat`. - /// - /// # Arguments - /// - /// - `name`: Name of the System - /// - `runtime`: A Tokio Runtime to run the system in. - /// - `rest_operations`: A future to be executed in the runtime along with the System. - /// - /// # Examples - /// ``` - /// use tokio::runtime::Runtime; - /// use actix_rt::System; - /// use futures_util::future::try_join_all; - /// - /// async fn run_application() { - /// let first_task = tokio::spawn(async { - /// // ... - /// # println!("One task"); - /// # Ok::<(),()>(()) - /// }); - /// - /// let second_task = tokio::spawn(async { - /// // ... - /// # println!("Another task"); - /// # Ok::<(),()>(()) - /// }); - /// - /// try_join_all(vec![first_task, second_task]) - /// .await - /// .expect("Some of the futures finished unexpectedly"); - /// } - /// - /// - /// let runtime = tokio::runtime::Builder::new_multi_thread() - /// .worker_threads(2) - /// .enable_all() - /// .build() - /// .unwrap(); - /// - /// let rest_operations = run_application(); - /// System::attach_to_tokio("actix-main-system", runtime, rest_operations); - /// ``` - pub fn attach_to_tokio( - name: impl Into, - runtime: tokio::runtime::Runtime, - rest_operations: Fut, - ) -> Fut::Output { - let actix_system_task = LocalSet::new(); - let sys = System::run_in_tokio(name.into(), &actix_system_task); - actix_system_task.spawn_local(sys); - - runtime.block_on(actix_system_task.run_until(rest_operations)) - } - /// Get current running system. pub fn current() -> System { CURRENT.with(|cell| match *cell.borrow() { @@ -219,11 +102,11 @@ impl System { /// Stop the system with a particular exit code. pub fn stop_with_code(&self, code: i32) { - let _ = self.sys.send(SystemCommand::Exit(code)); + let _ = self.tx.send(SystemCommand::Exit(code)); } - pub(crate) fn sys(&self) -> &UnboundedSender { - &self.sys + pub(crate) fn tx(&self) -> &UnboundedSender { + &self.tx } /// Return status of 'stop_on_panic' option which controls whether the System is stopped when an diff --git a/actix-rt/tests/integration_tests.rs b/actix-rt/tests/integration_tests.rs index 225fd53b..abaff1c9 100644 --- a/actix-rt/tests/integration_tests.rs +++ b/actix-rt/tests/integration_tests.rs @@ -1,6 +1,9 @@ -use std::time::{Duration, Instant}; +use std::{ + thread, + time::{Duration, Instant}, +}; -use futures_util::future::try_join_all; +use actix_rt::{Arbiter, System}; #[test] fn await_for_timer() { @@ -21,7 +24,7 @@ fn join_another_arbiter() { let instant = Instant::now(); actix_rt::System::new("test_join_another_arbiter").block_on(async move { let mut arbiter = actix_rt::Arbiter::new(); - arbiter.send(Box::pin(async move { + arbiter.spawn(Box::pin(async move { tokio::time::sleep(time).await; actix_rt::Arbiter::current().stop(); })); @@ -35,7 +38,7 @@ fn join_another_arbiter() { let instant = Instant::now(); actix_rt::System::new("test_join_another_arbiter").block_on(async move { let mut arbiter = actix_rt::Arbiter::new(); - arbiter.exec_fn(move || { + arbiter.spawn_fn(move || { actix_rt::spawn(async move { tokio::time::sleep(time).await; actix_rt::Arbiter::current().stop(); @@ -51,7 +54,7 @@ fn join_another_arbiter() { let instant = Instant::now(); actix_rt::System::new("test_join_another_arbiter").block_on(async move { let mut arbiter = actix_rt::Arbiter::new(); - arbiter.send(Box::pin(async move { + arbiter.spawn(Box::pin(async move { tokio::time::sleep(time).await; actix_rt::Arbiter::current().stop(); })); @@ -104,71 +107,31 @@ fn wait_for_spawns() { } #[test] -fn run_in_existing_tokio() { - use actix_rt::System; - use futures_util::future::try_join_all; - use tokio::task::LocalSet; +#[should_panic] +fn arbiter_drop_panic_fn() { + let _ = System::new("test-system"); - async fn run_application() { - let first_task = tokio::spawn(async { - println!("One task"); - Ok::<(), ()>(()) - }); + let mut arbiter = Arbiter::new(); + arbiter.spawn_fn(|| panic!("test")); - let second_task = tokio::spawn(async { - println!("Another task"); - Ok::<(), ()>(()) - }); - - try_join_all(vec![first_task, second_task]) - .await - .expect("Some of the futures finished unexpectedly"); - } - - let runtime = tokio::runtime::Builder::new_multi_thread() - .worker_threads(2) - .enable_all() - .build() - .unwrap(); - - let actix_local_set = LocalSet::new(); - let sys = System::run_in_tokio("actix-main-system", &actix_local_set); - actix_local_set.spawn_local(sys); - - let rest_operations = run_application(); - runtime.block_on(actix_local_set.run_until(rest_operations)); -} - -async fn run_application() -> usize { - let first_task = tokio::spawn(async { - println!("One task"); - Ok::<(), ()>(()) - }); - - let second_task = tokio::spawn(async { - println!("Another task"); - Ok::<(), ()>(()) - }); - - let tasks = try_join_all(vec![first_task, second_task]) - .await - .expect("Some of the futures finished unexpectedly"); - - tasks.len() + arbiter.join().unwrap(); } #[test] -fn attack_to_tokio() { - use actix_rt::System; +fn arbiter_drop_no_panic_fut() { + use futures_util::future::lazy; - let runtime = tokio::runtime::Builder::new_multi_thread() - .worker_threads(2) - .enable_all() - .build() - .unwrap(); + let _ = System::new("test-system"); - let rest_operations = run_application(); - let res = System::attach_to_tokio("actix-main-system", runtime, rest_operations); + let mut arbiter = Arbiter::new(); + arbiter.spawn(lazy(|_| panic!("test"))); - assert_eq!(res, 2); + let arb = arbiter.clone(); + let thread = thread::spawn(move || { + thread::sleep(Duration::from_millis(200)); + arb.stop(); + }); + + arbiter.join().unwrap(); + thread.join().unwrap(); } diff --git a/actix-server/src/accept.rs b/actix-server/src/accept.rs index bf895f06..5c434709 100644 --- a/actix-server/src/accept.rs +++ b/actix-server/src/accept.rs @@ -401,7 +401,7 @@ impl Accept { // after the sleep a Timer interest is sent to Accept Poll let waker = self.waker.clone(); - System::current().arbiter().send(Box::pin(async move { + System::current().arbiter().spawn(Box::pin(async move { sleep_until(Instant::now() + Duration::from_millis(510)).await; waker.wake(WakerInterest::Timer); })); diff --git a/actix-server/src/builder.rs b/actix-server/src/builder.rs index 08ab2d09..a78e4175 100644 --- a/actix-server/src/builder.rs +++ b/actix-server/src/builder.rs @@ -6,7 +6,7 @@ use std::{io, mem}; use actix_rt::net::TcpStream; use actix_rt::time::{sleep_until, Instant}; -use actix_rt::{spawn, System}; +use actix_rt::{self as rt, System}; use log::{error, info}; use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver}; use tokio::sync::oneshot; @@ -288,7 +288,7 @@ impl ServerBuilder { // start http server actor let server = self.server.clone(); - spawn(self); + rt::spawn(self); server } } @@ -364,7 +364,7 @@ impl ServerBuilder { let fut = join_all(iter); - spawn(async move { + rt::spawn(async move { let _ = fut.await; if let Some(tx) = completion { let _ = tx.send(()); @@ -373,16 +373,16 @@ impl ServerBuilder { let _ = tx.send(()); } if exit { - spawn(async { + rt::spawn(async { sleep_until(Instant::now() + Duration::from_millis(300)).await; System::current().stop(); }); } - }) + }); } else { // we need to stop system if server was spawned if self.exit { - spawn(async { + rt::spawn(async { sleep_until(Instant::now() + Duration::from_millis(300)).await; System::current().stop(); }); diff --git a/actix-server/src/worker.rs b/actix-server/src/worker.rs index 91e98fc2..d54a0829 100644 --- a/actix-server/src/worker.rs +++ b/actix-server/src/worker.rs @@ -172,7 +172,7 @@ impl Worker { let avail = availability.clone(); // every worker runs in it's own arbiter. - Arbiter::new().send(Box::pin(async move { + Arbiter::new().spawn(Box::pin(async move { availability.set(false); let mut wrk = MAX_CONNS_COUNTER.with(move |conns| Worker { rx, From 6b86b5efc59b947b5d002df5684bdfcccdd6b007 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Fri, 29 Jan 2021 04:08:14 +0000 Subject: [PATCH 47/73] rename arbiter to worker (#254) --- actix-rt/CHANGES.md | 2 + actix-rt/src/arbiter.rs | 347 ---------------------------- actix-rt/src/builder.rs | 9 +- actix-rt/src/lib.rs | 24 +- actix-rt/src/system.rs | 94 ++++++-- actix-rt/src/worker.rs | 294 +++++++++++++++++++++++ actix-rt/tests/integration_tests.rs | 137 ----------- actix-rt/tests/tests.rs | 160 +++++++++++++ actix-server/src/builder.rs | 4 +- actix-server/src/worker.rs | 15 +- 10 files changed, 559 insertions(+), 527 deletions(-) delete mode 100644 actix-rt/src/arbiter.rs create mode 100644 actix-rt/src/worker.rs delete mode 100644 actix-rt/tests/integration_tests.rs create mode 100644 actix-rt/tests/tests.rs diff --git a/actix-rt/CHANGES.md b/actix-rt/CHANGES.md index c38373dd..73bcac5c 100644 --- a/actix-rt/CHANGES.md +++ b/actix-rt/CHANGES.md @@ -8,8 +8,10 @@ * Rename `Arbiter::{send => spawn}` and `Arbiter::{exec_fn => spawn_fn}`. [#253] * Remove `Arbiter::exec`. [#253] * Remove deprecated `Arbiter::local_join` and `Arbiter::is_running`. [#253] +* Rename `Arbiter => Worker`. [#254] [#253]: https://github.com/actix/actix-net/pull/253 +[#254]: https://github.com/actix/actix-net/pull/254 ## 2.0.0-beta.2 - 2021-01-09 diff --git a/actix-rt/src/arbiter.rs b/actix-rt/src/arbiter.rs deleted file mode 100644 index fde3cd1c..00000000 --- a/actix-rt/src/arbiter.rs +++ /dev/null @@ -1,347 +0,0 @@ -use std::{ - any::{Any, TypeId}, - cell::RefCell, - collections::HashMap, - fmt, - future::Future, - pin::Pin, - sync::atomic::{AtomicUsize, Ordering}, - task::{Context, Poll}, - thread, -}; - -use futures_core::ready; -use tokio::{ - sync::{ - mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}, - oneshot::Sender, - }, - task::LocalSet, -}; - -use crate::{runtime::Runtime, system::System}; - -pub(crate) static COUNT: AtomicUsize = AtomicUsize::new(0); - -thread_local!( - static ADDR: RefCell> = RefCell::new(None); - static STORAGE: RefCell>> = RefCell::new(HashMap::new()); -); - -pub(crate) enum ArbiterCommand { - Stop, - Execute(Box + Unpin + Send>), - ExecuteFn(Box), -} - -impl fmt::Debug for ArbiterCommand { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - ArbiterCommand::Stop => write!(f, "ArbiterCommand::Stop"), - ArbiterCommand::Execute(_) => write!(f, "ArbiterCommand::Execute"), - ArbiterCommand::ExecuteFn(_) => write!(f, "ArbiterCommand::ExecuteFn"), - } - } -} - -/// Arbiters provide an asynchronous execution environment for actors, functions and futures. When -/// an Arbiter is created, it spawns a new OS thread, and hosts an event loop. Some Arbiter -/// functions execute on the current thread. -#[derive(Debug)] -pub struct Arbiter { - sender: UnboundedSender, - thread_handle: Option>, -} - -impl Clone for Arbiter { - fn clone(&self) -> Self { - Self::with_sender(self.sender.clone()) - } -} - -impl Default for Arbiter { - fn default() -> Self { - Self::new() - } -} - -impl Arbiter { - pub(crate) fn new_system(local: &LocalSet) -> Self { - let (tx, rx) = unbounded_channel(); - - let arb = Arbiter::with_sender(tx); - ADDR.with(|cell| *cell.borrow_mut() = Some(arb.clone())); - STORAGE.with(|cell| cell.borrow_mut().clear()); - - local.spawn_local(ArbiterController { rx }); - - arb - } - - /// Returns the current thread's arbiter's address. If no Arbiter is present, then this - /// function will panic! - pub fn current() -> Arbiter { - ADDR.with(|cell| match *cell.borrow() { - Some(ref addr) => addr.clone(), - None => panic!("Arbiter is not running"), - }) - } - - /// Stop arbiter from continuing it's event loop. - pub fn stop(&self) { - let _ = self.sender.send(ArbiterCommand::Stop); - } - - /// Spawn new thread and run event loop in spawned thread. - /// Returns address of newly created arbiter. - pub fn new() -> Arbiter { - let id = COUNT.fetch_add(1, Ordering::Relaxed); - let name = format!("actix-rt:worker:{}", id); - let sys = System::current(); - let (tx, rx) = unbounded_channel(); - - let handle = thread::Builder::new() - .name(name.clone()) - .spawn({ - let tx = tx.clone(); - move || { - let rt = Runtime::new().expect("Can not create Runtime"); - let arb = Arbiter::with_sender(tx); - - STORAGE.with(|cell| cell.borrow_mut().clear()); - - System::set_current(sys); - - ADDR.with(|cell| *cell.borrow_mut() = Some(arb.clone())); - - // register arbiter - let _ = System::current() - .tx() - .send(SystemCommand::RegisterArbiter(id, arb)); - - // start arbiter controller - // run loop - rt.block_on(ArbiterController { rx }); - - // deregister arbiter - let _ = System::current() - .tx() - .send(SystemCommand::DeregisterArbiter(id)); - } - }) - .unwrap_or_else(|err| { - panic!("Cannot spawn an arbiter's thread {:?}: {:?}", &name, err) - }); - - Arbiter { - sender: tx, - thread_handle: Some(handle), - } - } - - /// Send a future to the Arbiter's thread and spawn it. - /// - /// If you require a result, include a response channel in the future. - /// - /// Returns true if future was sent successfully and false if the Arbiter has died. - pub fn spawn(&self, future: Fut) -> bool - where - Fut: Future + Unpin + Send + 'static, - { - match self.sender.send(ArbiterCommand::Execute(Box::new(future))) { - Ok(_) => true, - Err(_) => false, - } - } - - /// Send a function to the Arbiter's thread and execute it. - /// - /// Any result from the function is discarded. If you require a result, include a response - /// channel in the function. - /// - /// Returns true if function was sent successfully and false if the Arbiter has died. - pub fn spawn_fn(&self, f: F) -> bool - where - F: FnOnce() + Send + 'static, - { - match self.sender.send(ArbiterCommand::ExecuteFn(Box::new(f))) { - Ok(_) => true, - Err(_) => false, - } - } - - /// Set item to arbiter storage - pub fn set_item(item: T) { - STORAGE.with(move |cell| cell.borrow_mut().insert(TypeId::of::(), Box::new(item))); - } - - /// Check if arbiter storage contains item - pub fn contains_item() -> bool { - STORAGE.with(move |cell| cell.borrow().get(&TypeId::of::()).is_some()) - } - - /// Get a reference to a type previously inserted on this arbiter's storage. - /// - /// Panics is item is not inserted - pub fn get_item(mut f: F) -> R - where - F: FnMut(&T) -> R, - { - STORAGE.with(move |cell| { - let st = cell.borrow(); - let item = st - .get(&TypeId::of::()) - .and_then(|boxed| (&**boxed as &(dyn Any + 'static)).downcast_ref()) - .unwrap(); - f(item) - }) - } - - /// Get a mutable reference to a type previously inserted on this arbiter's storage. - /// - /// Panics is item is not inserted - pub fn get_mut_item(mut f: F) -> R - where - F: FnMut(&mut T) -> R, - { - STORAGE.with(move |cell| { - let mut st = cell.borrow_mut(); - let item = st - .get_mut(&TypeId::of::()) - .and_then(|boxed| (&mut **boxed as &mut (dyn Any + 'static)).downcast_mut()) - .unwrap(); - f(item) - }) - } - - fn with_sender(sender: UnboundedSender) -> Self { - Self { - sender, - thread_handle: None, - } - } - - /// Wait for the event loop to stop by joining the underlying thread (if have Some). - pub fn join(&mut self) -> thread::Result<()> { - if let Some(thread_handle) = self.thread_handle.take() { - thread_handle.join() - } else { - Ok(()) - } - } -} - -struct ArbiterController { - rx: UnboundedReceiver, -} - -impl Drop for ArbiterController { - fn drop(&mut self) { - // panics can only occur with spawn_fn calls - if thread::panicking() { - if System::current().stop_on_panic() { - eprintln!("Panic in Arbiter thread, shutting down system."); - System::current().stop_with_code(1) - } else { - eprintln!("Panic in Arbiter thread."); - } - } - } -} - -impl Future for ArbiterController { - type Output = (); - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - // process all items currently buffered in channel - loop { - match ready!(Pin::new(&mut self.rx).poll_recv(cx)) { - // channel closed; no more messages can be received - None => return Poll::Ready(()), - - // process arbiter command - Some(item) => match item { - ArbiterCommand::Stop => return Poll::Ready(()), - ArbiterCommand::Execute(fut) => { - tokio::task::spawn_local(fut); - } - ArbiterCommand::ExecuteFn(f) => { - f.call_box(); - } - }, - } - } - } -} - -#[derive(Debug)] -pub(crate) enum SystemCommand { - Exit(i32), - RegisterArbiter(usize, Arbiter), - DeregisterArbiter(usize), -} - -#[derive(Debug)] -pub(crate) struct SystemArbiter { - stop: Option>, - commands: UnboundedReceiver, - arbiters: HashMap, -} - -impl SystemArbiter { - pub(crate) fn new(stop: Sender, commands: UnboundedReceiver) -> Self { - SystemArbiter { - commands, - stop: Some(stop), - arbiters: HashMap::new(), - } - } -} - -impl Future for SystemArbiter { - type Output = (); - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - // process all items currently buffered in channel - loop { - match ready!(Pin::new(&mut self.commands).poll_recv(cx)) { - // channel closed; no more messages can be received - None => return Poll::Ready(()), - - // process system command - Some(cmd) => match cmd { - SystemCommand::Exit(code) => { - // stop arbiters - for arb in self.arbiters.values() { - arb.stop(); - } - // stop event loop - if let Some(stop) = self.stop.take() { - let _ = stop.send(code); - } - } - SystemCommand::RegisterArbiter(name, hnd) => { - self.arbiters.insert(name, hnd); - } - SystemCommand::DeregisterArbiter(name) => { - self.arbiters.remove(&name); - } - }, - } - } - } -} - -pub trait FnExec: Send + 'static { - fn call_box(self: Box); -} - -impl FnExec for F -where - F: FnOnce() + Send + 'static, -{ - #[allow(clippy::boxed_local)] - fn call_box(self: Box) { - (*self)() - } -} diff --git a/actix-rt/src/builder.rs b/actix-rt/src/builder.rs index 56cfcb91..01c85512 100644 --- a/actix-rt/src/builder.rs +++ b/actix-rt/src/builder.rs @@ -6,9 +6,9 @@ use tokio::sync::{ }; use crate::{ - arbiter::{Arbiter, SystemArbiter}, runtime::Runtime, - system::System, + system::{System, SystemWorker}, + worker::Worker, }; /// Builder an actix runtime. @@ -73,12 +73,11 @@ impl Builder { let system = System::construct( sys_sender, - Arbiter::new_system(rt.local()), + Worker::new_system(rt.local()), self.stop_on_panic, ); - // system arbiter - let arb = SystemArbiter::new(stop_tx, sys_receiver); + let arb = SystemWorker::new(sys_receiver, stop_tx); rt.spawn(arb); diff --git a/actix-rt/src/lib.rs b/actix-rt/src/lib.rs index c2222a79..212c0c65 100644 --- a/actix-rt/src/lib.rs +++ b/actix-rt/src/lib.rs @@ -15,17 +15,17 @@ use tokio::task::JoinHandle; #[cfg(all(feature = "macros", not(test)))] pub use actix_macros::{main, test}; -mod arbiter; mod builder; mod runtime; mod system; +mod worker; -pub use self::arbiter::Arbiter; pub use self::builder::{Builder, SystemRunner}; pub use self::runtime::Runtime; pub use self::system::System; +pub use self::worker::Worker; -/// Spawns a future on the current arbiter. +/// Spawns a future on the current [Arbiter]. /// /// # Panics /// Panics if Actix system is not running. @@ -37,33 +37,29 @@ where tokio::task::spawn_local(f) } -/// Asynchronous signal handling pub mod signal { + //! Asynchronous signal handling (Tokio re-exports). + #[cfg(unix)] pub mod unix { - //! Unix specific signals. + //! Unix specific signals (Tokio re-exports). pub use tokio::signal::unix::*; } pub use tokio::signal::ctrl_c; } pub mod net { - //! TCP/UDP/Unix bindings + //! TCP/UDP/Unix bindings (Tokio re-exports). pub use tokio::net::UdpSocket; pub use tokio::net::{TcpListener, TcpStream}; #[cfg(unix)] - mod unix { - pub use tokio::net::{UnixDatagram, UnixListener, UnixStream}; - } - - #[cfg(unix)] - pub use self::unix::*; + pub use tokio::net::{UnixDatagram, UnixListener, UnixStream}; } pub mod time { - //! Utilities for tracking time. + //! Utilities for tracking time (Tokio re-exports). pub use tokio::time::Instant; pub use tokio::time::{interval, interval_at, Interval}; @@ -72,7 +68,7 @@ pub mod time { } pub mod task { - //! Task management. + //! Task management (Tokio re-exports). pub use tokio::task::{spawn_blocking, yield_now, JoinHandle}; } diff --git a/actix-rt/src/system.rs b/actix-rt/src/system.rs index 64080a63..e7830175 100644 --- a/actix-rt/src/system.rs +++ b/actix-rt/src/system.rs @@ -1,14 +1,19 @@ use std::{ cell::RefCell, + collections::HashMap, + future::Future, io, + pin::Pin, sync::atomic::{AtomicUsize, Ordering}, + task::{Context, Poll}, }; -use tokio::sync::mpsc::UnboundedSender; +use futures_core::ready; +use tokio::sync::{mpsc, oneshot}; use crate::{ - arbiter::{Arbiter, SystemCommand}, builder::{Builder, SystemRunner}, + worker::Worker, }; static SYSTEM_COUNT: AtomicUsize = AtomicUsize::new(0); @@ -17,8 +22,8 @@ static SYSTEM_COUNT: AtomicUsize = AtomicUsize::new(0); #[derive(Clone, Debug)] pub struct System { id: usize, - tx: UnboundedSender, - arbiter: Arbiter, + tx: mpsc::UnboundedSender, + worker: Worker, stop_on_panic: bool, } @@ -29,13 +34,13 @@ thread_local!( impl System { /// Constructs new system and sets it as current pub(crate) fn construct( - sys: UnboundedSender, - arbiter: Arbiter, + sys: mpsc::UnboundedSender, + worker: Worker, stop_on_panic: bool, ) -> Self { let sys = System { tx: sys, - arbiter, + worker, stop_on_panic, id: SYSTEM_COUNT.fetch_add(1, Ordering::SeqCst), }; @@ -43,7 +48,7 @@ impl System { sys } - /// Build a new system with a customized tokio runtime. + /// Build a new system with a customized Tokio runtime. /// /// This allows to customize the runtime. See [`Builder`] for more information. pub fn builder() -> Builder { @@ -52,7 +57,7 @@ impl System { /// Create new system. /// - /// This method panics if it can not create tokio runtime + /// This method panics if it can not create Tokio runtime #[allow(clippy::new_ret_no_self)] pub fn new(name: impl Into) -> SystemRunner { Self::builder().name(name).build() @@ -105,7 +110,7 @@ impl System { let _ = self.tx.send(SystemCommand::Exit(code)); } - pub(crate) fn tx(&self) -> &UnboundedSender { + pub(crate) fn tx(&self) -> &mpsc::UnboundedSender { &self.tx } @@ -116,12 +121,12 @@ impl System { } /// Get shared reference to system arbiter. - pub fn arbiter(&self) -> &Arbiter { - &self.arbiter + pub fn arbiter(&self) -> &Worker { + &self.worker } - /// This function will start tokio runtime and will finish once the `System::stop()` message - /// is called. Function `f` is called within tokio runtime context. + /// This function will start Tokio runtime and will finish once the `System::stop()` message + /// is called. Function `f` is called within Tokio runtime context. pub fn run(f: F) -> io::Result<()> where F: FnOnce(), @@ -129,3 +134,64 @@ impl System { Self::builder().run(f) } } + +#[derive(Debug)] +pub(crate) enum SystemCommand { + Exit(i32), + RegisterArbiter(usize, Worker), + DeregisterArbiter(usize), +} + +#[derive(Debug)] +pub(crate) struct SystemWorker { + stop: Option>, + commands: mpsc::UnboundedReceiver, + workers: HashMap, +} + +impl SystemWorker { + pub(crate) fn new( + commands: mpsc::UnboundedReceiver, + stop: oneshot::Sender, + ) -> Self { + SystemWorker { + commands, + stop: Some(stop), + workers: HashMap::new(), + } + } +} + +impl Future for SystemWorker { + type Output = (); + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + // process all items currently buffered in channel + loop { + match ready!(Pin::new(&mut self.commands).poll_recv(cx)) { + // channel closed; no more messages can be received + None => return Poll::Ready(()), + + // process system command + Some(cmd) => match cmd { + SystemCommand::Exit(code) => { + // stop arbiters + for arb in self.workers.values() { + arb.stop(); + } + // stop event loop + if let Some(stop) = self.stop.take() { + let _ = stop.send(code); + } + } + SystemCommand::RegisterArbiter(name, hnd) => { + self.workers.insert(name, hnd); + } + SystemCommand::DeregisterArbiter(name) => { + self.workers.remove(&name); + } + }, + } + } + } +} diff --git a/actix-rt/src/worker.rs b/actix-rt/src/worker.rs new file mode 100644 index 00000000..6b022eb2 --- /dev/null +++ b/actix-rt/src/worker.rs @@ -0,0 +1,294 @@ +use std::{ + any::{Any, TypeId}, + cell::RefCell, + collections::HashMap, + fmt, + future::Future, + pin::Pin, + sync::atomic::{AtomicUsize, Ordering}, + task::{Context, Poll}, + thread, +}; + +use futures_core::ready; +use tokio::{sync::mpsc, task::LocalSet}; + +use crate::{ + runtime::Runtime, + system::{System, SystemCommand}, +}; + +pub(crate) static COUNT: AtomicUsize = AtomicUsize::new(0); + +thread_local!( + static ADDR: RefCell> = RefCell::new(None); + static STORAGE: RefCell>> = RefCell::new(HashMap::new()); +); + +pub(crate) enum WorkerCommand { + Stop, + Execute(Box + Unpin + Send>), + ExecuteFn(Box), +} + +impl fmt::Debug for WorkerCommand { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + WorkerCommand::Stop => write!(f, "ArbiterCommand::Stop"), + WorkerCommand::Execute(_) => write!(f, "ArbiterCommand::Execute"), + WorkerCommand::ExecuteFn(_) => write!(f, "ArbiterCommand::ExecuteFn"), + } + } +} + +/// A worker represent a thread that provides an asynchronous execution environment for futures +/// and functions. +/// +/// When a Worker is created, it spawns a new [OS thread](thread), and hosts an event loop. +/// Some Arbiter functions execute on the current thread. +#[derive(Debug)] +pub struct Worker { + sender: mpsc::UnboundedSender, + thread_handle: Option>, +} + +impl Clone for Worker { + fn clone(&self) -> Self { + Self::new_handle(self.sender.clone()) + } +} + +impl Default for Worker { + fn default() -> Self { + Self::new() + } +} + +impl Worker { + pub(crate) fn new_system(local: &LocalSet) -> Self { + let (tx, rx) = mpsc::unbounded_channel(); + + let arb = Worker::new_handle(tx); + ADDR.with(|cell| *cell.borrow_mut() = Some(arb.clone())); + STORAGE.with(|cell| cell.borrow_mut().clear()); + + local.spawn_local(WorkerRunner { rx }); + + arb + } + + fn new_handle(sender: mpsc::UnboundedSender) -> Self { + Self { + sender, + thread_handle: None, + } + } + + /// Returns the current Worker's handle. + /// + /// # Panics + /// Panics if no Worker is running on the current thread. + pub fn current() -> Worker { + ADDR.with(|cell| match *cell.borrow() { + Some(ref addr) => addr.clone(), + None => panic!("Worker is not running."), + }) + } + + /// Stop worker from continuing it's event loop. + pub fn stop(&self) { + let _ = self.sender.send(WorkerCommand::Stop); + } + + /// Spawn new thread and run event loop in spawned thread. + /// + /// Returns handle of newly created worker. + pub fn new() -> Worker { + let id = COUNT.fetch_add(1, Ordering::Relaxed); + let name = format!("actix-rt:worker:{}", id); + let sys = System::current(); + let (tx, rx) = mpsc::unbounded_channel(); + + let handle = thread::Builder::new() + .name(name.clone()) + .spawn({ + let tx = tx.clone(); + move || { + let rt = Runtime::new().expect("Can not create Runtime"); + let arb = Worker::new_handle(tx); + + STORAGE.with(|cell| cell.borrow_mut().clear()); + + System::set_current(sys); + + ADDR.with(|cell| *cell.borrow_mut() = Some(arb.clone())); + + // register worker + let _ = System::current() + .tx() + .send(SystemCommand::RegisterArbiter(id, arb)); + + // run worker event processing loop + rt.block_on(WorkerRunner { rx }); + + // deregister worker + let _ = System::current() + .tx() + .send(SystemCommand::DeregisterArbiter(id)); + } + }) + .unwrap_or_else(|err| { + panic!("Cannot spawn a Worker's thread {:?}: {:?}", &name, err) + }); + + Worker { + sender: tx, + thread_handle: Some(handle), + } + } + + /// Send a future to the Arbiter's thread and spawn it. + /// + /// If you require a result, include a response channel in the future. + /// + /// Returns true if future was sent successfully and false if the Arbiter has died. + pub fn spawn(&self, future: Fut) -> bool + where + Fut: Future + Unpin + Send + 'static, + { + self.sender + .send(WorkerCommand::Execute(Box::new(future))) + .is_ok() + } + + /// Send a function to the Arbiter's thread and execute it. + /// + /// Any result from the function is discarded. If you require a result, include a response + /// channel in the function. + /// + /// Returns true if function was sent successfully and false if the Arbiter has died. + pub fn spawn_fn(&self, f: F) -> bool + where + F: FnOnce() + Send + 'static, + { + self.sender + .send(WorkerCommand::ExecuteFn(Box::new(f))) + .is_ok() + } + + /// Insert item into worker's thread-local storage. + /// + /// Overwrites any item of the same type previously inserted. + pub fn set_item(item: T) { + STORAGE.with(move |cell| cell.borrow_mut().insert(TypeId::of::(), Box::new(item))); + } + + /// Check if worker's thread-local storage contains an item type. + pub fn contains_item() -> bool { + STORAGE.with(move |cell| cell.borrow().contains_key(&TypeId::of::())) + } + + /// Call a function with a shared reference to an item in this worker's thread-local storage. + /// + /// # Examples + /// ``` + /// + /// ``` + /// + /// # Panics + /// Panics if item is not in worker's thread-local item storage. + pub fn get_item(mut f: F) -> R + where + F: FnMut(&T) -> R, + { + STORAGE.with(move |cell| { + let st = cell.borrow(); + + let type_id = TypeId::of::(); + let item = st.get(&type_id).and_then(downcast_ref).unwrap(); + + f(item) + }) + } + + /// Call a function with a mutable reference to an item in this worker's thread-local storage. + /// + /// # Panics + /// Panics if item is not in worker's thread-local item storage. + pub fn get_mut_item(mut f: F) -> R + where + F: FnMut(&mut T) -> R, + { + STORAGE.with(move |cell| { + let mut st = cell.borrow_mut(); + + let type_id = TypeId::of::(); + let item = st.get_mut(&type_id).and_then(downcast_mut).unwrap(); + + f(item) + }) + } + + /// Wait for worker's event loop to complete. + /// + /// Joins the underlying OS thread handle, if contained. + pub fn join(&mut self) -> thread::Result<()> { + if let Some(thread_handle) = self.thread_handle.take() { + thread_handle.join() + } else { + Ok(()) + } + } +} + +/// A persistent worker future that processes worker commands. +struct WorkerRunner { + rx: mpsc::UnboundedReceiver, +} + +impl Drop for WorkerRunner { + fn drop(&mut self) { + // panics can only occur with spawn_fn calls + if thread::panicking() { + if System::current().stop_on_panic() { + eprintln!("Panic in Worker thread, shutting down system."); + System::current().stop_with_code(1) + } else { + eprintln!("Panic in Worker thread."); + } + } + } +} + +impl Future for WorkerRunner { + type Output = (); + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + // process all items currently buffered in channel + loop { + match ready!(Pin::new(&mut self.rx).poll_recv(cx)) { + // channel closed; no more messages can be received + None => return Poll::Ready(()), + + // process arbiter command + Some(item) => match item { + WorkerCommand::Stop => return Poll::Ready(()), + WorkerCommand::Execute(task_fut) => { + tokio::task::spawn_local(task_fut); + } + WorkerCommand::ExecuteFn(task_fn) => { + task_fn(); + } + }, + } + } + } +} + +fn downcast_ref(boxed: &Box) -> Option<&T> { + boxed.downcast_ref() +} + +fn downcast_mut(boxed: &mut Box) -> Option<&mut T> { + boxed.downcast_mut() +} diff --git a/actix-rt/tests/integration_tests.rs b/actix-rt/tests/integration_tests.rs deleted file mode 100644 index abaff1c9..00000000 --- a/actix-rt/tests/integration_tests.rs +++ /dev/null @@ -1,137 +0,0 @@ -use std::{ - thread, - time::{Duration, Instant}, -}; - -use actix_rt::{Arbiter, System}; - -#[test] -fn await_for_timer() { - let time = Duration::from_secs(1); - let instant = Instant::now(); - actix_rt::System::new("test_wait_timer").block_on(async move { - tokio::time::sleep(time).await; - }); - assert!( - instant.elapsed() >= time, - "Block on should poll awaited future to completion" - ); -} - -#[test] -fn join_another_arbiter() { - let time = Duration::from_secs(1); - let instant = Instant::now(); - actix_rt::System::new("test_join_another_arbiter").block_on(async move { - let mut arbiter = actix_rt::Arbiter::new(); - arbiter.spawn(Box::pin(async move { - tokio::time::sleep(time).await; - actix_rt::Arbiter::current().stop(); - })); - arbiter.join().unwrap(); - }); - assert!( - instant.elapsed() >= time, - "Join on another arbiter should complete only when it calls stop" - ); - - let instant = Instant::now(); - actix_rt::System::new("test_join_another_arbiter").block_on(async move { - let mut arbiter = actix_rt::Arbiter::new(); - arbiter.spawn_fn(move || { - actix_rt::spawn(async move { - tokio::time::sleep(time).await; - actix_rt::Arbiter::current().stop(); - }); - }); - arbiter.join().unwrap(); - }); - assert!( - instant.elapsed() >= time, - "Join on a arbiter that has used actix_rt::spawn should wait for said future" - ); - - let instant = Instant::now(); - actix_rt::System::new("test_join_another_arbiter").block_on(async move { - let mut arbiter = actix_rt::Arbiter::new(); - arbiter.spawn(Box::pin(async move { - tokio::time::sleep(time).await; - actix_rt::Arbiter::current().stop(); - })); - arbiter.stop(); - arbiter.join().unwrap(); - }); - assert!( - instant.elapsed() < time, - "Premature stop of arbiter should conclude regardless of it's current state" - ); -} - -#[test] -fn non_static_block_on() { - let string = String::from("test_str"); - let str = string.as_str(); - - let sys = actix_rt::System::new("borrow some"); - - sys.block_on(async { - actix_rt::time::sleep(Duration::from_millis(1)).await; - assert_eq!("test_str", str); - }); - - let rt = actix_rt::Runtime::new().unwrap(); - - rt.block_on(async { - actix_rt::time::sleep(Duration::from_millis(1)).await; - assert_eq!("test_str", str); - }); - - actix_rt::System::run(|| { - assert_eq!("test_str", str); - actix_rt::System::current().stop(); - }) - .unwrap(); -} - -#[test] -fn wait_for_spawns() { - let rt = actix_rt::Runtime::new().unwrap(); - - let handle = rt.spawn(async { - println!("running on the runtime"); - // assertion panic is caught at task boundary - assert_eq!(1, 2); - }); - - assert!(rt.block_on(handle).is_err()); -} - -#[test] -#[should_panic] -fn arbiter_drop_panic_fn() { - let _ = System::new("test-system"); - - let mut arbiter = Arbiter::new(); - arbiter.spawn_fn(|| panic!("test")); - - arbiter.join().unwrap(); -} - -#[test] -fn arbiter_drop_no_panic_fut() { - use futures_util::future::lazy; - - let _ = System::new("test-system"); - - let mut arbiter = Arbiter::new(); - arbiter.spawn(lazy(|_| panic!("test"))); - - let arb = arbiter.clone(); - let thread = thread::spawn(move || { - thread::sleep(Duration::from_millis(200)); - arb.stop(); - }); - - arbiter.join().unwrap(); - thread.join().unwrap(); -} diff --git a/actix-rt/tests/tests.rs b/actix-rt/tests/tests.rs new file mode 100644 index 00000000..d3ce4e68 --- /dev/null +++ b/actix-rt/tests/tests.rs @@ -0,0 +1,160 @@ +use std::{ + thread, + time::{Duration, Instant}, +}; + +use actix_rt::{System, Worker}; + +#[test] +fn await_for_timer() { + let time = Duration::from_secs(1); + let instant = Instant::now(); + System::new("test_wait_timer").block_on(async move { + tokio::time::sleep(time).await; + }); + assert!( + instant.elapsed() >= time, + "Block on should poll awaited future to completion" + ); +} + +#[test] +fn join_another_worker() { + let time = Duration::from_secs(1); + let instant = Instant::now(); + System::new("test_join_another_worker").block_on(async move { + let mut worker = Worker::new(); + worker.spawn(Box::pin(async move { + tokio::time::sleep(time).await; + Worker::current().stop(); + })); + worker.join().unwrap(); + }); + assert!( + instant.elapsed() >= time, + "Join on another worker should complete only when it calls stop" + ); + + let instant = Instant::now(); + System::new("test_join_another_worker").block_on(async move { + let mut worker = Worker::new(); + worker.spawn_fn(move || { + actix_rt::spawn(async move { + tokio::time::sleep(time).await; + Worker::current().stop(); + }); + }); + worker.join().unwrap(); + }); + assert!( + instant.elapsed() >= time, + "Join on a worker that has used actix_rt::spawn should wait for said future" + ); + + let instant = Instant::now(); + System::new("test_join_another_worker").block_on(async move { + let mut worker = Worker::new(); + worker.spawn(Box::pin(async move { + tokio::time::sleep(time).await; + Worker::current().stop(); + })); + worker.stop(); + worker.join().unwrap(); + }); + assert!( + instant.elapsed() < time, + "Premature stop of worker should conclude regardless of it's current state" + ); +} + +#[test] +fn non_static_block_on() { + let string = String::from("test_str"); + let str = string.as_str(); + + let sys = System::new("borrow some"); + + sys.block_on(async { + actix_rt::time::sleep(Duration::from_millis(1)).await; + assert_eq!("test_str", str); + }); + + let rt = actix_rt::Runtime::new().unwrap(); + + rt.block_on(async { + actix_rt::time::sleep(Duration::from_millis(1)).await; + assert_eq!("test_str", str); + }); + + System::run(|| { + assert_eq!("test_str", str); + System::current().stop(); + }) + .unwrap(); +} + +#[test] +fn wait_for_spawns() { + let rt = actix_rt::Runtime::new().unwrap(); + + let handle = rt.spawn(async { + println!("running on the runtime"); + // assertion panic is caught at task boundary + assert_eq!(1, 2); + }); + + assert!(rt.block_on(handle).is_err()); +} + +#[test] +#[should_panic] +fn worker_drop_panic_fn() { + let _ = System::new("test-system"); + + let mut worker = Worker::new(); + worker.spawn_fn(|| panic!("test")); + + worker.join().unwrap(); +} + +#[test] +fn worker_drop_no_panic_fut() { + use futures_util::future::lazy; + + let _ = System::new("test-system"); + + let mut worker = Worker::new(); + worker.spawn(lazy(|_| panic!("test"))); + + worker.stop(); + worker.join().unwrap(); +} + +#[test] +fn worker_item_storage() { + let _ = System::new("test-system"); + + let mut worker = Worker::new(); + + assert!(!Worker::contains_item::()); + Worker::set_item(42u32); + assert!(Worker::contains_item::()); + + Worker::get_item(|&item: &u32| assert_eq!(item, 42)); + Worker::get_mut_item(|&mut item: &mut u32| assert_eq!(item, 42)); + + let thread = thread::spawn(move || { + Worker::get_item(|&_item: &u32| unreachable!("u32 not in this thread")); + }) + .join(); + assert!(thread.is_err()); + + let thread = thread::spawn(move || { + Worker::get_mut_item(|&mut _item: &mut i8| unreachable!("i8 not in this thread")); + }) + .join(); + assert!(thread.is_err()); + + worker.stop(); + worker.join().unwrap(); +} diff --git a/actix-server/src/builder.rs b/actix-server/src/builder.rs index a78e4175..7290f9dd 100644 --- a/actix-server/src/builder.rs +++ b/actix-server/src/builder.rs @@ -19,7 +19,7 @@ use crate::signals::{Signal, Signals}; use crate::socket::{MioListener, StdSocketAddr, StdTcpListener, ToSocketAddrs}; use crate::socket::{MioTcpListener, MioTcpSocket}; use crate::waker_queue::{WakerInterest, WakerQueue}; -use crate::worker::{self, Worker, WorkerAvailability, WorkerHandle}; +use crate::worker::{self, ServerWorker, WorkerAvailability, WorkerHandle}; use crate::{join_all, Token}; /// Server builder @@ -297,7 +297,7 @@ impl ServerBuilder { let avail = WorkerAvailability::new(waker); let services = self.services.iter().map(|v| v.clone_factory()).collect(); - Worker::start(idx, services, avail, self.shutdown_timeout) + ServerWorker::start(idx, services, avail, self.shutdown_timeout) } fn handle_cmd(&mut self, item: ServerCommand) { diff --git a/actix-server/src/worker.rs b/actix-server/src/worker.rs index d54a0829..ae387cbd 100644 --- a/actix-server/src/worker.rs +++ b/actix-server/src/worker.rs @@ -6,7 +6,7 @@ use std::task::{Context, Poll}; use std::time::Duration; use actix_rt::time::{sleep_until, Instant, Sleep}; -use actix_rt::{spawn, Arbiter}; +use actix_rt::{spawn, Worker as Arbiter}; use actix_utils::counter::Counter; use futures_core::future::LocalBoxFuture; use log::{error, info, trace}; @@ -122,11 +122,10 @@ impl WorkerAvailability { } } -/// Service worker +/// Service worker. /// -/// Worker accepts Socket objects via unbounded channel and starts stream -/// processing. -pub(crate) struct Worker { +/// Worker accepts Socket objects via unbounded channel and starts stream processing. +pub(crate) struct ServerWorker { rx: UnboundedReceiver, rx2: UnboundedReceiver, services: Vec, @@ -160,7 +159,7 @@ enum WorkerServiceStatus { Stopped, } -impl Worker { +impl ServerWorker { pub(crate) fn start( idx: usize, factories: Vec>, @@ -174,7 +173,7 @@ impl Worker { // every worker runs in it's own arbiter. Arbiter::new().spawn(Box::pin(async move { availability.set(false); - let mut wrk = MAX_CONNS_COUNTER.with(move |conns| Worker { + let mut wrk = MAX_CONNS_COUNTER.with(move |conns| ServerWorker { rx, rx2, availability, @@ -304,7 +303,7 @@ enum WorkerState { ), } -impl Future for Worker { +impl Future for ServerWorker { type Output = (); fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { From 29244199055abaa868c9ec790c5fbd399b7d71df Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Fri, 29 Jan 2021 14:16:10 +0000 Subject: [PATCH 48/73] prevent spawn_fn panic bubbling (#255) --- actix-rt/src/builder.rs | 41 ++++++++++++----------------------------- actix-rt/src/system.rs | 20 +++++--------------- actix-rt/src/worker.rs | 18 +++--------------- actix-rt/tests/tests.rs | 33 +++++++++++++++++++++++++++++++-- 4 files changed, 51 insertions(+), 61 deletions(-) diff --git a/actix-rt/src/builder.rs b/actix-rt/src/builder.rs index 01c85512..93d2fe28 100644 --- a/actix-rt/src/builder.rs +++ b/actix-rt/src/builder.rs @@ -11,38 +11,25 @@ use crate::{ worker::Worker, }; -/// Builder an actix runtime. +/// System builder. /// /// Either use `Builder::build` to create a system and start actors. Alternatively, use /// `Builder::run` to start the Tokio runtime and run a function in its context. pub struct Builder { /// Name of the System. Defaults to "actix-rt" if unset. name: Cow<'static, str>, - - /// Whether the Arbiter will stop the whole System on uncaught panic. Defaults to false. - stop_on_panic: bool, } impl Builder { pub(crate) fn new() -> Self { Builder { name: Cow::Borrowed("actix-rt"), - stop_on_panic: false, } } /// Sets the name of the System. - pub fn name(mut self, name: impl Into) -> Self { - self.name = Cow::Owned(name.into()); - self - } - - /// Sets the option 'stop_on_panic' which controls whether the System is stopped when an - /// uncaught panic is thrown from a worker thread. - /// - /// Defaults to false. - pub fn stop_on_panic(mut self, stop_on_panic: bool) -> Self { - self.stop_on_panic = stop_on_panic; + pub fn name(mut self, name: impl Into>) -> Self { + self.name = name.into(); self } @@ -55,14 +42,14 @@ impl Builder { /// This function will start Tokio runtime and will finish once the `System::stop()` message /// is called. Function `f` is called within Tokio runtime context. - pub fn run(self, f: F) -> io::Result<()> + pub fn run(self, init_fn: F) -> io::Result<()> where F: FnOnce(), { - self.create_runtime(f).run() + self.create_runtime(init_fn).run() } - fn create_runtime(self, f: F) -> SystemRunner + fn create_runtime(self, init_fn: F) -> SystemRunner where F: FnOnce(), { @@ -71,18 +58,14 @@ impl Builder { let rt = Runtime::new().unwrap(); - let system = System::construct( - sys_sender, - Worker::new_system(rt.local()), - self.stop_on_panic, - ); + let system = System::construct(sys_sender, Worker::new_system(rt.local())); - let arb = SystemWorker::new(sys_receiver, stop_tx); + // init system worker + let sys_worker = SystemWorker::new(sys_receiver, stop_tx); + rt.spawn(sys_worker); - rt.spawn(arb); - - // init system arbiter and run configuration method - rt.block_on(async { f() }); + // run system init method + rt.block_on(async { init_fn() }); SystemRunner { rt, stop, system } } diff --git a/actix-rt/src/system.rs b/actix-rt/src/system.rs index e7830175..e3ee720b 100644 --- a/actix-rt/src/system.rs +++ b/actix-rt/src/system.rs @@ -1,4 +1,5 @@ use std::{ + borrow::Cow, cell::RefCell, collections::HashMap, future::Future, @@ -24,7 +25,6 @@ pub struct System { id: usize, tx: mpsc::UnboundedSender, worker: Worker, - stop_on_panic: bool, } thread_local!( @@ -33,15 +33,10 @@ thread_local!( impl System { /// Constructs new system and sets it as current - pub(crate) fn construct( - sys: mpsc::UnboundedSender, - worker: Worker, - stop_on_panic: bool, - ) -> Self { + pub(crate) fn construct(sys: mpsc::UnboundedSender, worker: Worker) -> Self { let sys = System { tx: sys, worker, - stop_on_panic, id: SYSTEM_COUNT.fetch_add(1, Ordering::SeqCst), }; System::set_current(sys.clone()); @@ -57,9 +52,10 @@ impl System { /// Create new system. /// - /// This method panics if it can not create Tokio runtime + /// # Panics + /// Panics if underlying Tokio runtime can not be created. #[allow(clippy::new_ret_no_self)] - pub fn new(name: impl Into) -> SystemRunner { + pub fn new(name: impl Into>) -> SystemRunner { Self::builder().name(name).build() } @@ -114,12 +110,6 @@ impl System { &self.tx } - /// Return status of 'stop_on_panic' option which controls whether the System is stopped when an - /// uncaught panic is thrown from a worker thread. - pub(crate) fn stop_on_panic(&self) -> bool { - self.stop_on_panic - } - /// Get shared reference to system arbiter. pub fn arbiter(&self) -> &Worker { &self.worker diff --git a/actix-rt/src/worker.rs b/actix-rt/src/worker.rs index 6b022eb2..f586d15f 100644 --- a/actix-rt/src/worker.rs +++ b/actix-rt/src/worker.rs @@ -246,20 +246,6 @@ struct WorkerRunner { rx: mpsc::UnboundedReceiver, } -impl Drop for WorkerRunner { - fn drop(&mut self) { - // panics can only occur with spawn_fn calls - if thread::panicking() { - if System::current().stop_on_panic() { - eprintln!("Panic in Worker thread, shutting down system."); - System::current().stop_with_code(1) - } else { - eprintln!("Panic in Worker thread."); - } - } - } -} - impl Future for WorkerRunner { type Output = (); @@ -277,7 +263,9 @@ impl Future for WorkerRunner { tokio::task::spawn_local(task_fut); } WorkerCommand::ExecuteFn(task_fn) => { - task_fn(); + tokio::task::spawn_local(async { + task_fn(); + }); } }, } diff --git a/actix-rt/tests/tests.rs b/actix-rt/tests/tests.rs index d3ce4e68..e05e8975 100644 --- a/actix-rt/tests/tests.rs +++ b/actix-rt/tests/tests.rs @@ -1,4 +1,5 @@ use std::{ + sync::mpsc::sync_channel, thread, time::{Duration, Instant}, }; @@ -107,13 +108,29 @@ fn wait_for_spawns() { } #[test] -#[should_panic] -fn worker_drop_panic_fn() { +fn worker_spawn_fn_runs() { + let _ = System::new("test-system"); + + let (tx, rx) = sync_channel::(1); + + let mut worker = Worker::new(); + worker.spawn_fn(move || tx.send(42).unwrap()); + + let num = rx.recv().unwrap(); + assert_eq!(num, 42); + + worker.stop(); + worker.join().unwrap(); +} + +#[test] +fn worker_drop_no_panic_fn() { let _ = System::new("test-system"); let mut worker = Worker::new(); worker.spawn_fn(|| panic!("test")); + worker.stop(); worker.join().unwrap(); } @@ -158,3 +175,15 @@ fn worker_item_storage() { worker.stop(); worker.join().unwrap(); } + +#[test] +fn system_name_cow_str() { + let _ = System::new("test-system"); + System::current().stop(); +} + +#[test] +fn system_name_cow_string() { + let _ = System::new("test-system".to_owned()); + System::current().stop(); +} From 1b35ff8ee6df13151aa7e7c7f37a65398e2c2d4e Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Fri, 29 Jan 2021 15:16:30 +0000 Subject: [PATCH 49/73] express spawn fn as spawn fut (#256) --- actix-rt/CHANGES.md | 13 ++-- actix-rt/Cargo.toml | 1 - actix-rt/src/builder.rs | 24 ++++---- actix-rt/src/lib.rs | 24 ++++---- actix-rt/src/system.rs | 33 ++++++---- actix-rt/src/worker.rs | 119 +++++++++++++++++-------------------- actix-rt/tests/tests.rs | 16 ++++- actix-server/src/accept.rs | 11 ++-- 8 files changed, 130 insertions(+), 111 deletions(-) diff --git a/actix-rt/CHANGES.md b/actix-rt/CHANGES.md index 73bcac5c..379afbd7 100644 --- a/actix-rt/CHANGES.md +++ b/actix-rt/CHANGES.md @@ -2,16 +2,19 @@ ## Unreleased - 2021-xx-xx +* Rename `Arbiter => Worker`. [#254] * Remove `run_in_tokio`, `attach_to_tokio` and `AsyncSystemRunner`. [#253] * Return `JoinHandle` from `actix_rt::spawn`. [#253] -* Remove old `Arbiter::spawn`. Implementation is now inlined into `actix_rt::spawn`. [#253] -* Rename `Arbiter::{send => spawn}` and `Arbiter::{exec_fn => spawn_fn}`. [#253] -* Remove `Arbiter::exec`. [#253] -* Remove deprecated `Arbiter::local_join` and `Arbiter::is_running`. [#253] -* Rename `Arbiter => Worker`. [#254] +* Remove old `Worker::spawn`. Implementation is now inlined into `actix_rt::spawn`. [#253] +* Rename `Worker::{send => spawn}` and `Worker::{exec_fn => spawn_fn}`. [#253] +* Remove `Worker::exec`. [#253] +* Remove `System::arbiter`. [#256] +* Remove deprecated `Worker::local_join` and `Worker::is_running`. [#253] +* `Worker::spawn` now accepts !Unpin futures. [#256] [#253]: https://github.com/actix/actix-net/pull/253 [#254]: https://github.com/actix/actix-net/pull/254 +[#256]: https://github.com/actix/actix-net/pull/256 ## 2.0.0-beta.2 - 2021-01-09 diff --git a/actix-rt/Cargo.toml b/actix-rt/Cargo.toml index f5a6ba6a..68ed8563 100644 --- a/actix-rt/Cargo.toml +++ b/actix-rt/Cargo.toml @@ -27,4 +27,3 @@ tokio = { version = "1", features = ["rt", "net", "parking_lot", "signal", "sync [dev-dependencies] tokio = { version = "1", features = ["full"] } -futures-util = { version = "0.3.7", default-features = true, features = ["alloc"] } diff --git a/actix-rt/src/builder.rs b/actix-rt/src/builder.rs index 93d2fe28..f9a3fca2 100644 --- a/actix-rt/src/builder.rs +++ b/actix-rt/src/builder.rs @@ -53,7 +53,7 @@ impl Builder { where F: FnOnce(), { - let (stop_tx, stop) = channel(); + let (stop_tx, stop_rx) = channel(); let (sys_sender, sys_receiver) = unbounded_channel(); let rt = Runtime::new().unwrap(); @@ -67,27 +67,30 @@ impl Builder { // run system init method rt.block_on(async { init_fn() }); - SystemRunner { rt, stop, system } + SystemRunner { + rt, + stop_rx, + system, + } } } -/// Helper object that runs System's event loop -#[must_use = "SystemRunner must be run"] +/// System runner object that keeps event loop alive and running until stop message is received. +#[must_use = "A SystemRunner does nothing unless `run` is called."] #[derive(Debug)] pub struct SystemRunner { rt: Runtime, - stop: Receiver, + stop_rx: Receiver, system: System, } impl SystemRunner { - /// This function will start event loop and will finish once the - /// `System::stop()` function is called. + /// Starts event loop and will finish once [`System::stop()`] is called. pub fn run(self) -> io::Result<()> { - let SystemRunner { rt, stop, .. } = self; + let SystemRunner { rt, stop_rx, .. } = self; // run loop - match rt.block_on(stop) { + match rt.block_on(stop_rx) { Ok(code) => { if code != 0 { Err(io::Error::new( @@ -98,11 +101,12 @@ impl SystemRunner { Ok(()) } } + Err(e) => Err(io::Error::new(io::ErrorKind::Other, e)), } } - /// Execute a future and wait for result. + /// Runs the provided future, blocking the current thread until the future completes. #[inline] pub fn block_on(&self, fut: F) -> F::Output { self.rt.block_on(fut) diff --git a/actix-rt/src/lib.rs b/actix-rt/src/lib.rs index 212c0c65..b0303d6c 100644 --- a/actix-rt/src/lib.rs +++ b/actix-rt/src/lib.rs @@ -25,18 +25,6 @@ pub use self::runtime::Runtime; pub use self::system::System; pub use self::worker::Worker; -/// Spawns a future on the current [Arbiter]. -/// -/// # Panics -/// Panics if Actix system is not running. -#[inline] -pub fn spawn(f: Fut) -> JoinHandle<()> -where - Fut: Future + 'static, -{ - tokio::task::spawn_local(f) -} - pub mod signal { //! Asynchronous signal handling (Tokio re-exports). @@ -72,3 +60,15 @@ pub mod task { pub use tokio::task::{spawn_blocking, yield_now, JoinHandle}; } + +/// Spawns a future on the current [Worker]. +/// +/// # Panics +/// Panics if Actix system is not running. +#[inline] +pub fn spawn(f: Fut) -> JoinHandle<()> +where + Fut: Future + 'static, +{ + tokio::task::spawn_local(f) +} diff --git a/actix-rt/src/system.rs b/actix-rt/src/system.rs index e3ee720b..0182136e 100644 --- a/actix-rt/src/system.rs +++ b/actix-rt/src/system.rs @@ -23,7 +23,8 @@ static SYSTEM_COUNT: AtomicUsize = AtomicUsize::new(0); #[derive(Clone, Debug)] pub struct System { id: usize, - tx: mpsc::UnboundedSender, + sys_tx: mpsc::UnboundedSender, + // TODO: which worker is this exactly worker: Worker, } @@ -32,10 +33,13 @@ thread_local!( ); impl System { - /// Constructs new system and sets it as current - pub(crate) fn construct(sys: mpsc::UnboundedSender, worker: Worker) -> Self { + /// Constructs new system and sets it as current. + pub(crate) fn construct( + sys_tx: mpsc::UnboundedSender, + worker: Worker, + ) -> Self { let sys = System { - tx: sys, + sys_tx, worker, id: SYSTEM_COUNT.fetch_add(1, Ordering::SeqCst), }; @@ -60,6 +64,9 @@ impl System { } /// Get current running system. + /// + /// # Panics + /// Panics if no system is registered on the current thread. pub fn current() -> System { CURRENT.with(|cell| match *cell.borrow() { Some(ref sys) => sys.clone(), @@ -103,15 +110,16 @@ impl System { /// Stop the system with a particular exit code. pub fn stop_with_code(&self, code: i32) { - let _ = self.tx.send(SystemCommand::Exit(code)); + let _ = self.sys_tx.send(SystemCommand::Exit(code)); } pub(crate) fn tx(&self) -> &mpsc::UnboundedSender { - &self.tx + &self.sys_tx } - /// Get shared reference to system arbiter. - pub fn arbiter(&self) -> &Worker { + // TODO: give clarity on which worker this is; previous documented as returning "system worker" + /// Get shared reference to a worker. + pub fn worker(&self) -> &Worker { &self.worker } @@ -165,18 +173,21 @@ impl Future for SystemWorker { // process system command Some(cmd) => match cmd { SystemCommand::Exit(code) => { - // stop arbiters - for arb in self.workers.values() { - arb.stop(); + // stop workers + for wkr in self.workers.values() { + wkr.stop(); } + // stop event loop if let Some(stop) = self.stop.take() { let _ = stop.send(code); } } + SystemCommand::RegisterArbiter(name, hnd) => { self.workers.insert(name, hnd); } + SystemCommand::DeregisterArbiter(name) => { self.workers.remove(&name); } diff --git a/actix-rt/src/worker.rs b/actix-rt/src/worker.rs index f586d15f..adda3cff 100644 --- a/actix-rt/src/worker.rs +++ b/actix-rt/src/worker.rs @@ -27,8 +27,7 @@ thread_local!( pub(crate) enum WorkerCommand { Stop, - Execute(Box + Unpin + Send>), - ExecuteFn(Box), + Execute(Pin + Send>>), } impl fmt::Debug for WorkerCommand { @@ -36,7 +35,6 @@ impl fmt::Debug for WorkerCommand { match self { WorkerCommand::Stop => write!(f, "ArbiterCommand::Stop"), WorkerCommand::Execute(_) => write!(f, "ArbiterCommand::Execute"), - WorkerCommand::ExecuteFn(_) => write!(f, "ArbiterCommand::ExecuteFn"), } } } @@ -65,44 +63,12 @@ impl Default for Worker { } impl Worker { - pub(crate) fn new_system(local: &LocalSet) -> Self { - let (tx, rx) = mpsc::unbounded_channel(); - - let arb = Worker::new_handle(tx); - ADDR.with(|cell| *cell.borrow_mut() = Some(arb.clone())); - STORAGE.with(|cell| cell.borrow_mut().clear()); - - local.spawn_local(WorkerRunner { rx }); - - arb - } - - fn new_handle(sender: mpsc::UnboundedSender) -> Self { - Self { - sender, - thread_handle: None, - } - } - - /// Returns the current Worker's handle. - /// - /// # Panics - /// Panics if no Worker is running on the current thread. - pub fn current() -> Worker { - ADDR.with(|cell| match *cell.borrow() { - Some(ref addr) => addr.clone(), - None => panic!("Worker is not running."), - }) - } - - /// Stop worker from continuing it's event loop. - pub fn stop(&self) { - let _ = self.sender.send(WorkerCommand::Stop); - } - /// Spawn new thread and run event loop in spawned thread. /// /// Returns handle of newly created worker. + /// + /// # Panics + /// Panics if a [System] not registered on the current thread. pub fn new() -> Worker { let id = COUNT.fetch_add(1, Ordering::Relaxed); let name = format!("actix-rt:worker:{}", id); @@ -147,6 +113,22 @@ impl Worker { } } + /// Returns the current Worker's handle. + /// + /// # Panics + /// Panics if no Worker is running on the current thread. + pub fn current() -> Worker { + ADDR.with(|cell| match *cell.borrow() { + Some(ref addr) => addr.clone(), + None => panic!("Worker is not running."), + }) + } + + /// Stop worker from continuing it's event loop. + pub fn stop(&self) { + let _ = self.sender.send(WorkerCommand::Stop); + } + /// Send a future to the Arbiter's thread and spawn it. /// /// If you require a result, include a response channel in the future. @@ -154,10 +136,10 @@ impl Worker { /// Returns true if future was sent successfully and false if the Arbiter has died. pub fn spawn(&self, future: Fut) -> bool where - Fut: Future + Unpin + Send + 'static, + Fut: Future + Send + 'static, { self.sender - .send(WorkerCommand::Execute(Box::new(future))) + .send(WorkerCommand::Execute(Box::pin(future))) .is_ok() } @@ -171,9 +153,37 @@ impl Worker { where F: FnOnce() + Send + 'static, { - self.sender - .send(WorkerCommand::ExecuteFn(Box::new(f))) - .is_ok() + self.spawn(async { f() }) + } + + /// Wait for worker's event loop to complete. + /// + /// Joins the underlying OS thread handle, if contained. + pub fn join(&mut self) -> thread::Result<()> { + if let Some(thread_handle) = self.thread_handle.take() { + thread_handle.join() + } else { + Ok(()) + } + } + + pub(crate) fn new_system(local: &LocalSet) -> Self { + let (tx, rx) = mpsc::unbounded_channel(); + + let arb = Worker::new_handle(tx); + ADDR.with(|cell| *cell.borrow_mut() = Some(arb.clone())); + STORAGE.with(|cell| cell.borrow_mut().clear()); + + local.spawn_local(WorkerRunner { rx }); + + arb + } + + fn new_handle(sender: mpsc::UnboundedSender) -> Self { + Self { + sender, + thread_handle: None, + } } /// Insert item into worker's thread-local storage. @@ -190,11 +200,6 @@ impl Worker { /// Call a function with a shared reference to an item in this worker's thread-local storage. /// - /// # Examples - /// ``` - /// - /// ``` - /// /// # Panics /// Panics if item is not in worker's thread-local item storage. pub fn get_item(mut f: F) -> R @@ -228,17 +233,6 @@ impl Worker { f(item) }) } - - /// Wait for worker's event loop to complete. - /// - /// Joins the underlying OS thread handle, if contained. - pub fn join(&mut self) -> thread::Result<()> { - if let Some(thread_handle) = self.thread_handle.take() { - thread_handle.join() - } else { - Ok(()) - } - } } /// A persistent worker future that processes worker commands. @@ -256,17 +250,12 @@ impl Future for WorkerRunner { // channel closed; no more messages can be received None => return Poll::Ready(()), - // process arbiter command + // process worker command Some(item) => match item { WorkerCommand::Stop => return Poll::Ready(()), WorkerCommand::Execute(task_fut) => { tokio::task::spawn_local(task_fut); } - WorkerCommand::ExecuteFn(task_fn) => { - tokio::task::spawn_local(async { - task_fn(); - }); - } }, } } diff --git a/actix-rt/tests/tests.rs b/actix-rt/tests/tests.rs index e05e8975..ec71656c 100644 --- a/actix-rt/tests/tests.rs +++ b/actix-rt/tests/tests.rs @@ -136,12 +136,10 @@ fn worker_drop_no_panic_fn() { #[test] fn worker_drop_no_panic_fut() { - use futures_util::future::lazy; - let _ = System::new("test-system"); let mut worker = Worker::new(); - worker.spawn(lazy(|_| panic!("test"))); + worker.spawn(async { panic!("test") }); worker.stop(); worker.join().unwrap(); @@ -187,3 +185,15 @@ fn system_name_cow_string() { let _ = System::new("test-system".to_owned()); System::current().stop(); } + +#[test] +#[should_panic] +fn no_system_current_panic() { + System::current(); +} + +#[test] +#[should_panic] +fn no_system_worker_new_panic() { + Worker::new(); +} diff --git a/actix-server/src/accept.rs b/actix-server/src/accept.rs index 5c434709..82c00ef5 100644 --- a/actix-server/src/accept.rs +++ b/actix-server/src/accept.rs @@ -1,8 +1,10 @@ use std::time::Duration; use std::{io, thread}; -use actix_rt::time::{sleep_until, Instant}; -use actix_rt::System; +use actix_rt::{ + time::{sleep_until, Instant}, + System, +}; use log::{error, info}; use mio::{Interest, Poll, Token as MioToken}; use slab::Slab; @@ -401,10 +403,11 @@ impl Accept { // after the sleep a Timer interest is sent to Accept Poll let waker = self.waker.clone(); - System::current().arbiter().spawn(Box::pin(async move { + System::current().worker().spawn(async move { sleep_until(Instant::now() + Duration::from_millis(510)).await; waker.wake(WakerInterest::Timer); - })); + }); + return; } } From b75254403a70e5421f9b35757d24cea5dd67f126 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sun, 31 Jan 2021 03:34:07 +0000 Subject: [PATCH 50/73] remove builder and introduce worker handle (#257) --- actix-macros/src/lib.rs | 11 +- actix-rt/CHANGES.md | 23 ++- actix-rt/src/arbiter.rs | 299 ++++++++++++++++++++++++++++++ actix-rt/src/builder.rs | 114 ------------ actix-rt/src/lib.rs | 10 +- actix-rt/src/runtime.rs | 7 +- actix-rt/src/system.rs | 204 +++++++++++--------- actix-rt/src/worker.rs | 271 --------------------------- actix-rt/tests/tests.rs | 169 +++++++++-------- actix-server/src/accept.rs | 2 +- actix-server/src/test_server.rs | 4 +- actix-server/src/worker.rs | 8 +- actix-server/tests/test_server.rs | 8 +- 13 files changed, 545 insertions(+), 585 deletions(-) create mode 100644 actix-rt/src/arbiter.rs delete mode 100644 actix-rt/src/builder.rs delete mode 100644 actix-rt/src/worker.rs diff --git a/actix-macros/src/lib.rs b/actix-macros/src/lib.rs index 60f177fa..54b89565 100644 --- a/actix-macros/src/lib.rs +++ b/actix-macros/src/lib.rs @@ -7,7 +7,7 @@ use proc_macro::TokenStream; use quote::quote; -/// Marks async function to be executed by actix system. +/// Marks async function to be executed by Actix system. /// /// ## Usage /// @@ -26,7 +26,6 @@ pub fn main(_: TokenStream, item: TokenStream) -> TokenStream { let vis = &input.vis; let sig = &mut input.sig; let body = &input.block; - let name = &sig.ident; if sig.asyncness.is_none() { return syn::Error::new_spanned(sig.fn_token, "only async fn is supported") @@ -39,14 +38,14 @@ pub fn main(_: TokenStream, item: TokenStream) -> TokenStream { (quote! { #(#attrs)* #vis #sig { - actix_rt::System::new(stringify!(#name)) + actix_rt::System::new() .block_on(async move { #body }) } }) .into() } -/// Marks async test function to be executed by actix runtime. +/// Marks async test function to be executed by Actix system. /// /// ## Usage /// @@ -86,7 +85,7 @@ pub fn test(_: TokenStream, item: TokenStream) -> TokenStream { quote! { #(#attrs)* #vis #sig { - actix_rt::System::new("test") + actix_rt::System::new() .block_on(async { #body }) } } @@ -95,7 +94,7 @@ pub fn test(_: TokenStream, item: TokenStream) -> TokenStream { #[test] #(#attrs)* #vis #sig { - actix_rt::System::new("test") + actix_rt::System::new() .block_on(async { #body }) } } diff --git a/actix-rt/CHANGES.md b/actix-rt/CHANGES.md index 379afbd7..3f2db63f 100644 --- a/actix-rt/CHANGES.md +++ b/actix-rt/CHANGES.md @@ -1,20 +1,27 @@ # Changes ## Unreleased - 2021-xx-xx - -* Rename `Arbiter => Worker`. [#254] * Remove `run_in_tokio`, `attach_to_tokio` and `AsyncSystemRunner`. [#253] * Return `JoinHandle` from `actix_rt::spawn`. [#253] -* Remove old `Worker::spawn`. Implementation is now inlined into `actix_rt::spawn`. [#253] -* Rename `Worker::{send => spawn}` and `Worker::{exec_fn => spawn_fn}`. [#253] -* Remove `Worker::exec`. [#253] -* Remove `System::arbiter`. [#256] -* Remove deprecated `Worker::local_join` and `Worker::is_running`. [#253] -* `Worker::spawn` now accepts !Unpin futures. [#256] +* Remove old `Arbiter::spawn`. Implementation is now inlined into `actix_rt::spawn`. [#253] +* Rename `Arbiter::{send => spawn}` and `Arbiter::{exec_fn => spawn_fn}`. [#253] +* Remove `Arbiter::exec`. [#253] +* Remove deprecated `Arbiter::local_join` and `Arbiter::is_running`. [#253] +* `Arbiter::spawn` now accepts !Unpin futures. [#256] +* `System::new` no longer takes arguments. [#257] +* Remove `System::with_current`. [#257] +* Remove `Builder`. [#257] +* Add `System::with_init` as replacement for `Builder::run`. [#257] +* Rename `System::{is_set => is_registered}`. [#257] +* Add `ArbiterHandle` for sending messages to non-current-thread arbiters. [#257]. +* `System::arbiter` now returns a `&ArbiterHandle`. [#257] +* Rename `Arbiter::{current => handle}` and return a `ArbiterHandle` instead. [#257] +* `Arbiter::join` now takes self by value. [#257] [#253]: https://github.com/actix/actix-net/pull/253 [#254]: https://github.com/actix/actix-net/pull/254 [#256]: https://github.com/actix/actix-net/pull/256 +[#257]: https://github.com/actix/actix-net/pull/257 ## 2.0.0-beta.2 - 2021-01-09 diff --git a/actix-rt/src/arbiter.rs b/actix-rt/src/arbiter.rs new file mode 100644 index 00000000..6f723111 --- /dev/null +++ b/actix-rt/src/arbiter.rs @@ -0,0 +1,299 @@ +use std::{ + any::{Any, TypeId}, + cell::RefCell, + collections::HashMap, + fmt, + future::Future, + pin::Pin, + sync::atomic::{AtomicUsize, Ordering}, + task::{Context, Poll}, + thread, +}; + +use futures_core::ready; +use tokio::{sync::mpsc, task::LocalSet}; + +use crate::{ + runtime::Runtime, + system::{System, SystemCommand}, +}; + +pub(crate) static COUNT: AtomicUsize = AtomicUsize::new(0); + +thread_local!( + static HANDLE: RefCell> = RefCell::new(None); + static STORAGE: RefCell>> = RefCell::new(HashMap::new()); +); + +pub(crate) enum ArbiterCommand { + Stop, + Execute(Pin + Send>>), +} + +impl fmt::Debug for ArbiterCommand { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ArbiterCommand::Stop => write!(f, "ArbiterCommand::Stop"), + ArbiterCommand::Execute(_) => write!(f, "ArbiterCommand::Execute"), + } + } +} + +/// A handle for sending spawn and stop messages to an [Arbiter]. +#[derive(Debug, Clone)] +pub struct ArbiterHandle { + sender: mpsc::UnboundedSender, +} + +impl ArbiterHandle { + pub(crate) fn new(sender: mpsc::UnboundedSender) -> Self { + Self { sender } + } + + /// Send a future to the [Arbiter]'s thread and spawn it. + /// + /// If you require a result, include a response channel in the future. + /// + /// Returns true if future was sent successfully and false if the [Arbiter] has died. + pub fn spawn(&self, future: Fut) -> bool + where + Fut: Future + Send + 'static, + { + self.sender + .send(ArbiterCommand::Execute(Box::pin(future))) + .is_ok() + } + + /// Send a function to the [Arbiter]'s thread and execute it. + /// + /// Any result from the function is discarded. If you require a result, include a response + /// channel in the function. + /// + /// Returns true if function was sent successfully and false if the [Arbiter] has died. + pub fn spawn_fn(&self, f: F) -> bool + where + F: FnOnce() + Send + 'static, + { + self.spawn(async { f() }) + } + + /// Instruct [Arbiter] to stop processing it's event loop. + /// + /// Returns true if stop message was sent successfully and false if the [Arbiter] has + /// been dropped. + pub fn stop(&self) -> bool { + self.sender.send(ArbiterCommand::Stop).is_ok() + } +} + +/// An Arbiter represents a thread that provides an asynchronous execution environment for futures +/// and functions. +/// +/// When an arbiter is created, it spawns a new [OS thread](thread), and hosts an event loop. +#[derive(Debug)] +pub struct Arbiter { + sender: mpsc::UnboundedSender, + thread_handle: thread::JoinHandle<()>, +} + +impl Arbiter { + /// Spawn new Arbiter thread and start its event loop. + /// + /// # Panics + /// Panics if a [System] is not registered on the current thread. + #[allow(clippy::new_without_default)] + pub fn new() -> Arbiter { + let id = COUNT.fetch_add(1, Ordering::Relaxed); + let system_id = System::current().id(); + let name = format!("actix-rt|system:{}|arbiter:{}", system_id, id); + let sys = System::current(); + let (tx, rx) = mpsc::unbounded_channel(); + + let thread_handle = thread::Builder::new() + .name(name.clone()) + .spawn({ + let tx = tx.clone(); + move || { + let rt = Runtime::new().expect("Can not create Runtime"); + let hnd = ArbiterHandle::new(tx); + + System::set_current(sys); + + STORAGE.with(|cell| cell.borrow_mut().clear()); + HANDLE.with(|cell| *cell.borrow_mut() = Some(hnd.clone())); + + // register arbiter + let _ = System::current() + .tx() + .send(SystemCommand::RegisterArbiter(id, hnd)); + + // run arbiter event processing loop + rt.block_on(ArbiterRunner { rx }); + + // deregister arbiter + let _ = System::current() + .tx() + .send(SystemCommand::DeregisterArbiter(id)); + } + }) + .unwrap_or_else(|err| { + panic!("Cannot spawn Arbiter's thread: {:?}. {:?}", &name, err) + }); + + Arbiter { + sender: tx, + thread_handle, + } + } + + /// Sets up an Arbiter runner on the current thread using the provided runtime local task set. + pub(crate) fn in_new_system(local: &LocalSet) -> ArbiterHandle { + let (tx, rx) = mpsc::unbounded_channel(); + + let hnd = ArbiterHandle::new(tx); + + HANDLE.with(|cell| *cell.borrow_mut() = Some(hnd.clone())); + STORAGE.with(|cell| cell.borrow_mut().clear()); + + local.spawn_local(ArbiterRunner { rx }); + + hnd + } + + /// Return a handle to the Arbiter's message sender. + /// + /// # Panics + /// Panics if no Arbiter is running on the current thread. + pub fn handle() -> ArbiterHandle { + HANDLE.with(|cell| match *cell.borrow() { + Some(ref addr) => addr.clone(), + None => panic!("Arbiter is not running."), + }) + } + + /// Stop Arbiter from continuing it's event loop. + /// + /// Returns true if stop message was sent successfully and false if the Arbiter has been dropped. + pub fn stop(&self) -> bool { + self.sender.send(ArbiterCommand::Stop).is_ok() + } + + /// Send a future to the Arbiter's thread and spawn it. + /// + /// If you require a result, include a response channel in the future. + /// + /// Returns true if future was sent successfully and false if the Arbiter has died. + pub fn spawn(&self, future: Fut) -> bool + where + Fut: Future + Send + 'static, + { + self.sender + .send(ArbiterCommand::Execute(Box::pin(future))) + .is_ok() + } + + /// Send a function to the Arbiter's thread and execute it. + /// + /// Any result from the function is discarded. If you require a result, include a response + /// channel in the function. + /// + /// Returns true if function was sent successfully and false if the Arbiter has died. + pub fn spawn_fn(&self, f: F) -> bool + where + F: FnOnce() + Send + 'static, + { + self.spawn(async { f() }) + } + + /// Wait for Arbiter's event loop to complete. + /// + /// Joins the underlying OS thread handle. See [`JoinHandle::join`](thread::JoinHandle::join). + pub fn join(self) -> thread::Result<()> { + self.thread_handle.join() + } + + /// Insert item into Arbiter's thread-local storage. + /// + /// Overwrites any item of the same type previously inserted. + pub fn set_item(item: T) { + STORAGE.with(move |cell| cell.borrow_mut().insert(TypeId::of::(), Box::new(item))); + } + + /// Check if Arbiter's thread-local storage contains an item type. + pub fn contains_item() -> bool { + STORAGE.with(move |cell| cell.borrow().contains_key(&TypeId::of::())) + } + + /// Call a function with a shared reference to an item in this Arbiter's thread-local storage. + /// + /// # Panics + /// Panics if item is not in Arbiter's thread-local item storage. + pub fn get_item(mut f: F) -> R + where + F: FnMut(&T) -> R, + { + STORAGE.with(move |cell| { + let st = cell.borrow(); + + let type_id = TypeId::of::(); + let item = st.get(&type_id).and_then(downcast_ref).unwrap(); + + f(item) + }) + } + + /// Call a function with a mutable reference to an item in this Arbiter's thread-local storage. + /// + /// # Panics + /// Panics if item is not in Arbiter's thread-local item storage. + pub fn get_mut_item(mut f: F) -> R + where + F: FnMut(&mut T) -> R, + { + STORAGE.with(move |cell| { + let mut st = cell.borrow_mut(); + + let type_id = TypeId::of::(); + let item = st.get_mut(&type_id).and_then(downcast_mut).unwrap(); + + f(item) + }) + } +} + +/// A persistent future that processes [Arbiter] commands. +struct ArbiterRunner { + rx: mpsc::UnboundedReceiver, +} + +impl Future for ArbiterRunner { + type Output = (); + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + // process all items currently buffered in channel + loop { + match ready!(Pin::new(&mut self.rx).poll_recv(cx)) { + // channel closed; no more messages can be received + None => return Poll::Ready(()), + + // process arbiter command + Some(item) => match item { + ArbiterCommand::Stop => { + return Poll::Ready(()); + } + ArbiterCommand::Execute(task_fut) => { + tokio::task::spawn_local(task_fut); + } + }, + } + } + } +} + +fn downcast_ref(boxed: &Box) -> Option<&T> { + boxed.downcast_ref() +} + +fn downcast_mut(boxed: &mut Box) -> Option<&mut T> { + boxed.downcast_mut() +} diff --git a/actix-rt/src/builder.rs b/actix-rt/src/builder.rs deleted file mode 100644 index f9a3fca2..00000000 --- a/actix-rt/src/builder.rs +++ /dev/null @@ -1,114 +0,0 @@ -use std::{borrow::Cow, future::Future, io}; - -use tokio::sync::{ - mpsc::unbounded_channel, - oneshot::{channel, Receiver}, -}; - -use crate::{ - runtime::Runtime, - system::{System, SystemWorker}, - worker::Worker, -}; - -/// System builder. -/// -/// Either use `Builder::build` to create a system and start actors. Alternatively, use -/// `Builder::run` to start the Tokio runtime and run a function in its context. -pub struct Builder { - /// Name of the System. Defaults to "actix-rt" if unset. - name: Cow<'static, str>, -} - -impl Builder { - pub(crate) fn new() -> Self { - Builder { - name: Cow::Borrowed("actix-rt"), - } - } - - /// Sets the name of the System. - pub fn name(mut self, name: impl Into>) -> Self { - self.name = name.into(); - self - } - - /// Create new System. - /// - /// This method panics if it can not create Tokio runtime - pub fn build(self) -> SystemRunner { - self.create_runtime(|| {}) - } - - /// This function will start Tokio runtime and will finish once the `System::stop()` message - /// is called. Function `f` is called within Tokio runtime context. - pub fn run(self, init_fn: F) -> io::Result<()> - where - F: FnOnce(), - { - self.create_runtime(init_fn).run() - } - - fn create_runtime(self, init_fn: F) -> SystemRunner - where - F: FnOnce(), - { - let (stop_tx, stop_rx) = channel(); - let (sys_sender, sys_receiver) = unbounded_channel(); - - let rt = Runtime::new().unwrap(); - - let system = System::construct(sys_sender, Worker::new_system(rt.local())); - - // init system worker - let sys_worker = SystemWorker::new(sys_receiver, stop_tx); - rt.spawn(sys_worker); - - // run system init method - rt.block_on(async { init_fn() }); - - SystemRunner { - rt, - stop_rx, - system, - } - } -} - -/// System runner object that keeps event loop alive and running until stop message is received. -#[must_use = "A SystemRunner does nothing unless `run` is called."] -#[derive(Debug)] -pub struct SystemRunner { - rt: Runtime, - stop_rx: Receiver, - system: System, -} - -impl SystemRunner { - /// Starts event loop and will finish once [`System::stop()`] is called. - pub fn run(self) -> io::Result<()> { - let SystemRunner { rt, stop_rx, .. } = self; - - // run loop - match rt.block_on(stop_rx) { - Ok(code) => { - if code != 0 { - Err(io::Error::new( - io::ErrorKind::Other, - format!("Non-zero exit code: {}", code), - )) - } else { - Ok(()) - } - } - - Err(e) => Err(io::Error::new(io::ErrorKind::Other, e)), - } - } - - /// Runs the provided future, blocking the current thread until the future completes. - #[inline] - pub fn block_on(&self, fut: F) -> F::Output { - self.rt.block_on(fut) - } -} diff --git a/actix-rt/src/lib.rs b/actix-rt/src/lib.rs index b0303d6c..bf8a4796 100644 --- a/actix-rt/src/lib.rs +++ b/actix-rt/src/lib.rs @@ -15,15 +15,13 @@ use tokio::task::JoinHandle; #[cfg(all(feature = "macros", not(test)))] pub use actix_macros::{main, test}; -mod builder; +mod arbiter; mod runtime; mod system; -mod worker; -pub use self::builder::{Builder, SystemRunner}; +pub use self::arbiter::{Arbiter, ArbiterHandle}; pub use self::runtime::Runtime; -pub use self::system::System; -pub use self::worker::Worker; +pub use self::system::{System, SystemRunner}; pub mod signal { //! Asynchronous signal handling (Tokio re-exports). @@ -61,7 +59,7 @@ pub mod task { pub use tokio::task::{spawn_blocking, yield_now, JoinHandle}; } -/// Spawns a future on the current [Worker]. +/// Spawns a future on the current thread. /// /// # Panics /// Panics if Actix system is not running. diff --git a/actix-rt/src/runtime.rs b/actix-rt/src/runtime.rs index c7f611ed..a20dfe7e 100644 --- a/actix-rt/src/runtime.rs +++ b/actix-rt/src/runtime.rs @@ -2,9 +2,10 @@ use std::{future::Future, io}; use tokio::task::{JoinHandle, LocalSet}; -/// Single-threaded runtime provides a way to start reactor and runtime on the current thread. +/// A single-threaded runtime based on Tokio's "current thread" runtime. /// -/// See [crate root][crate] documentation for more details. +/// All spawned futures will be executed on the current thread. Therefore, there is no `Send` bound +/// on submitted futures. #[derive(Debug)] pub struct Runtime { local: LocalSet, @@ -27,7 +28,7 @@ impl Runtime { } /// Reference to local task set. - pub(crate) fn local(&self) -> &LocalSet { + pub(crate) fn local_set(&self) -> &LocalSet { &self.local } diff --git a/actix-rt/src/system.rs b/actix-rt/src/system.rs index 0182136e..d2f38ca8 100644 --- a/actix-rt/src/system.rs +++ b/actix-rt/src/system.rs @@ -1,5 +1,4 @@ use std::{ - borrow::Cow, cell::RefCell, collections::HashMap, future::Future, @@ -12,55 +11,62 @@ use std::{ use futures_core::ready; use tokio::sync::{mpsc, oneshot}; -use crate::{ - builder::{Builder, SystemRunner}, - worker::Worker, -}; +use crate::{arbiter::ArbiterHandle, Arbiter, Runtime}; static SYSTEM_COUNT: AtomicUsize = AtomicUsize::new(0); -/// System is a runtime manager. -#[derive(Clone, Debug)] -pub struct System { - id: usize, - sys_tx: mpsc::UnboundedSender, - // TODO: which worker is this exactly - worker: Worker, -} - thread_local!( static CURRENT: RefCell> = RefCell::new(None); ); +/// A manager for a per-thread distributed async runtime. +#[derive(Clone, Debug)] +pub struct System { + id: usize, + sys_tx: mpsc::UnboundedSender, + + /// Handle to the first [Arbiter] that is created with the System. + arbiter_handle: ArbiterHandle, +} + impl System { - /// Constructs new system and sets it as current. - pub(crate) fn construct( - sys_tx: mpsc::UnboundedSender, - worker: Worker, - ) -> Self { - let sys = System { - sys_tx, - worker, - id: SYSTEM_COUNT.fetch_add(1, Ordering::SeqCst), - }; - System::set_current(sys.clone()); - sys - } - - /// Build a new system with a customized Tokio runtime. - /// - /// This allows to customize the runtime. See [`Builder`] for more information. - pub fn builder() -> Builder { - Builder::new() - } - - /// Create new system. + /// Create a new system. /// /// # Panics /// Panics if underlying Tokio runtime can not be created. #[allow(clippy::new_ret_no_self)] - pub fn new(name: impl Into>) -> SystemRunner { - Self::builder().name(name).build() + pub fn new() -> SystemRunner { + let (stop_tx, stop_rx) = oneshot::channel(); + let (sys_tx, sys_rx) = mpsc::unbounded_channel(); + + let rt = Runtime::new().expect("Actix (Tokio) runtime could not be created."); + let system = System::construct(sys_tx, Arbiter::in_new_system(rt.local_set())); + + // init background system arbiter + let sys_ctrl = SystemController::new(sys_rx, stop_tx); + rt.spawn(sys_ctrl); + + SystemRunner { + rt, + stop_rx, + system, + } + } + + /// Constructs new system and registers it on the current thread. + pub(crate) fn construct( + sys_tx: mpsc::UnboundedSender, + arbiter_handle: ArbiterHandle, + ) -> Self { + let sys = System { + sys_tx, + arbiter_handle, + id: SYSTEM_COUNT.fetch_add(1, Ordering::SeqCst), + }; + + System::set_current(sys.clone()); + + sys } /// Get current running system. @@ -74,31 +80,27 @@ impl System { }) } - /// Check if current system has started. - pub fn is_set() -> bool { - CURRENT.with(|cell| cell.borrow().is_some()) + /// Get handle to a the System's initial [Arbiter]. + pub fn arbiter(&self) -> &ArbiterHandle { + &self.arbiter_handle } - /// Set current running system. + /// Check if there is a System registered on the current thread. + pub fn is_registered() -> bool { + CURRENT.with(|sys| sys.borrow().is_some()) + } + + /// Register given system on current thread. #[doc(hidden)] pub fn set_current(sys: System) { - CURRENT.with(|s| { - *s.borrow_mut() = Some(sys); + CURRENT.with(|cell| { + *cell.borrow_mut() = Some(sys); }) } - /// Execute function with system reference. - pub fn with_current(f: F) -> R - where - F: FnOnce(&System) -> R, - { - CURRENT.with(|cell| match *cell.borrow() { - Some(ref sys) => f(sys), - None => panic!("System is not running"), - }) - } - - /// Numeric system ID. + /// Numeric system identifier. + /// + /// Useful when using multiple Systems. pub fn id(&self) -> usize { self.id } @@ -108,7 +110,7 @@ impl System { self.stop_with_code(0) } - /// Stop the system with a particular exit code. + /// Stop the system with a given exit code. pub fn stop_with_code(&self, code: i32) { let _ = self.sys_tx.send(SystemCommand::Exit(code)); } @@ -116,80 +118,106 @@ impl System { pub(crate) fn tx(&self) -> &mpsc::UnboundedSender { &self.sys_tx } +} - // TODO: give clarity on which worker this is; previous documented as returning "system worker" - /// Get shared reference to a worker. - pub fn worker(&self) -> &Worker { - &self.worker +/// Runner that keeps a [System]'s event loop alive until stop message is received. +#[must_use = "A SystemRunner does nothing unless `run` is called."] +#[derive(Debug)] +pub struct SystemRunner { + rt: Runtime, + stop_rx: oneshot::Receiver, + system: System, +} + +impl SystemRunner { + /// Starts event loop and will return once [System] is [stopped](System::stop). + pub fn run(self) -> io::Result<()> { + let SystemRunner { rt, stop_rx, .. } = self; + + // run loop + match rt.block_on(stop_rx) { + Ok(code) => { + if code != 0 { + Err(io::Error::new( + io::ErrorKind::Other, + format!("Non-zero exit code: {}", code), + )) + } else { + Ok(()) + } + } + + Err(e) => Err(io::Error::new(io::ErrorKind::Other, e)), + } } - /// This function will start Tokio runtime and will finish once the `System::stop()` message - /// is called. Function `f` is called within Tokio runtime context. - pub fn run(f: F) -> io::Result<()> - where - F: FnOnce(), - { - Self::builder().run(f) + /// Runs the provided future, blocking the current thread until the future completes. + #[inline] + pub fn block_on(&self, fut: F) -> F::Output { + self.rt.block_on(fut) } } #[derive(Debug)] pub(crate) enum SystemCommand { Exit(i32), - RegisterArbiter(usize, Worker), + RegisterArbiter(usize, ArbiterHandle), DeregisterArbiter(usize), } +/// There is one `SystemController` per [System]. It runs in the background, keeping track of +/// [Arbiter]s and is able to distribute a system-wide stop command. #[derive(Debug)] -pub(crate) struct SystemWorker { - stop: Option>, - commands: mpsc::UnboundedReceiver, - workers: HashMap, +pub(crate) struct SystemController { + stop_tx: Option>, + cmd_rx: mpsc::UnboundedReceiver, + arbiters: HashMap, } -impl SystemWorker { +impl SystemController { pub(crate) fn new( - commands: mpsc::UnboundedReceiver, - stop: oneshot::Sender, + cmd_rx: mpsc::UnboundedReceiver, + stop_tx: oneshot::Sender, ) -> Self { - SystemWorker { - commands, - stop: Some(stop), - workers: HashMap::new(), + SystemController { + cmd_rx, + stop_tx: Some(stop_tx), + arbiters: HashMap::with_capacity(4), } } } -impl Future for SystemWorker { +impl Future for SystemController { type Output = (); fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { // process all items currently buffered in channel loop { - match ready!(Pin::new(&mut self.commands).poll_recv(cx)) { + match ready!(Pin::new(&mut self.cmd_rx).poll_recv(cx)) { // channel closed; no more messages can be received None => return Poll::Ready(()), // process system command Some(cmd) => match cmd { SystemCommand::Exit(code) => { - // stop workers - for wkr in self.workers.values() { + // stop all arbiters + for wkr in self.arbiters.values() { wkr.stop(); } // stop event loop - if let Some(stop) = self.stop.take() { - let _ = stop.send(code); + // will only fire once + if let Some(stop_tx) = self.stop_tx.take() { + let _ = stop_tx.send(code); } } SystemCommand::RegisterArbiter(name, hnd) => { - self.workers.insert(name, hnd); + self.arbiters.insert(name, hnd); } SystemCommand::DeregisterArbiter(name) => { - self.workers.remove(&name); + self.arbiters.remove(&name); } }, } diff --git a/actix-rt/src/worker.rs b/actix-rt/src/worker.rs deleted file mode 100644 index adda3cff..00000000 --- a/actix-rt/src/worker.rs +++ /dev/null @@ -1,271 +0,0 @@ -use std::{ - any::{Any, TypeId}, - cell::RefCell, - collections::HashMap, - fmt, - future::Future, - pin::Pin, - sync::atomic::{AtomicUsize, Ordering}, - task::{Context, Poll}, - thread, -}; - -use futures_core::ready; -use tokio::{sync::mpsc, task::LocalSet}; - -use crate::{ - runtime::Runtime, - system::{System, SystemCommand}, -}; - -pub(crate) static COUNT: AtomicUsize = AtomicUsize::new(0); - -thread_local!( - static ADDR: RefCell> = RefCell::new(None); - static STORAGE: RefCell>> = RefCell::new(HashMap::new()); -); - -pub(crate) enum WorkerCommand { - Stop, - Execute(Pin + Send>>), -} - -impl fmt::Debug for WorkerCommand { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - WorkerCommand::Stop => write!(f, "ArbiterCommand::Stop"), - WorkerCommand::Execute(_) => write!(f, "ArbiterCommand::Execute"), - } - } -} - -/// A worker represent a thread that provides an asynchronous execution environment for futures -/// and functions. -/// -/// When a Worker is created, it spawns a new [OS thread](thread), and hosts an event loop. -/// Some Arbiter functions execute on the current thread. -#[derive(Debug)] -pub struct Worker { - sender: mpsc::UnboundedSender, - thread_handle: Option>, -} - -impl Clone for Worker { - fn clone(&self) -> Self { - Self::new_handle(self.sender.clone()) - } -} - -impl Default for Worker { - fn default() -> Self { - Self::new() - } -} - -impl Worker { - /// Spawn new thread and run event loop in spawned thread. - /// - /// Returns handle of newly created worker. - /// - /// # Panics - /// Panics if a [System] not registered on the current thread. - pub fn new() -> Worker { - let id = COUNT.fetch_add(1, Ordering::Relaxed); - let name = format!("actix-rt:worker:{}", id); - let sys = System::current(); - let (tx, rx) = mpsc::unbounded_channel(); - - let handle = thread::Builder::new() - .name(name.clone()) - .spawn({ - let tx = tx.clone(); - move || { - let rt = Runtime::new().expect("Can not create Runtime"); - let arb = Worker::new_handle(tx); - - STORAGE.with(|cell| cell.borrow_mut().clear()); - - System::set_current(sys); - - ADDR.with(|cell| *cell.borrow_mut() = Some(arb.clone())); - - // register worker - let _ = System::current() - .tx() - .send(SystemCommand::RegisterArbiter(id, arb)); - - // run worker event processing loop - rt.block_on(WorkerRunner { rx }); - - // deregister worker - let _ = System::current() - .tx() - .send(SystemCommand::DeregisterArbiter(id)); - } - }) - .unwrap_or_else(|err| { - panic!("Cannot spawn a Worker's thread {:?}: {:?}", &name, err) - }); - - Worker { - sender: tx, - thread_handle: Some(handle), - } - } - - /// Returns the current Worker's handle. - /// - /// # Panics - /// Panics if no Worker is running on the current thread. - pub fn current() -> Worker { - ADDR.with(|cell| match *cell.borrow() { - Some(ref addr) => addr.clone(), - None => panic!("Worker is not running."), - }) - } - - /// Stop worker from continuing it's event loop. - pub fn stop(&self) { - let _ = self.sender.send(WorkerCommand::Stop); - } - - /// Send a future to the Arbiter's thread and spawn it. - /// - /// If you require a result, include a response channel in the future. - /// - /// Returns true if future was sent successfully and false if the Arbiter has died. - pub fn spawn(&self, future: Fut) -> bool - where - Fut: Future + Send + 'static, - { - self.sender - .send(WorkerCommand::Execute(Box::pin(future))) - .is_ok() - } - - /// Send a function to the Arbiter's thread and execute it. - /// - /// Any result from the function is discarded. If you require a result, include a response - /// channel in the function. - /// - /// Returns true if function was sent successfully and false if the Arbiter has died. - pub fn spawn_fn(&self, f: F) -> bool - where - F: FnOnce() + Send + 'static, - { - self.spawn(async { f() }) - } - - /// Wait for worker's event loop to complete. - /// - /// Joins the underlying OS thread handle, if contained. - pub fn join(&mut self) -> thread::Result<()> { - if let Some(thread_handle) = self.thread_handle.take() { - thread_handle.join() - } else { - Ok(()) - } - } - - pub(crate) fn new_system(local: &LocalSet) -> Self { - let (tx, rx) = mpsc::unbounded_channel(); - - let arb = Worker::new_handle(tx); - ADDR.with(|cell| *cell.borrow_mut() = Some(arb.clone())); - STORAGE.with(|cell| cell.borrow_mut().clear()); - - local.spawn_local(WorkerRunner { rx }); - - arb - } - - fn new_handle(sender: mpsc::UnboundedSender) -> Self { - Self { - sender, - thread_handle: None, - } - } - - /// Insert item into worker's thread-local storage. - /// - /// Overwrites any item of the same type previously inserted. - pub fn set_item(item: T) { - STORAGE.with(move |cell| cell.borrow_mut().insert(TypeId::of::(), Box::new(item))); - } - - /// Check if worker's thread-local storage contains an item type. - pub fn contains_item() -> bool { - STORAGE.with(move |cell| cell.borrow().contains_key(&TypeId::of::())) - } - - /// Call a function with a shared reference to an item in this worker's thread-local storage. - /// - /// # Panics - /// Panics if item is not in worker's thread-local item storage. - pub fn get_item(mut f: F) -> R - where - F: FnMut(&T) -> R, - { - STORAGE.with(move |cell| { - let st = cell.borrow(); - - let type_id = TypeId::of::(); - let item = st.get(&type_id).and_then(downcast_ref).unwrap(); - - f(item) - }) - } - - /// Call a function with a mutable reference to an item in this worker's thread-local storage. - /// - /// # Panics - /// Panics if item is not in worker's thread-local item storage. - pub fn get_mut_item(mut f: F) -> R - where - F: FnMut(&mut T) -> R, - { - STORAGE.with(move |cell| { - let mut st = cell.borrow_mut(); - - let type_id = TypeId::of::(); - let item = st.get_mut(&type_id).and_then(downcast_mut).unwrap(); - - f(item) - }) - } -} - -/// A persistent worker future that processes worker commands. -struct WorkerRunner { - rx: mpsc::UnboundedReceiver, -} - -impl Future for WorkerRunner { - type Output = (); - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - // process all items currently buffered in channel - loop { - match ready!(Pin::new(&mut self.rx).poll_recv(cx)) { - // channel closed; no more messages can be received - None => return Poll::Ready(()), - - // process worker command - Some(item) => match item { - WorkerCommand::Stop => return Poll::Ready(()), - WorkerCommand::Execute(task_fut) => { - tokio::task::spawn_local(task_fut); - } - }, - } - } - } -} - -fn downcast_ref(boxed: &Box) -> Option<&T> { - boxed.downcast_ref() -} - -fn downcast_mut(boxed: &mut Box) -> Option<&mut T> { - boxed.downcast_mut() -} diff --git a/actix-rt/tests/tests.rs b/actix-rt/tests/tests.rs index ec71656c..7749ad0a 100644 --- a/actix-rt/tests/tests.rs +++ b/actix-rt/tests/tests.rs @@ -1,16 +1,17 @@ use std::{ - sync::mpsc::sync_channel, + sync::mpsc::channel, thread, time::{Duration, Instant}, }; -use actix_rt::{System, Worker}; +use actix_rt::{Arbiter, System}; +use tokio::sync::oneshot; #[test] fn await_for_timer() { let time = Duration::from_secs(1); let instant = Instant::now(); - System::new("test_wait_timer").block_on(async move { + System::new().block_on(async move { tokio::time::sleep(time).await; }); assert!( @@ -20,78 +21,72 @@ fn await_for_timer() { } #[test] -fn join_another_worker() { +fn join_another_arbiter() { let time = Duration::from_secs(1); let instant = Instant::now(); - System::new("test_join_another_worker").block_on(async move { - let mut worker = Worker::new(); - worker.spawn(Box::pin(async move { + System::new().block_on(async move { + let arbiter = Arbiter::new(); + arbiter.spawn(Box::pin(async move { tokio::time::sleep(time).await; - Worker::current().stop(); + Arbiter::handle().stop(); })); - worker.join().unwrap(); + arbiter.join().unwrap(); }); assert!( instant.elapsed() >= time, - "Join on another worker should complete only when it calls stop" + "Join on another arbiter should complete only when it calls stop" ); let instant = Instant::now(); - System::new("test_join_another_worker").block_on(async move { - let mut worker = Worker::new(); - worker.spawn_fn(move || { + System::new().block_on(async move { + let arbiter = Arbiter::new(); + arbiter.spawn_fn(move || { actix_rt::spawn(async move { tokio::time::sleep(time).await; - Worker::current().stop(); + Arbiter::handle().stop(); }); }); - worker.join().unwrap(); + arbiter.join().unwrap(); }); assert!( instant.elapsed() >= time, - "Join on a worker that has used actix_rt::spawn should wait for said future" + "Join on an arbiter that has used actix_rt::spawn should wait for said future" ); let instant = Instant::now(); - System::new("test_join_another_worker").block_on(async move { - let mut worker = Worker::new(); - worker.spawn(Box::pin(async move { + System::new().block_on(async move { + let arbiter = Arbiter::new(); + arbiter.spawn(Box::pin(async move { tokio::time::sleep(time).await; - Worker::current().stop(); + Arbiter::handle().stop(); })); - worker.stop(); - worker.join().unwrap(); + arbiter.stop(); + arbiter.join().unwrap(); }); assert!( instant.elapsed() < time, - "Premature stop of worker should conclude regardless of it's current state" + "Premature stop of arbiter should conclude regardless of it's current state" ); } #[test] fn non_static_block_on() { let string = String::from("test_str"); - let str = string.as_str(); + let string = string.as_str(); - let sys = System::new("borrow some"); + let sys = System::new(); sys.block_on(async { actix_rt::time::sleep(Duration::from_millis(1)).await; - assert_eq!("test_str", str); + assert_eq!("test_str", string); }); let rt = actix_rt::Runtime::new().unwrap(); rt.block_on(async { actix_rt::time::sleep(Duration::from_millis(1)).await; - assert_eq!("test_str", str); + assert_eq!("test_str", string); }); - - System::run(|| { - assert_eq!("test_str", str); - System::current().stop(); - }) - .unwrap(); } #[test] @@ -108,82 +103,70 @@ fn wait_for_spawns() { } #[test] -fn worker_spawn_fn_runs() { - let _ = System::new("test-system"); +fn arbiter_spawn_fn_runs() { + let _ = System::new(); - let (tx, rx) = sync_channel::(1); + let (tx, rx) = channel::(); - let mut worker = Worker::new(); - worker.spawn_fn(move || tx.send(42).unwrap()); + let arbiter = Arbiter::new(); + arbiter.spawn_fn(move || tx.send(42).unwrap()); let num = rx.recv().unwrap(); assert_eq!(num, 42); - worker.stop(); - worker.join().unwrap(); + arbiter.stop(); + arbiter.join().unwrap(); } #[test] -fn worker_drop_no_panic_fn() { - let _ = System::new("test-system"); +fn arbiter_drop_no_panic_fn() { + let _ = System::new(); - let mut worker = Worker::new(); - worker.spawn_fn(|| panic!("test")); + let arbiter = Arbiter::new(); + arbiter.spawn_fn(|| panic!("test")); - worker.stop(); - worker.join().unwrap(); + arbiter.stop(); + arbiter.join().unwrap(); } #[test] -fn worker_drop_no_panic_fut() { - let _ = System::new("test-system"); +fn arbiter_drop_no_panic_fut() { + let _ = System::new(); - let mut worker = Worker::new(); - worker.spawn(async { panic!("test") }); + let arbiter = Arbiter::new(); + arbiter.spawn(async { panic!("test") }); - worker.stop(); - worker.join().unwrap(); + arbiter.stop(); + arbiter.join().unwrap(); } #[test] -fn worker_item_storage() { - let _ = System::new("test-system"); +fn arbiter_item_storage() { + let _ = System::new(); - let mut worker = Worker::new(); + let arbiter = Arbiter::new(); - assert!(!Worker::contains_item::()); - Worker::set_item(42u32); - assert!(Worker::contains_item::()); + assert!(!Arbiter::contains_item::()); + Arbiter::set_item(42u32); + assert!(Arbiter::contains_item::()); - Worker::get_item(|&item: &u32| assert_eq!(item, 42)); - Worker::get_mut_item(|&mut item: &mut u32| assert_eq!(item, 42)); + Arbiter::get_item(|&item: &u32| assert_eq!(item, 42)); + Arbiter::get_mut_item(|&mut item: &mut u32| assert_eq!(item, 42)); let thread = thread::spawn(move || { - Worker::get_item(|&_item: &u32| unreachable!("u32 not in this thread")); + Arbiter::get_item(|&_item: &u32| unreachable!("u32 not in this thread")); }) .join(); assert!(thread.is_err()); let thread = thread::spawn(move || { - Worker::get_mut_item(|&mut _item: &mut i8| unreachable!("i8 not in this thread")); + Arbiter::get_mut_item(|&mut _item: &mut i8| unreachable!("i8 not in this thread")); }) .join(); assert!(thread.is_err()); - worker.stop(); - worker.join().unwrap(); -} - -#[test] -fn system_name_cow_str() { - let _ = System::new("test-system"); - System::current().stop(); -} - -#[test] -fn system_name_cow_string() { - let _ = System::new("test-system".to_owned()); - System::current().stop(); + arbiter.stop(); + arbiter.join().unwrap(); } #[test] @@ -194,6 +177,36 @@ fn no_system_current_panic() { #[test] #[should_panic] -fn no_system_worker_new_panic() { - Worker::new(); +fn no_system_arbiter_new_panic() { + Arbiter::new(); +} + +#[test] +fn system_arbiter_spawn() { + let runner = System::new(); + + let (tx, rx) = oneshot::channel(); + let sys = System::current(); + + thread::spawn(|| { + // this thread will have no arbiter in it's thread local so call will panic + Arbiter::handle(); + }) + .join() + .unwrap_err(); + + let thread = thread::spawn(|| { + // this thread will have no arbiter in it's thread local so use the system handle instead + System::set_current(sys); + let sys = System::current(); + + let wrk = sys.arbiter(); + wrk.spawn(async move { + tx.send(42u32).unwrap(); + System::current().stop(); + }); + }); + + assert_eq!(runner.block_on(rx).unwrap(), 42); + thread.join().unwrap(); } diff --git a/actix-server/src/accept.rs b/actix-server/src/accept.rs index 82c00ef5..a52184d9 100644 --- a/actix-server/src/accept.rs +++ b/actix-server/src/accept.rs @@ -403,7 +403,7 @@ impl Accept { // after the sleep a Timer interest is sent to Accept Poll let waker = self.waker.clone(); - System::current().worker().spawn(async move { + System::current().arbiter().spawn(async move { sleep_until(Instant::now() + Duration::from_millis(510)).await; waker.wake(WakerInterest::Timer); }); diff --git a/actix-server/src/test_server.rs b/actix-server/src/test_server.rs index 4b7f7873..864f391c 100644 --- a/actix-server/src/test_server.rs +++ b/actix-server/src/test_server.rs @@ -48,7 +48,7 @@ impl TestServer { // run server in separate thread thread::spawn(move || { - let sys = System::new("actix-test-server"); + let sys = System::new(); factory(Server::build()).workers(1).disable_signals().run(); tx.send(System::current()).unwrap(); @@ -70,7 +70,7 @@ impl TestServer { // run server in separate thread thread::spawn(move || { - let sys = System::new("actix-test-server"); + let sys = System::new(); let tcp = net::TcpListener::bind("127.0.0.1:0").unwrap(); let local_addr = tcp.local_addr().unwrap(); diff --git a/actix-server/src/worker.rs b/actix-server/src/worker.rs index ae387cbd..9c61e4ad 100644 --- a/actix-server/src/worker.rs +++ b/actix-server/src/worker.rs @@ -6,7 +6,7 @@ use std::task::{Context, Poll}; use std::time::Duration; use actix_rt::time::{sleep_until, Instant, Sleep}; -use actix_rt::{spawn, Worker as Arbiter}; +use actix_rt::{spawn, Arbiter}; use actix_utils::counter::Counter; use futures_core::future::LocalBoxFuture; use log::{error, info, trace}; @@ -215,7 +215,7 @@ impl ServerWorker { } Err(e) => { error!("Can not start worker: {:?}", e); - Arbiter::current().stop(); + Arbiter::handle().stop(); } } wrk.await @@ -386,7 +386,7 @@ impl Future for ServerWorker { let num = num_connections(); if num == 0 { let _ = tx.take().unwrap().send(true); - Arbiter::current().stop(); + Arbiter::handle().stop(); return Poll::Ready(()); } @@ -394,7 +394,7 @@ impl Future for ServerWorker { if Pin::new(t2).poll(cx).is_ready() { let _ = tx.take().unwrap().send(false); self.shutdown(true); - Arbiter::current().stop(); + Arbiter::handle().stop(); return Poll::Ready(()); } diff --git a/actix-server/tests/test_server.rs b/actix-server/tests/test_server.rs index 8d93ba0b..86ec25e6 100644 --- a/actix-server/tests/test_server.rs +++ b/actix-server/tests/test_server.rs @@ -21,7 +21,7 @@ fn test_bind() { let (tx, rx) = mpsc::channel(); let h = thread::spawn(move || { - let sys = actix_rt::System::new("test"); + let sys = actix_rt::System::new(); let srv = sys.block_on(lazy(|_| { Server::build() .workers(1) @@ -47,7 +47,7 @@ fn test_listen() { let (tx, rx) = mpsc::channel(); let h = thread::spawn(move || { - let sys = actix_rt::System::new("test"); + let sys = actix_rt::System::new(); let lst = net::TcpListener::bind(addr).unwrap(); sys.block_on(async { Server::build() @@ -81,7 +81,7 @@ fn test_start() { let (tx, rx) = mpsc::channel(); let h = thread::spawn(move || { - let sys = actix_rt::System::new("test"); + let sys = actix_rt::System::new(); let srv = sys.block_on(lazy(|_| { Server::build() .backlog(100) @@ -150,7 +150,7 @@ fn test_configure() { let h = thread::spawn(move || { let num = num2.clone(); - let sys = actix_rt::System::new("test"); + let sys = actix_rt::System::new(); let srv = sys.block_on(lazy(|_| { Server::build() .disable_signals() From 2fa60b07ae7a3227de7d11097a0625f9ca19cb62 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sun, 31 Jan 2021 04:41:28 +0000 Subject: [PATCH 51/73] prevent arbiter leaks by waiting for registration --- actix-rt/CHANGES.md | 4 ++-- actix-rt/src/arbiter.rs | 35 +++++++++++++++++++---------------- actix-rt/src/system.rs | 20 +++++++++++++------- actix-rt/tests/tests.rs | 31 +++++++++++++++++++++++++------ actix-server/src/worker.rs | 6 +++--- 5 files changed, 62 insertions(+), 34 deletions(-) diff --git a/actix-rt/CHANGES.md b/actix-rt/CHANGES.md index 3f2db63f..a18c57d8 100644 --- a/actix-rt/CHANGES.md +++ b/actix-rt/CHANGES.md @@ -14,8 +14,8 @@ * Add `System::with_init` as replacement for `Builder::run`. [#257] * Rename `System::{is_set => is_registered}`. [#257] * Add `ArbiterHandle` for sending messages to non-current-thread arbiters. [#257]. -* `System::arbiter` now returns a `&ArbiterHandle`. [#257] -* Rename `Arbiter::{current => handle}` and return a `ArbiterHandle` instead. [#257] +* `System::arbiter` now returns an `&ArbiterHandle`. [#257] +* `Arbiter::current` now returns an `ArbiterHandle` instead. [#257] * `Arbiter::join` now takes self by value. [#257] [#253]: https://github.com/actix/actix-net/pull/253 diff --git a/actix-rt/src/arbiter.rs b/actix-rt/src/arbiter.rs index 6f723111..44e34b5d 100644 --- a/actix-rt/src/arbiter.rs +++ b/actix-rt/src/arbiter.rs @@ -42,12 +42,12 @@ impl fmt::Debug for ArbiterCommand { /// A handle for sending spawn and stop messages to an [Arbiter]. #[derive(Debug, Clone)] pub struct ArbiterHandle { - sender: mpsc::UnboundedSender, + tx: mpsc::UnboundedSender, } impl ArbiterHandle { - pub(crate) fn new(sender: mpsc::UnboundedSender) -> Self { - Self { sender } + pub(crate) fn new(tx: mpsc::UnboundedSender) -> Self { + Self { tx } } /// Send a future to the [Arbiter]'s thread and spawn it. @@ -59,7 +59,7 @@ impl ArbiterHandle { where Fut: Future + Send + 'static, { - self.sender + self.tx .send(ArbiterCommand::Execute(Box::pin(future))) .is_ok() } @@ -82,7 +82,7 @@ impl ArbiterHandle { /// Returns true if stop message was sent successfully and false if the [Arbiter] has /// been dropped. pub fn stop(&self) -> bool { - self.sender.send(ArbiterCommand::Stop).is_ok() + self.tx.send(ArbiterCommand::Stop).is_ok() } } @@ -92,7 +92,7 @@ impl ArbiterHandle { /// When an arbiter is created, it spawns a new [OS thread](thread), and hosts an event loop. #[derive(Debug)] pub struct Arbiter { - sender: mpsc::UnboundedSender, + tx: mpsc::UnboundedSender, thread_handle: thread::JoinHandle<()>, } @@ -109,12 +109,14 @@ impl Arbiter { let sys = System::current(); let (tx, rx) = mpsc::unbounded_channel(); + let (ready_tx, ready_rx) = std::sync::mpsc::channel::<()>(); + let thread_handle = thread::Builder::new() .name(name.clone()) .spawn({ let tx = tx.clone(); move || { - let rt = Runtime::new().expect("Can not create Runtime"); + let rt = Runtime::new().expect("Cannot create new Arbiter's Runtime."); let hnd = ArbiterHandle::new(tx); System::set_current(sys); @@ -127,6 +129,8 @@ impl Arbiter { .tx() .send(SystemCommand::RegisterArbiter(id, hnd)); + ready_tx.send(()).unwrap(); + // run arbiter event processing loop rt.block_on(ArbiterRunner { rx }); @@ -140,13 +144,12 @@ impl Arbiter { panic!("Cannot spawn Arbiter's thread: {:?}. {:?}", &name, err) }); - Arbiter { - sender: tx, - thread_handle, - } + ready_rx.recv().unwrap(); + + Arbiter { tx, thread_handle } } - /// Sets up an Arbiter runner on the current thread using the provided runtime local task set. + /// Sets up an Arbiter runner in a new System using the provided runtime local task set. pub(crate) fn in_new_system(local: &LocalSet) -> ArbiterHandle { let (tx, rx) = mpsc::unbounded_channel(); @@ -160,11 +163,11 @@ impl Arbiter { hnd } - /// Return a handle to the Arbiter's message sender. + /// Return a handle to the current thread's Arbiter's message sender. /// /// # Panics /// Panics if no Arbiter is running on the current thread. - pub fn handle() -> ArbiterHandle { + pub fn current() -> ArbiterHandle { HANDLE.with(|cell| match *cell.borrow() { Some(ref addr) => addr.clone(), None => panic!("Arbiter is not running."), @@ -175,7 +178,7 @@ impl Arbiter { /// /// Returns true if stop message was sent successfully and false if the Arbiter has been dropped. pub fn stop(&self) -> bool { - self.sender.send(ArbiterCommand::Stop).is_ok() + self.tx.send(ArbiterCommand::Stop).is_ok() } /// Send a future to the Arbiter's thread and spawn it. @@ -187,7 +190,7 @@ impl Arbiter { where Fut: Future + Send + 'static, { - self.sender + self.tx .send(ArbiterCommand::Execute(Box::pin(future))) .is_ok() } diff --git a/actix-rt/src/system.rs b/actix-rt/src/system.rs index d2f38ca8..58fe3cab 100644 --- a/actix-rt/src/system.rs +++ b/actix-rt/src/system.rs @@ -40,7 +40,13 @@ impl System { let (sys_tx, sys_rx) = mpsc::unbounded_channel(); let rt = Runtime::new().expect("Actix (Tokio) runtime could not be created."); - let system = System::construct(sys_tx, Arbiter::in_new_system(rt.local_set())); + let sys_arbiter = Arbiter::in_new_system(rt.local_set()); + let system = System::construct(sys_tx, sys_arbiter.clone()); + + system + .tx() + .send(SystemCommand::RegisterArbiter(usize::MAX, sys_arbiter)) + .unwrap(); // init background system arbiter let sys_ctrl = SystemController::new(sys_rx, stop_tx); @@ -201,8 +207,8 @@ impl Future for SystemController { Some(cmd) => match cmd { SystemCommand::Exit(code) => { // stop all arbiters - for wkr in self.arbiters.values() { - wkr.stop(); + for arb in self.arbiters.values() { + arb.stop(); } // stop event loop @@ -212,12 +218,12 @@ impl Future for SystemController { } } - SystemCommand::RegisterArbiter(name, hnd) => { - self.arbiters.insert(name, hnd); + SystemCommand::RegisterArbiter(id, arb) => { + self.arbiters.insert(id, arb); } - SystemCommand::DeregisterArbiter(name) => { - self.arbiters.remove(&name); + SystemCommand::DeregisterArbiter(id) => { + self.arbiters.remove(&id); } }, } diff --git a/actix-rt/tests/tests.rs b/actix-rt/tests/tests.rs index 7749ad0a..b2607a91 100644 --- a/actix-rt/tests/tests.rs +++ b/actix-rt/tests/tests.rs @@ -28,7 +28,7 @@ fn join_another_arbiter() { let arbiter = Arbiter::new(); arbiter.spawn(Box::pin(async move { tokio::time::sleep(time).await; - Arbiter::handle().stop(); + Arbiter::current().stop(); })); arbiter.join().unwrap(); }); @@ -43,7 +43,7 @@ fn join_another_arbiter() { arbiter.spawn_fn(move || { actix_rt::spawn(async move { tokio::time::sleep(time).await; - Arbiter::handle().stop(); + Arbiter::current().stop(); }); }); arbiter.join().unwrap(); @@ -58,7 +58,7 @@ fn join_another_arbiter() { let arbiter = Arbiter::new(); arbiter.spawn(Box::pin(async move { tokio::time::sleep(time).await; - Arbiter::handle().stop(); + Arbiter::current().stop(); })); arbiter.stop(); arbiter.join().unwrap(); @@ -190,7 +190,7 @@ fn system_arbiter_spawn() { thread::spawn(|| { // this thread will have no arbiter in it's thread local so call will panic - Arbiter::handle(); + Arbiter::current(); }) .join() .unwrap_err(); @@ -200,8 +200,8 @@ fn system_arbiter_spawn() { System::set_current(sys); let sys = System::current(); - let wrk = sys.arbiter(); - wrk.spawn(async move { + let arb = sys.arbiter(); + arb.spawn(async move { tx.send(42u32).unwrap(); System::current().stop(); }); @@ -210,3 +210,22 @@ fn system_arbiter_spawn() { assert_eq!(runner.block_on(rx).unwrap(), 42); thread.join().unwrap(); } + +#[test] +fn system_stop_stops_arbiters() { + let sys = System::new(); + let arb = Arbiter::new(); + + // arbiter should be alive to receive spawn msg + assert!(Arbiter::current().spawn_fn(|| {})); + assert!(arb.spawn_fn(|| {})); + + System::current().stop(); + sys.run().unwrap(); + + // arbiter should be dead and return false + assert!(!Arbiter::current().spawn_fn(|| {})); + assert!(!arb.spawn_fn(|| {})); + + arb.join().unwrap(); +} diff --git a/actix-server/src/worker.rs b/actix-server/src/worker.rs index 9c61e4ad..25a0429c 100644 --- a/actix-server/src/worker.rs +++ b/actix-server/src/worker.rs @@ -215,7 +215,7 @@ impl ServerWorker { } Err(e) => { error!("Can not start worker: {:?}", e); - Arbiter::handle().stop(); + Arbiter::current().stop(); } } wrk.await @@ -386,7 +386,7 @@ impl Future for ServerWorker { let num = num_connections(); if num == 0 { let _ = tx.take().unwrap().send(true); - Arbiter::handle().stop(); + Arbiter::current().stop(); return Poll::Ready(()); } @@ -394,7 +394,7 @@ impl Future for ServerWorker { if Pin::new(t2).poll(cx).is_ready() { let _ = tx.take().unwrap().send(false); self.shutdown(true); - Arbiter::handle().stop(); + Arbiter::current().stop(); return Poll::Ready(()); } From 3e6f69885cbcca4d9516d50f958c95a237e75ce8 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sun, 31 Jan 2021 04:43:35 +0000 Subject: [PATCH 52/73] remove arbiter TLS item storage --- actix-rt/src/arbiter.rs | 61 ----------------------------------------- actix-rt/tests/tests.rs | 29 -------------------- 2 files changed, 90 deletions(-) diff --git a/actix-rt/src/arbiter.rs b/actix-rt/src/arbiter.rs index 44e34b5d..a3cb5272 100644 --- a/actix-rt/src/arbiter.rs +++ b/actix-rt/src/arbiter.rs @@ -1,7 +1,5 @@ use std::{ - any::{Any, TypeId}, cell::RefCell, - collections::HashMap, fmt, future::Future, pin::Pin, @@ -22,7 +20,6 @@ pub(crate) static COUNT: AtomicUsize = AtomicUsize::new(0); thread_local!( static HANDLE: RefCell> = RefCell::new(None); - static STORAGE: RefCell>> = RefCell::new(HashMap::new()); ); pub(crate) enum ArbiterCommand { @@ -121,7 +118,6 @@ impl Arbiter { System::set_current(sys); - STORAGE.with(|cell| cell.borrow_mut().clear()); HANDLE.with(|cell| *cell.borrow_mut() = Some(hnd.clone())); // register arbiter @@ -156,7 +152,6 @@ impl Arbiter { let hnd = ArbiterHandle::new(tx); HANDLE.with(|cell| *cell.borrow_mut() = Some(hnd.clone())); - STORAGE.with(|cell| cell.borrow_mut().clear()); local.spawn_local(ArbiterRunner { rx }); @@ -214,54 +209,6 @@ impl Arbiter { pub fn join(self) -> thread::Result<()> { self.thread_handle.join() } - - /// Insert item into Arbiter's thread-local storage. - /// - /// Overwrites any item of the same type previously inserted. - pub fn set_item(item: T) { - STORAGE.with(move |cell| cell.borrow_mut().insert(TypeId::of::(), Box::new(item))); - } - - /// Check if Arbiter's thread-local storage contains an item type. - pub fn contains_item() -> bool { - STORAGE.with(move |cell| cell.borrow().contains_key(&TypeId::of::())) - } - - /// Call a function with a shared reference to an item in this Arbiter's thread-local storage. - /// - /// # Panics - /// Panics if item is not in Arbiter's thread-local item storage. - pub fn get_item(mut f: F) -> R - where - F: FnMut(&T) -> R, - { - STORAGE.with(move |cell| { - let st = cell.borrow(); - - let type_id = TypeId::of::(); - let item = st.get(&type_id).and_then(downcast_ref).unwrap(); - - f(item) - }) - } - - /// Call a function with a mutable reference to an item in this Arbiter's thread-local storage. - /// - /// # Panics - /// Panics if item is not in Arbiter's thread-local item storage. - pub fn get_mut_item(mut f: F) -> R - where - F: FnMut(&mut T) -> R, - { - STORAGE.with(move |cell| { - let mut st = cell.borrow_mut(); - - let type_id = TypeId::of::(); - let item = st.get_mut(&type_id).and_then(downcast_mut).unwrap(); - - f(item) - }) - } } /// A persistent future that processes [Arbiter] commands. @@ -292,11 +239,3 @@ impl Future for ArbiterRunner { } } } - -fn downcast_ref(boxed: &Box) -> Option<&T> { - boxed.downcast_ref() -} - -fn downcast_mut(boxed: &mut Box) -> Option<&mut T> { - boxed.downcast_mut() -} diff --git a/actix-rt/tests/tests.rs b/actix-rt/tests/tests.rs index b2607a91..f54e9909 100644 --- a/actix-rt/tests/tests.rs +++ b/actix-rt/tests/tests.rs @@ -140,35 +140,6 @@ fn arbiter_drop_no_panic_fut() { arbiter.join().unwrap(); } -#[test] -fn arbiter_item_storage() { - let _ = System::new(); - - let arbiter = Arbiter::new(); - - assert!(!Arbiter::contains_item::()); - Arbiter::set_item(42u32); - assert!(Arbiter::contains_item::()); - - Arbiter::get_item(|&item: &u32| assert_eq!(item, 42)); - Arbiter::get_mut_item(|&mut item: &mut u32| assert_eq!(item, 42)); - - let thread = thread::spawn(move || { - Arbiter::get_item(|&_item: &u32| unreachable!("u32 not in this thread")); - }) - .join(); - assert!(thread.is_err()); - - let thread = thread::spawn(move || { - Arbiter::get_mut_item(|&mut _item: &mut i8| unreachable!("i8 not in this thread")); - }) - .join(); - assert!(thread.is_err()); - - arbiter.stop(); - arbiter.join().unwrap(); -} - #[test] #[should_panic] fn no_system_current_panic() { From 0eb68d1c7b3f3337d9aee2fe0360c17f881515e8 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sun, 31 Jan 2021 04:45:27 +0000 Subject: [PATCH 53/73] Revert "remove arbiter TLS item storage" This reverts commit 3e6f69885cbcca4d9516d50f958c95a237e75ce8. --- actix-rt/src/arbiter.rs | 61 +++++++++++++++++++++++++++++++++++++++++ actix-rt/tests/tests.rs | 29 ++++++++++++++++++++ 2 files changed, 90 insertions(+) diff --git a/actix-rt/src/arbiter.rs b/actix-rt/src/arbiter.rs index a3cb5272..44e34b5d 100644 --- a/actix-rt/src/arbiter.rs +++ b/actix-rt/src/arbiter.rs @@ -1,5 +1,7 @@ use std::{ + any::{Any, TypeId}, cell::RefCell, + collections::HashMap, fmt, future::Future, pin::Pin, @@ -20,6 +22,7 @@ pub(crate) static COUNT: AtomicUsize = AtomicUsize::new(0); thread_local!( static HANDLE: RefCell> = RefCell::new(None); + static STORAGE: RefCell>> = RefCell::new(HashMap::new()); ); pub(crate) enum ArbiterCommand { @@ -118,6 +121,7 @@ impl Arbiter { System::set_current(sys); + STORAGE.with(|cell| cell.borrow_mut().clear()); HANDLE.with(|cell| *cell.borrow_mut() = Some(hnd.clone())); // register arbiter @@ -152,6 +156,7 @@ impl Arbiter { let hnd = ArbiterHandle::new(tx); HANDLE.with(|cell| *cell.borrow_mut() = Some(hnd.clone())); + STORAGE.with(|cell| cell.borrow_mut().clear()); local.spawn_local(ArbiterRunner { rx }); @@ -209,6 +214,54 @@ impl Arbiter { pub fn join(self) -> thread::Result<()> { self.thread_handle.join() } + + /// Insert item into Arbiter's thread-local storage. + /// + /// Overwrites any item of the same type previously inserted. + pub fn set_item(item: T) { + STORAGE.with(move |cell| cell.borrow_mut().insert(TypeId::of::(), Box::new(item))); + } + + /// Check if Arbiter's thread-local storage contains an item type. + pub fn contains_item() -> bool { + STORAGE.with(move |cell| cell.borrow().contains_key(&TypeId::of::())) + } + + /// Call a function with a shared reference to an item in this Arbiter's thread-local storage. + /// + /// # Panics + /// Panics if item is not in Arbiter's thread-local item storage. + pub fn get_item(mut f: F) -> R + where + F: FnMut(&T) -> R, + { + STORAGE.with(move |cell| { + let st = cell.borrow(); + + let type_id = TypeId::of::(); + let item = st.get(&type_id).and_then(downcast_ref).unwrap(); + + f(item) + }) + } + + /// Call a function with a mutable reference to an item in this Arbiter's thread-local storage. + /// + /// # Panics + /// Panics if item is not in Arbiter's thread-local item storage. + pub fn get_mut_item(mut f: F) -> R + where + F: FnMut(&mut T) -> R, + { + STORAGE.with(move |cell| { + let mut st = cell.borrow_mut(); + + let type_id = TypeId::of::(); + let item = st.get_mut(&type_id).and_then(downcast_mut).unwrap(); + + f(item) + }) + } } /// A persistent future that processes [Arbiter] commands. @@ -239,3 +292,11 @@ impl Future for ArbiterRunner { } } } + +fn downcast_ref(boxed: &Box) -> Option<&T> { + boxed.downcast_ref() +} + +fn downcast_mut(boxed: &mut Box) -> Option<&mut T> { + boxed.downcast_mut() +} diff --git a/actix-rt/tests/tests.rs b/actix-rt/tests/tests.rs index f54e9909..b2607a91 100644 --- a/actix-rt/tests/tests.rs +++ b/actix-rt/tests/tests.rs @@ -140,6 +140,35 @@ fn arbiter_drop_no_panic_fut() { arbiter.join().unwrap(); } +#[test] +fn arbiter_item_storage() { + let _ = System::new(); + + let arbiter = Arbiter::new(); + + assert!(!Arbiter::contains_item::()); + Arbiter::set_item(42u32); + assert!(Arbiter::contains_item::()); + + Arbiter::get_item(|&item: &u32| assert_eq!(item, 42)); + Arbiter::get_mut_item(|&mut item: &mut u32| assert_eq!(item, 42)); + + let thread = thread::spawn(move || { + Arbiter::get_item(|&_item: &u32| unreachable!("u32 not in this thread")); + }) + .join(); + assert!(thread.is_err()); + + let thread = thread::spawn(move || { + Arbiter::get_mut_item(|&mut _item: &mut i8| unreachable!("i8 not in this thread")); + }) + .join(); + assert!(thread.is_err()); + + arbiter.stop(); + arbiter.join().unwrap(); +} + #[test] #[should_panic] fn no_system_current_panic() { From 0b656f51e133850bbba8b96c4cae535619f9604e Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sun, 31 Jan 2021 04:48:03 +0000 Subject: [PATCH 54/73] deprecate rt TLS item storage --- actix-rt/src/arbiter.rs | 4 ++++ actix-rt/tests/tests.rs | 1 + 2 files changed, 5 insertions(+) diff --git a/actix-rt/src/arbiter.rs b/actix-rt/src/arbiter.rs index 44e34b5d..72e2d3e3 100644 --- a/actix-rt/src/arbiter.rs +++ b/actix-rt/src/arbiter.rs @@ -218,11 +218,13 @@ impl Arbiter { /// Insert item into Arbiter's thread-local storage. /// /// Overwrites any item of the same type previously inserted. + #[deprecated = "Will be removed in stable v2."] pub fn set_item(item: T) { STORAGE.with(move |cell| cell.borrow_mut().insert(TypeId::of::(), Box::new(item))); } /// Check if Arbiter's thread-local storage contains an item type. + #[deprecated = "Will be removed in stable v2."] pub fn contains_item() -> bool { STORAGE.with(move |cell| cell.borrow().contains_key(&TypeId::of::())) } @@ -231,6 +233,7 @@ impl Arbiter { /// /// # Panics /// Panics if item is not in Arbiter's thread-local item storage. + #[deprecated = "Will be removed in stable v2."] pub fn get_item(mut f: F) -> R where F: FnMut(&T) -> R, @@ -249,6 +252,7 @@ impl Arbiter { /// /// # Panics /// Panics if item is not in Arbiter's thread-local item storage. + #[deprecated = "Will be removed in stable v2."] pub fn get_mut_item(mut f: F) -> R where F: FnMut(&mut T) -> R, diff --git a/actix-rt/tests/tests.rs b/actix-rt/tests/tests.rs index b2607a91..56ac2017 100644 --- a/actix-rt/tests/tests.rs +++ b/actix-rt/tests/tests.rs @@ -141,6 +141,7 @@ fn arbiter_drop_no_panic_fut() { } #[test] +#[allow(deprecated)] fn arbiter_item_storage() { let _ = System::new(); From 057e7cd7c95513db7cc1e33f3fe34fb93fc75775 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sun, 31 Jan 2021 05:19:30 +0000 Subject: [PATCH 55/73] prepare rt v2.0.0-beta.3 --- actix-rt/CHANGES.md | 3 +++ actix-rt/Cargo.toml | 7 +++++-- actix-rt/README.md | 7 +++++++ actix-rt/src/lib.rs | 33 +++++++++++++++++++++++++++++++++ 4 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 actix-rt/README.md diff --git a/actix-rt/CHANGES.md b/actix-rt/CHANGES.md index a18c57d8..d3b74137 100644 --- a/actix-rt/CHANGES.md +++ b/actix-rt/CHANGES.md @@ -1,6 +1,9 @@ # Changes ## Unreleased - 2021-xx-xx + + +## 2.0.0-beta.3 - 2021-01-31 * Remove `run_in_tokio`, `attach_to_tokio` and `AsyncSystemRunner`. [#253] * Return `JoinHandle` from `actix_rt::spawn`. [#253] * Remove old `Arbiter::spawn`. Implementation is now inlined into `actix_rt::spawn`. [#253] diff --git a/actix-rt/Cargo.toml b/actix-rt/Cargo.toml index 68ed8563..65877abd 100644 --- a/actix-rt/Cargo.toml +++ b/actix-rt/Cargo.toml @@ -1,7 +1,10 @@ [package] name = "actix-rt" -version = "2.0.0-beta.2" -authors = ["Nikolay Kim "] +version = "2.0.0-beta.3" +authors = [ + "Nikolay Kim ", + "Rob Ede ", +] description = "Tokio-based single-thread async runtime for the Actix ecosystem" keywords = ["network", "framework", "async", "futures"] homepage = "https://actix.rs" diff --git a/actix-rt/README.md b/actix-rt/README.md new file mode 100644 index 00000000..f58c2f4f --- /dev/null +++ b/actix-rt/README.md @@ -0,0 +1,7 @@ +# actix-rt + +> Tokio-based single-thread async runtime for the Actix ecosystem. + +See documentation for detailed explanations these components: [https://docs.rs/actix-rt][docs]. + +[docs]: https://docs.rs/actix-rt diff --git a/actix-rt/src/lib.rs b/actix-rt/src/lib.rs index bf8a4796..09d155e5 100644 --- a/actix-rt/src/lib.rs +++ b/actix-rt/src/lib.rs @@ -1,4 +1,37 @@ //! Tokio-based single-thread async runtime for the Actix ecosystem. +//! +//! In most parts of the the Actix ecosystem, it has been chosen to use !Send futures. For this +//! reason, a single-threaded runtime is appropriate since it is guaranteed that futures will not +//! be moved between threads. This can result in small performance improvements over cases where +//! atomics would otherwise be needed. +//! +//! To achieve similar performance to multi-threaded, work-stealing runtimes, applications +//! using `actix-rt` will create multiple, mostly disconnected, single-threaded runtimes. +//! This approach has good performance characteristics for workloads where the majority of tasks +//! have similar runtime expense. +//! +//! The disadvantage is that idle threads will not steal work from very busy, stuck or otherwise +//! backlogged threads. Tasks that are disproportionately expensive should be offloaded to the +//! blocking thread-pool using [`task::spawn_blocking`]. +//! +//! # Examples +//! ``` +//! use std::sync::mpsc; +//! use actix_rt::{Arbiter, System}; +//! +//! let _ = System::new(); +//! +//! let (tx, rx) = mpsc::channel::(); +//! +//! let arbiter = Arbiter::new(); +//! arbiter.spawn_fn(move || tx.send(42).unwrap()); +//! +//! let num = rx.recv().unwrap(); +//! assert_eq!(num, 42); +//! +//! arbiter.stop(); +//! arbiter.join().unwrap(); +//! ``` #![deny(rust_2018_idioms, nonstandard_style)] #![allow(clippy::type_complexity)] From 66bd5bf4a2518bb0d0a5a3e9503c1091b9d086bc Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Tue, 2 Feb 2021 02:07:58 +0000 Subject: [PATCH 56/73] prepare macros v0.2.0 release (#261) --- actix-codec/Cargo.toml | 2 +- actix-macros/CHANGES.md | 10 ++- actix-macros/Cargo.toml | 13 ++-- actix-macros/src/lib.rs | 62 ++++++++++--------- actix-macros/tests/trybuild.rs | 2 + .../tests/trybuild/main-02-only-async.stderr | 2 +- .../tests/trybuild/main-03-fn-params.rs | 6 ++ .../tests/trybuild/test-03-only-async.rs | 6 ++ .../tests/trybuild/test-03-only-async.stderr | 5 ++ actix-router/Cargo.toml | 2 +- actix-rt/Cargo.toml | 6 +- actix-rt/README.md | 2 +- actix-rt/src/lib.rs | 2 +- actix-server/Cargo.toml | 4 +- actix-service/Cargo.toml | 2 +- actix-tls/Cargo.toml | 6 +- actix-tracing/Cargo.toml | 4 +- actix-utils/Cargo.toml | 4 +- bytestring/Cargo.toml | 2 +- 19 files changed, 85 insertions(+), 57 deletions(-) create mode 100644 actix-macros/tests/trybuild/main-03-fn-params.rs create mode 100644 actix-macros/tests/trybuild/test-03-only-async.rs create mode 100644 actix-macros/tests/trybuild/test-03-only-async.stderr diff --git a/actix-codec/Cargo.toml b/actix-codec/Cargo.toml index d0f6646d..95a24764 100644 --- a/actix-codec/Cargo.toml +++ b/actix-codec/Cargo.toml @@ -6,7 +6,7 @@ description = "Codec utilities for working with framed protocols" keywords = ["network", "framework", "async", "futures"] homepage = "https://actix.rs" repository = "https://github.com/actix/actix-net.git" -documentation = "https://docs.rs/actix-codec/" +documentation = "https://docs.rs/actix-codec" categories = ["network-programming", "asynchronous"] license = "MIT OR Apache-2.0" edition = "2018" diff --git a/actix-macros/CHANGES.md b/actix-macros/CHANGES.md index 065d8e30..e5245775 100644 --- a/actix-macros/CHANGES.md +++ b/actix-macros/CHANGES.md @@ -3,8 +3,16 @@ ## Unreleased - 2021-xx-xx +## 0.2.0 - 2021-02-02 +* Update to latest `actix_rt::System::new` signature. [#261] + +[#261]: https://github.com/actix/actix-net/pull/261 + + ## 0.2.0-beta.1 - 2021-01-09 -* Remove `actix-reexport` feature. +* Remove `actix-reexport` feature. [#218] + +[#218]: https://github.com/actix/actix-net/pull/218 ## 0.1.3 - 2020-12-03 diff --git a/actix-macros/Cargo.toml b/actix-macros/Cargo.toml index 7392eb12..5da49b16 100644 --- a/actix-macros/Cargo.toml +++ b/actix-macros/Cargo.toml @@ -1,10 +1,10 @@ [package] name = "actix-macros" -version = "0.2.0-beta.1" +version = "0.2.0" authors = ["Nikolay Kim "] -description = "Actix runtime macros" -repository = "https://github.com/actix/actix-net" -documentation = "https://docs.rs/actix-macros/" +description = "Macros for Actix system and runtime" +repository = "https://github.com/actix/actix-net.git" +documentation = "https://docs.rs/actix-macros" categories = ["network-programming", "asynchronous"] license = "MIT OR Apache-2.0" edition = "2018" @@ -16,11 +16,8 @@ proc-macro = true quote = "1.0.3" syn = { version = "^1", features = ["full"] } -[features] -actix-reexport = [] - [dev-dependencies] -actix-rt = "2.0.0-beta.2" +actix-rt = "2.0.0-beta.3" futures-util = { version = "0.3", default-features = false } trybuild = "1" diff --git a/actix-macros/src/lib.rs b/actix-macros/src/lib.rs index 54b89565..0d634a59 100644 --- a/actix-macros/src/lib.rs +++ b/actix-macros/src/lib.rs @@ -1,4 +1,12 @@ -//! Macros for use with Tokio +//! Macros for Actix system and runtime. +//! +//! The [`actix-rt`](https://docs.rs/actix-rt) crate must be available for macro output to compile. +//! +//! # Entry-point +//! See docs for the [`#[main]`](macro@main) macro. +//! +//! # Tests +//! See docs for the [`#[test]`](macro@test) macro. #![deny(rust_2018_idioms, nonstandard_style)] #![doc(html_logo_url = "https://actix.rs/img/logo.png")] @@ -7,10 +15,9 @@ use proc_macro::TokenStream; use quote::quote; -/// Marks async function to be executed by Actix system. -/// -/// ## Usage +/// Marks async entry-point function to be executed by Actix system. /// +/// # Examples /// ``` /// #[actix_rt::main] /// async fn main() { @@ -28,9 +35,12 @@ pub fn main(_: TokenStream, item: TokenStream) -> TokenStream { let body = &input.block; if sig.asyncness.is_none() { - return syn::Error::new_spanned(sig.fn_token, "only async fn is supported") - .to_compile_error() - .into(); + return syn::Error::new_spanned( + sig.fn_token, + "the async keyword is missing from the function declaration", + ) + .to_compile_error() + .into(); } sig.asyncness = None; @@ -45,11 +55,10 @@ pub fn main(_: TokenStream, item: TokenStream) -> TokenStream { .into() } -/// Marks async test function to be executed by Actix system. +/// Marks async test function to be executed in an Actix system. /// -/// ## Usage -/// -/// ```no_run +/// # Examples +/// ``` /// #[actix_rt::test] /// async fn my_test() { /// assert!(true); @@ -73,7 +82,7 @@ pub fn test(_: TokenStream, item: TokenStream) -> TokenStream { if sig.asyncness.is_none() { return syn::Error::new_spanned( input.sig.fn_token, - format!("only async fn is supported, {}", input.sig.ident), + "the async keyword is missing from the function declaration", ) .to_compile_error() .into(); @@ -81,24 +90,19 @@ pub fn test(_: TokenStream, item: TokenStream) -> TokenStream { sig.asyncness = None; - let result = if has_test_attr { - quote! { - #(#attrs)* - #vis #sig { - actix_rt::System::new() - .block_on(async { #body }) - } - } + let missing_test_attr = if has_test_attr { + quote!() } else { - quote! { - #[test] - #(#attrs)* - #vis #sig { - actix_rt::System::new() - .block_on(async { #body }) - } - } + quote!(#[test]) }; - result.into() + (quote! { + #missing_test_attr + #(#attrs)* + #vis #sig { + actix_rt::System::new() + .block_on(async { #body }) + } + }) + .into() } diff --git a/actix-macros/tests/trybuild.rs b/actix-macros/tests/trybuild.rs index d944e3a2..a726d3bd 100644 --- a/actix-macros/tests/trybuild.rs +++ b/actix-macros/tests/trybuild.rs @@ -3,7 +3,9 @@ fn compile_macros() { let t = trybuild::TestCases::new(); t.pass("tests/trybuild/main-01-basic.rs"); t.compile_fail("tests/trybuild/main-02-only-async.rs"); + t.pass("tests/trybuild/main-03-fn-params.rs"); t.pass("tests/trybuild/test-01-basic.rs"); t.pass("tests/trybuild/test-02-keep-attrs.rs"); + t.compile_fail("tests/trybuild/test-03-only-async.rs"); } diff --git a/actix-macros/tests/trybuild/main-02-only-async.stderr b/actix-macros/tests/trybuild/main-02-only-async.stderr index e5e44492..fc060071 100644 --- a/actix-macros/tests/trybuild/main-02-only-async.stderr +++ b/actix-macros/tests/trybuild/main-02-only-async.stderr @@ -1,4 +1,4 @@ -error: only async fn is supported +error: the async keyword is missing from the function declaration --> $DIR/main-02-only-async.rs:2:1 | 2 | fn main() { diff --git a/actix-macros/tests/trybuild/main-03-fn-params.rs b/actix-macros/tests/trybuild/main-03-fn-params.rs new file mode 100644 index 00000000..3f83b4b6 --- /dev/null +++ b/actix-macros/tests/trybuild/main-03-fn-params.rs @@ -0,0 +1,6 @@ +#[actix_rt::main] +async fn main2(_param: bool) { + futures_util::future::ready(()).await +} + +fn main() {} diff --git a/actix-macros/tests/trybuild/test-03-only-async.rs b/actix-macros/tests/trybuild/test-03-only-async.rs new file mode 100644 index 00000000..436ff460 --- /dev/null +++ b/actix-macros/tests/trybuild/test-03-only-async.rs @@ -0,0 +1,6 @@ +#[actix_rt::test] +fn my_test() { + futures_util::future::ready(()).await +} + +fn main() {} diff --git a/actix-macros/tests/trybuild/test-03-only-async.stderr b/actix-macros/tests/trybuild/test-03-only-async.stderr new file mode 100644 index 00000000..4d8190a5 --- /dev/null +++ b/actix-macros/tests/trybuild/test-03-only-async.stderr @@ -0,0 +1,5 @@ +error: the async keyword is missing from the function declaration + --> $DIR/test-03-only-async.rs:2:1 + | +2 | fn my_test() { + | ^^ diff --git a/actix-router/Cargo.toml b/actix-router/Cargo.toml index 4277cabf..2446fc1b 100644 --- a/actix-router/Cargo.toml +++ b/actix-router/Cargo.toml @@ -6,7 +6,7 @@ description = "Resource path matching library" keywords = ["actix"] homepage = "https://actix.rs" repository = "https://github.com/actix/actix-net.git" -documentation = "https://docs.rs/actix-router/" +documentation = "https://docs.rs/actix-router" license = "MIT OR Apache-2.0" edition = "2018" diff --git a/actix-rt/Cargo.toml b/actix-rt/Cargo.toml index 65877abd..79513580 100644 --- a/actix-rt/Cargo.toml +++ b/actix-rt/Cargo.toml @@ -5,11 +5,11 @@ authors = [ "Nikolay Kim ", "Rob Ede ", ] -description = "Tokio-based single-thread async runtime for the Actix ecosystem" +description = "Tokio-based single-threaded async runtime for the Actix ecosystem" keywords = ["network", "framework", "async", "futures"] homepage = "https://actix.rs" repository = "https://github.com/actix/actix-net.git" -documentation = "https://docs.rs/actix-rt/" +documentation = "https://docs.rs/actix-rt" categories = ["network-programming", "asynchronous"] license = "MIT OR Apache-2.0" edition = "2018" @@ -23,7 +23,7 @@ default = ["macros"] macros = ["actix-macros"] [dependencies] -actix-macros = { version = "0.2.0-beta.1", optional = true } +actix-macros = { version = "0.2.0", optional = true } futures-core = { version = "0.3", default-features = false } tokio = { version = "1", features = ["rt", "net", "parking_lot", "signal", "sync", "time"] } diff --git a/actix-rt/README.md b/actix-rt/README.md index f58c2f4f..7027b91a 100644 --- a/actix-rt/README.md +++ b/actix-rt/README.md @@ -1,6 +1,6 @@ # actix-rt -> Tokio-based single-thread async runtime for the Actix ecosystem. +> Tokio-based single-threaded async runtime for the Actix ecosystem. See documentation for detailed explanations these components: [https://docs.rs/actix-rt][docs]. diff --git a/actix-rt/src/lib.rs b/actix-rt/src/lib.rs index 09d155e5..9c846203 100644 --- a/actix-rt/src/lib.rs +++ b/actix-rt/src/lib.rs @@ -1,4 +1,4 @@ -//! Tokio-based single-thread async runtime for the Actix ecosystem. +//! Tokio-based single-threaded async runtime for the Actix ecosystem. //! //! In most parts of the the Actix ecosystem, it has been chosen to use !Send futures. For this //! reason, a single-threaded runtime is appropriate since it is guaranteed that futures will not diff --git a/actix-server/Cargo.toml b/actix-server/Cargo.toml index c366124a..a5f112b4 100755 --- a/actix-server/Cargo.toml +++ b/actix-server/Cargo.toml @@ -9,7 +9,7 @@ description = "General purpose TCP server built for the Actix ecosystem" keywords = ["network", "framework", "async", "futures"] homepage = "https://actix.rs" repository = "https://github.com/actix/actix-net.git" -documentation = "https://docs.rs/actix-server/" +documentation = "https://docs.rs/actix-server" categories = ["network-programming", "asynchronous"] license = "MIT OR Apache-2.0" exclude = [".gitignore", ".cargo/config"] @@ -24,7 +24,7 @@ default = [] [dependencies] actix-codec = "0.4.0-beta.1" -actix-rt = { version = "2.0.0-beta.2", default-features = false } +actix-rt = { version = "2.0.0-beta.3", default-features = false } actix-service = "2.0.0-beta.3" actix-utils = "3.0.0-beta.1" diff --git a/actix-service/Cargo.toml b/actix-service/Cargo.toml index e6cb1250..f83939f2 100644 --- a/actix-service/Cargo.toml +++ b/actix-service/Cargo.toml @@ -24,5 +24,5 @@ futures-core = { version = "0.3.7", default-features = false } pin-project-lite = "0.2" [dev-dependencies] -actix-rt = "2.0.0-beta.2" +actix-rt = "2.0.0-beta.3" futures-util = { version = "0.3.7", default-features = false } diff --git a/actix-tls/Cargo.toml b/actix-tls/Cargo.toml index c31cded0..8c13a2fd 100755 --- a/actix-tls/Cargo.toml +++ b/actix-tls/Cargo.toml @@ -6,7 +6,7 @@ description = "TLS acceptor and connector services for Actix ecosystem" keywords = ["network", "tls", "ssl", "async", "transport"] homepage = "https://actix.rs" repository = "https://github.com/actix/actix-net.git" -documentation = "https://docs.rs/actix-tls/" +documentation = "https://docs.rs/actix-tls" categories = ["network-programming", "asynchronous"] license = "MIT OR Apache-2.0" edition = "2018" @@ -41,7 +41,7 @@ uri = ["http"] [dependencies] actix-codec = "0.4.0-beta.1" -actix-rt = { version = "2.0.0-beta.2", default-features = false } +actix-rt = { version = "2.0.0-beta.3", default-features = false } actix-service = "2.0.0-beta.3" actix-utils = "3.0.0-beta.1" @@ -67,7 +67,7 @@ tls-native-tls = { package = "native-tls", version = "0.2", optional = true } tokio-native-tls = { version = "0.3", optional = true } [dev-dependencies] -actix-rt = "2.0.0-beta.2" +actix-rt = "2.0.0-beta.3" actix-server = "2.0.0-beta.2" bytes = "1" env_logger = "0.8" diff --git a/actix-tracing/Cargo.toml b/actix-tracing/Cargo.toml index 72f7b702..24888da3 100644 --- a/actix-tracing/Cargo.toml +++ b/actix-tracing/Cargo.toml @@ -6,7 +6,7 @@ description = "Support for tokio tracing with Actix services" keywords = ["network", "framework", "tracing"] homepage = "https://actix.rs" repository = "https://github.com/actix/actix-net.git" -documentation = "https://docs.rs/actix-tracing/" +documentation = "https://docs.rs/actix-tracing" categories = ["network-programming", "asynchronous"] license = "MIT OR Apache-2.0" edition = "2018" @@ -23,5 +23,5 @@ tracing = "0.1" tracing-futures = "0.2" [dev_dependencies] -actix-rt = "2.0.0-beta.2" +actix-rt = "2.0.0-beta.3" slab = "0.4" diff --git a/actix-utils/Cargo.toml b/actix-utils/Cargo.toml index c82cf79e..468cb8d7 100644 --- a/actix-utils/Cargo.toml +++ b/actix-utils/Cargo.toml @@ -6,7 +6,7 @@ description = "Various network related services and utilities for the Actix ecos keywords = ["network", "framework", "async", "futures"] homepage = "https://actix.rs" repository = "https://github.com/actix/actix-net.git" -documentation = "https://docs.rs/actix-utils/" +documentation = "https://docs.rs/actix-utils" categories = ["network-programming", "asynchronous"] license = "MIT OR Apache-2.0" edition = "2018" @@ -17,7 +17,7 @@ path = "src/lib.rs" [dependencies] actix-codec = "0.4.0-beta.1" -actix-rt = { version = "2.0.0-beta.2", default-features = false } +actix-rt = { version = "2.0.0-beta.3", default-features = false } actix-service = "2.0.0-beta.3" futures-core = { version = "0.3.7", default-features = false } diff --git a/bytestring/Cargo.toml b/bytestring/Cargo.toml index 6af76031..3e53417c 100644 --- a/bytestring/Cargo.toml +++ b/bytestring/Cargo.toml @@ -10,7 +10,7 @@ keywords = ["string", "bytes", "utf8", "web", "actix"] categories = ["no-std", "web-programming"] homepage = "https://actix.rs" repository = "https://github.com/actix/actix-net.git" -documentation = "https://docs.rs/bytestring/" +documentation = "https://docs.rs/bytestring" license = "MIT OR Apache-2.0" edition = "2018" From 4ec358575ed9b1a9636c8b83d349f6a5eeaddaa2 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Wed, 3 Feb 2021 10:25:31 +0000 Subject: [PATCH 57/73] prepare actix-rt v2.0.0 release (#262) --- actix-router/src/resource.rs | 1 + actix-rt/CHANGES.md | 7 +++ actix-rt/Cargo.toml | 5 +- actix-rt/examples/hyper.rs | 28 +++++++++++ actix-rt/src/arbiter.rs | 95 ++++++++-------------------------- actix-rt/src/lib.rs | 4 +- actix-rt/src/runtime.rs | 25 ++++++--- actix-rt/src/system.rs | 18 ++++++- actix-rt/tests/tests.rs | 98 ++++++++++++++++++++++++------------ actix-server/Cargo.toml | 2 +- actix-service/Cargo.toml | 2 +- actix-tls/Cargo.toml | 4 +- actix-tracing/Cargo.toml | 2 +- actix-utils/Cargo.toml | 2 +- 14 files changed, 172 insertions(+), 121 deletions(-) create mode 100644 actix-rt/examples/hyper.rs diff --git a/actix-router/src/resource.rs b/actix-router/src/resource.rs index 3808bc25..8dbef26c 100644 --- a/actix-router/src/resource.rs +++ b/actix-router/src/resource.rs @@ -28,6 +28,7 @@ enum PatternElement { } #[derive(Clone, Debug)] +#[allow(clippy::large_enum_variant)] enum PatternType { Static(String), Prefix(String), diff --git a/actix-rt/CHANGES.md b/actix-rt/CHANGES.md index d3b74137..15052613 100644 --- a/actix-rt/CHANGES.md +++ b/actix-rt/CHANGES.md @@ -3,6 +3,13 @@ ## Unreleased - 2021-xx-xx +## 2.0.0 - 2021-02-02 +* Remove all Arbiter-local storage methods. [#262] +* Re-export `tokio::pin`. [#262] + +[#262]: https://github.com/actix/actix-net/pull/262 + + ## 2.0.0-beta.3 - 2021-01-31 * Remove `run_in_tokio`, `attach_to_tokio` and `AsyncSystemRunner`. [#253] * Return `JoinHandle` from `actix_rt::spawn`. [#253] diff --git a/actix-rt/Cargo.toml b/actix-rt/Cargo.toml index 79513580..f8f3984d 100644 --- a/actix-rt/Cargo.toml +++ b/actix-rt/Cargo.toml @@ -1,12 +1,12 @@ [package] name = "actix-rt" -version = "2.0.0-beta.3" +version = "2.0.0" authors = [ "Nikolay Kim ", "Rob Ede ", ] description = "Tokio-based single-threaded async runtime for the Actix ecosystem" -keywords = ["network", "framework", "async", "futures"] +keywords = ["async", "futures", "io", "runtime"] homepage = "https://actix.rs" repository = "https://github.com/actix/actix-net.git" documentation = "https://docs.rs/actix-rt" @@ -30,3 +30,4 @@ tokio = { version = "1", features = ["rt", "net", "parking_lot", "signal", "sync [dev-dependencies] tokio = { version = "1", features = ["full"] } +hyper = { version = "0.14", default-features = false, features = ["server", "tcp", "http1"] } diff --git a/actix-rt/examples/hyper.rs b/actix-rt/examples/hyper.rs new file mode 100644 index 00000000..8bad1b33 --- /dev/null +++ b/actix-rt/examples/hyper.rs @@ -0,0 +1,28 @@ +use hyper::service::{make_service_fn, service_fn}; +use hyper::{Body, Request, Response, Server}; +use std::convert::Infallible; +use std::net::SocketAddr; + +async fn handle(_req: Request) -> Result, Infallible> { + Ok(Response::new(Body::from("Hello World"))) +} + +fn main() { + actix_rt::System::with_tokio_rt(|| { + tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build() + .unwrap() + }) + .block_on(async { + let make_service = + make_service_fn(|_conn| async { Ok::<_, Infallible>(service_fn(handle)) }); + + let server = + Server::bind(&SocketAddr::from(([127, 0, 0, 1], 3000))).serve(make_service); + + if let Err(e) = server.await { + eprintln!("server error: {}", e); + } + }) +} diff --git a/actix-rt/src/arbiter.rs b/actix-rt/src/arbiter.rs index 72e2d3e3..7eae662a 100644 --- a/actix-rt/src/arbiter.rs +++ b/actix-rt/src/arbiter.rs @@ -1,7 +1,5 @@ use std::{ - any::{Any, TypeId}, cell::RefCell, - collections::HashMap, fmt, future::Future, pin::Pin, @@ -14,7 +12,7 @@ use futures_core::ready; use tokio::{sync::mpsc, task::LocalSet}; use crate::{ - runtime::Runtime, + runtime::{default_tokio_runtime, Runtime}, system::{System, SystemCommand}, }; @@ -22,7 +20,6 @@ pub(crate) static COUNT: AtomicUsize = AtomicUsize::new(0); thread_local!( static HANDLE: RefCell> = RefCell::new(None); - static STORAGE: RefCell>> = RefCell::new(HashMap::new()); ); pub(crate) enum ArbiterCommand { @@ -97,16 +94,30 @@ pub struct Arbiter { } impl Arbiter { - /// Spawn new Arbiter thread and start its event loop. + /// Spawn a new Arbiter thread and start its event loop. /// /// # Panics /// Panics if a [System] is not registered on the current thread. #[allow(clippy::new_without_default)] pub fn new() -> Arbiter { - let id = COUNT.fetch_add(1, Ordering::Relaxed); - let system_id = System::current().id(); - let name = format!("actix-rt|system:{}|arbiter:{}", system_id, id); + Self::with_tokio_rt(|| { + default_tokio_runtime().expect("Cannot create new Arbiter's Runtime.") + }) + } + + /// Spawn a new Arbiter using the [Tokio Runtime](tokio-runtime) returned from a closure. + /// + /// [tokio-runtime]: tokio::runtime::Runtime + #[doc(hidden)] + pub fn with_tokio_rt(runtime_factory: F) -> Arbiter + where + F: Fn() -> tokio::runtime::Runtime + Send + 'static, + { let sys = System::current(); + let system_id = sys.id(); + let arb_id = COUNT.fetch_add(1, Ordering::Relaxed); + + let name = format!("actix-rt|system:{}|arbiter:{}", system_id, arb_id); let (tx, rx) = mpsc::unbounded_channel(); let (ready_tx, ready_rx) = std::sync::mpsc::channel::<()>(); @@ -116,18 +127,17 @@ impl Arbiter { .spawn({ let tx = tx.clone(); move || { - let rt = Runtime::new().expect("Cannot create new Arbiter's Runtime."); + let rt = Runtime::from(runtime_factory()); let hnd = ArbiterHandle::new(tx); System::set_current(sys); - STORAGE.with(|cell| cell.borrow_mut().clear()); HANDLE.with(|cell| *cell.borrow_mut() = Some(hnd.clone())); // register arbiter let _ = System::current() .tx() - .send(SystemCommand::RegisterArbiter(id, hnd)); + .send(SystemCommand::RegisterArbiter(arb_id, hnd)); ready_tx.send(()).unwrap(); @@ -137,7 +147,7 @@ impl Arbiter { // deregister arbiter let _ = System::current() .tx() - .send(SystemCommand::DeregisterArbiter(id)); + .send(SystemCommand::DeregisterArbiter(arb_id)); } }) .unwrap_or_else(|err| { @@ -156,7 +166,6 @@ impl Arbiter { let hnd = ArbiterHandle::new(tx); HANDLE.with(|cell| *cell.borrow_mut() = Some(hnd.clone())); - STORAGE.with(|cell| cell.borrow_mut().clear()); local.spawn_local(ArbiterRunner { rx }); @@ -214,58 +223,6 @@ impl Arbiter { pub fn join(self) -> thread::Result<()> { self.thread_handle.join() } - - /// Insert item into Arbiter's thread-local storage. - /// - /// Overwrites any item of the same type previously inserted. - #[deprecated = "Will be removed in stable v2."] - pub fn set_item(item: T) { - STORAGE.with(move |cell| cell.borrow_mut().insert(TypeId::of::(), Box::new(item))); - } - - /// Check if Arbiter's thread-local storage contains an item type. - #[deprecated = "Will be removed in stable v2."] - pub fn contains_item() -> bool { - STORAGE.with(move |cell| cell.borrow().contains_key(&TypeId::of::())) - } - - /// Call a function with a shared reference to an item in this Arbiter's thread-local storage. - /// - /// # Panics - /// Panics if item is not in Arbiter's thread-local item storage. - #[deprecated = "Will be removed in stable v2."] - pub fn get_item(mut f: F) -> R - where - F: FnMut(&T) -> R, - { - STORAGE.with(move |cell| { - let st = cell.borrow(); - - let type_id = TypeId::of::(); - let item = st.get(&type_id).and_then(downcast_ref).unwrap(); - - f(item) - }) - } - - /// Call a function with a mutable reference to an item in this Arbiter's thread-local storage. - /// - /// # Panics - /// Panics if item is not in Arbiter's thread-local item storage. - #[deprecated = "Will be removed in stable v2."] - pub fn get_mut_item(mut f: F) -> R - where - F: FnMut(&mut T) -> R, - { - STORAGE.with(move |cell| { - let mut st = cell.borrow_mut(); - - let type_id = TypeId::of::(); - let item = st.get_mut(&type_id).and_then(downcast_mut).unwrap(); - - f(item) - }) - } } /// A persistent future that processes [Arbiter] commands. @@ -296,11 +253,3 @@ impl Future for ArbiterRunner { } } } - -fn downcast_ref(boxed: &Box) -> Option<&T> { - boxed.downcast_ref() -} - -fn downcast_mut(boxed: &mut Box) -> Option<&mut T> { - boxed.downcast_mut() -} diff --git a/actix-rt/src/lib.rs b/actix-rt/src/lib.rs index 9c846203..831958aa 100644 --- a/actix-rt/src/lib.rs +++ b/actix-rt/src/lib.rs @@ -12,7 +12,7 @@ //! //! The disadvantage is that idle threads will not steal work from very busy, stuck or otherwise //! backlogged threads. Tasks that are disproportionately expensive should be offloaded to the -//! blocking thread-pool using [`task::spawn_blocking`]. +//! blocking task thread-pool using [`task::spawn_blocking`]. //! //! # Examples //! ``` @@ -56,6 +56,8 @@ pub use self::arbiter::{Arbiter, ArbiterHandle}; pub use self::runtime::Runtime; pub use self::system::{System, SystemRunner}; +pub use tokio::pin; + pub mod signal { //! Asynchronous signal handling (Tokio re-exports). diff --git a/actix-rt/src/runtime.rs b/actix-rt/src/runtime.rs index a20dfe7e..1adbf6c0 100644 --- a/actix-rt/src/runtime.rs +++ b/actix-rt/src/runtime.rs @@ -2,7 +2,7 @@ use std::{future::Future, io}; use tokio::task::{JoinHandle, LocalSet}; -/// A single-threaded runtime based on Tokio's "current thread" runtime. +/// A Tokio-based runtime proxy. /// /// All spawned futures will be executed on the current thread. Therefore, there is no `Send` bound /// on submitted futures. @@ -12,14 +12,18 @@ pub struct Runtime { rt: tokio::runtime::Runtime, } +pub(crate) fn default_tokio_runtime() -> io::Result { + tokio::runtime::Builder::new_current_thread() + .enable_io() + .enable_time() + .build() +} + impl Runtime { /// Returns a new runtime initialized with default configuration values. #[allow(clippy::new_ret_no_self)] - pub fn new() -> io::Result { - let rt = tokio::runtime::Builder::new_current_thread() - .enable_io() - .enable_time() - .build()?; + pub fn new() -> io::Result { + let rt = default_tokio_runtime()?; Ok(Runtime { rt, @@ -81,3 +85,12 @@ impl Runtime { self.local.block_on(&self.rt, f) } } + +impl From for Runtime { + fn from(rt: tokio::runtime::Runtime) -> Self { + Self { + local: LocalSet::new(), + rt, + } + } +} diff --git a/actix-rt/src/system.rs b/actix-rt/src/system.rs index 58fe3cab..b7f134cb 100644 --- a/actix-rt/src/system.rs +++ b/actix-rt/src/system.rs @@ -11,7 +11,7 @@ use std::{ use futures_core::ready; use tokio::sync::{mpsc, oneshot}; -use crate::{arbiter::ArbiterHandle, Arbiter, Runtime}; +use crate::{arbiter::ArbiterHandle, runtime::default_tokio_runtime, Arbiter, Runtime}; static SYSTEM_COUNT: AtomicUsize = AtomicUsize::new(0); @@ -36,10 +36,24 @@ impl System { /// Panics if underlying Tokio runtime can not be created. #[allow(clippy::new_ret_no_self)] pub fn new() -> SystemRunner { + Self::with_tokio_rt(|| { + default_tokio_runtime() + .expect("Default Actix (Tokio) runtime could not be created.") + }) + } + + /// Create a new System using the [Tokio Runtime](tokio-runtime) returned from a closure. + /// + /// [tokio-runtime]: tokio::runtime::Runtime + #[doc(hidden)] + pub fn with_tokio_rt(runtime_factory: F) -> SystemRunner + where + F: Fn() -> tokio::runtime::Runtime, + { let (stop_tx, stop_rx) = oneshot::channel(); let (sys_tx, sys_rx) = mpsc::unbounded_channel(); - let rt = Runtime::new().expect("Actix (Tokio) runtime could not be created."); + let rt = Runtime::from(runtime_factory()); let sys_arbiter = Arbiter::in_new_system(rt.local_set()); let system = System::construct(sys_tx, sys_arbiter.clone()); diff --git a/actix-rt/tests/tests.rs b/actix-rt/tests/tests.rs index 56ac2017..56b5e8a6 100644 --- a/actix-rt/tests/tests.rs +++ b/actix-rt/tests/tests.rs @@ -1,5 +1,9 @@ use std::{ - sync::mpsc::channel, + sync::{ + atomic::{AtomicBool, Ordering}, + mpsc::channel, + Arc, + }, thread, time::{Duration, Instant}, }; @@ -140,36 +144,6 @@ fn arbiter_drop_no_panic_fut() { arbiter.join().unwrap(); } -#[test] -#[allow(deprecated)] -fn arbiter_item_storage() { - let _ = System::new(); - - let arbiter = Arbiter::new(); - - assert!(!Arbiter::contains_item::()); - Arbiter::set_item(42u32); - assert!(Arbiter::contains_item::()); - - Arbiter::get_item(|&item: &u32| assert_eq!(item, 42)); - Arbiter::get_mut_item(|&mut item: &mut u32| assert_eq!(item, 42)); - - let thread = thread::spawn(move || { - Arbiter::get_item(|&_item: &u32| unreachable!("u32 not in this thread")); - }) - .join(); - assert!(thread.is_err()); - - let thread = thread::spawn(move || { - Arbiter::get_mut_item(|&mut _item: &mut i8| unreachable!("i8 not in this thread")); - }) - .join(); - assert!(thread.is_err()); - - arbiter.stop(); - arbiter.join().unwrap(); -} - #[test] #[should_panic] fn no_system_current_panic() { @@ -224,9 +198,71 @@ fn system_stop_stops_arbiters() { System::current().stop(); sys.run().unwrap(); + // account for slightly slow thread de-spawns (only observed on windows) + thread::sleep(Duration::from_millis(100)); + // arbiter should be dead and return false assert!(!Arbiter::current().spawn_fn(|| {})); assert!(!arb.spawn_fn(|| {})); arb.join().unwrap(); } + +#[test] +fn new_system_with_tokio() { + let (tx, rx) = channel(); + + let res = System::with_tokio_rt(move || { + tokio::runtime::Builder::new_multi_thread() + .enable_io() + .enable_time() + .thread_keep_alive(Duration::from_millis(1000)) + .worker_threads(2) + .max_blocking_threads(2) + .on_thread_start(|| {}) + .on_thread_stop(|| {}) + .build() + .unwrap() + }) + .block_on(async { + actix_rt::time::sleep(Duration::from_millis(1)).await; + + tokio::task::spawn(async move { + tx.send(42).unwrap(); + }) + .await + .unwrap(); + + 123usize + }); + + assert_eq!(res, 123); + assert_eq!(rx.recv().unwrap(), 42); +} + +#[test] +fn new_arbiter_with_tokio() { + let _ = System::new(); + + let arb = Arbiter::with_tokio_rt(|| { + tokio::runtime::Builder::new_current_thread() + .enable_all() + .build() + .unwrap() + }); + + let counter = Arc::new(AtomicBool::new(true)); + + let counter1 = counter.clone(); + let did_spawn = arb.spawn(async move { + actix_rt::time::sleep(Duration::from_millis(1)).await; + counter1.store(false, Ordering::SeqCst); + Arbiter::current().stop(); + }); + + assert!(did_spawn); + + arb.join().unwrap(); + + assert_eq!(false, counter.load(Ordering::SeqCst)); +} diff --git a/actix-server/Cargo.toml b/actix-server/Cargo.toml index a5f112b4..845dc03e 100755 --- a/actix-server/Cargo.toml +++ b/actix-server/Cargo.toml @@ -24,7 +24,7 @@ default = [] [dependencies] actix-codec = "0.4.0-beta.1" -actix-rt = { version = "2.0.0-beta.3", default-features = false } +actix-rt = { version = "2.0.0", default-features = false } actix-service = "2.0.0-beta.3" actix-utils = "3.0.0-beta.1" diff --git a/actix-service/Cargo.toml b/actix-service/Cargo.toml index f83939f2..ce18ba66 100644 --- a/actix-service/Cargo.toml +++ b/actix-service/Cargo.toml @@ -24,5 +24,5 @@ futures-core = { version = "0.3.7", default-features = false } pin-project-lite = "0.2" [dev-dependencies] -actix-rt = "2.0.0-beta.3" +actix-rt = "2.0.0" futures-util = { version = "0.3.7", default-features = false } diff --git a/actix-tls/Cargo.toml b/actix-tls/Cargo.toml index 8c13a2fd..acdd0419 100755 --- a/actix-tls/Cargo.toml +++ b/actix-tls/Cargo.toml @@ -41,7 +41,7 @@ uri = ["http"] [dependencies] actix-codec = "0.4.0-beta.1" -actix-rt = { version = "2.0.0-beta.3", default-features = false } +actix-rt = { version = "2.0.0", default-features = false } actix-service = "2.0.0-beta.3" actix-utils = "3.0.0-beta.1" @@ -67,7 +67,7 @@ tls-native-tls = { package = "native-tls", version = "0.2", optional = true } tokio-native-tls = { version = "0.3", optional = true } [dev-dependencies] -actix-rt = "2.0.0-beta.3" +actix-rt = "2.0.0" actix-server = "2.0.0-beta.2" bytes = "1" env_logger = "0.8" diff --git a/actix-tracing/Cargo.toml b/actix-tracing/Cargo.toml index 24888da3..23d785cb 100644 --- a/actix-tracing/Cargo.toml +++ b/actix-tracing/Cargo.toml @@ -23,5 +23,5 @@ tracing = "0.1" tracing-futures = "0.2" [dev_dependencies] -actix-rt = "2.0.0-beta.3" +actix-rt = "2.0.0" slab = "0.4" diff --git a/actix-utils/Cargo.toml b/actix-utils/Cargo.toml index 468cb8d7..8b19937f 100644 --- a/actix-utils/Cargo.toml +++ b/actix-utils/Cargo.toml @@ -17,7 +17,7 @@ path = "src/lib.rs" [dependencies] actix-codec = "0.4.0-beta.1" -actix-rt = { version = "2.0.0-beta.3", default-features = false } +actix-rt = { version = "2.0.0", default-features = false } actix-service = "2.0.0-beta.3" futures-core = { version = "0.3.7", default-features = false } From 4a8693d00006493bf4523782e5b99d58b791935f Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Wed, 3 Feb 2021 11:18:35 +0000 Subject: [PATCH 58/73] readme grammar --- actix-rt/README.md | 4 +--- actix-service/README.md | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/actix-rt/README.md b/actix-rt/README.md index 7027b91a..c29d563d 100644 --- a/actix-rt/README.md +++ b/actix-rt/README.md @@ -2,6 +2,4 @@ > Tokio-based single-threaded async runtime for the Actix ecosystem. -See documentation for detailed explanations these components: [https://docs.rs/actix-rt][docs]. - -[docs]: https://docs.rs/actix-rt +See crate documentation for more: https://docs.rs/actix-rt. diff --git a/actix-service/README.md b/actix-service/README.md index 73108a36..1656f6b5 100644 --- a/actix-service/README.md +++ b/actix-service/README.md @@ -2,6 +2,4 @@ > Service trait and combinators for representing asynchronous request/response operations. -See documentation for detailed explanations these components: [https://docs.rs/actix-service][docs]. - -[docs]: https://docs.rs/actix-service +See documentation for detailed explanations of these components: https://docs.rs/actix-service. From db3385e8659ae17aa43fb27b8d119eda254c111c Mon Sep 17 00:00:00 2001 From: shuo Date: Thu, 4 Feb 2021 18:20:37 +0800 Subject: [PATCH 59/73] retry on EINTR in accept loop (#264) Co-authored-by: lishuo --- actix-server/CHANGES.md | 2 ++ actix-server/src/accept.rs | 13 ++++++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/actix-server/CHANGES.md b/actix-server/CHANGES.md index 70728064..d99287ef 100644 --- a/actix-server/CHANGES.md +++ b/actix-server/CHANGES.md @@ -2,8 +2,10 @@ ## Unreleased - 2021-xx-xx * Hidden `ServerBuilder::start` method has been removed. Use `ServerBuilder::run`. [#246] +* Add retry for EINTR(`io::Interrupted`) in `Accept`'s poll loop. [#264] [#246]: https://github.com/actix/actix-net/pull/246 +[#264]: https://github.com/actix/actix-net/pull/264 ## 2.0.0-beta.2 - 2021-01-03 diff --git a/actix-server/src/accept.rs b/actix-server/src/accept.rs index a52184d9..c8c1da47 100644 --- a/actix-server/src/accept.rs +++ b/actix-server/src/accept.rs @@ -161,9 +161,16 @@ impl Accept { let mut events = mio::Events::with_capacity(128); loop { - self.poll - .poll(&mut events, None) - .unwrap_or_else(|e| panic!("Poll error: {}", e)); + if let Err(e) = self.poll.poll(&mut events, None) { + match e.kind() { + std::io::ErrorKind::Interrupted => { + continue; + } + _ => { + panic!("Poll error: {}", e); + } + } + } for event in events.iter() { let token = event.token(); From 7f0eddd794a9d8efac2ea5fa42034993961e8c59 Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Thu, 4 Feb 2021 07:01:51 -0800 Subject: [PATCH 60/73] add blocking thread customize (#265) --- actix-server/CHANGES.md | 2 ++ actix-server/Cargo.toml | 2 +- actix-server/src/builder.rs | 29 +++++++++++++++++---- actix-server/src/worker.rs | 50 ++++++++++++++++++++++++++++++++----- 4 files changed, 71 insertions(+), 12 deletions(-) diff --git a/actix-server/CHANGES.md b/actix-server/CHANGES.md index d99287ef..be59f125 100644 --- a/actix-server/CHANGES.md +++ b/actix-server/CHANGES.md @@ -3,9 +3,11 @@ ## Unreleased - 2021-xx-xx * Hidden `ServerBuilder::start` method has been removed. Use `ServerBuilder::run`. [#246] * Add retry for EINTR(`io::Interrupted`) in `Accept`'s poll loop. [#264] +* Add `ServerBuilder::worker_max_blocking_threads` for customize blocking thread pool. [#265] [#246]: https://github.com/actix/actix-net/pull/246 [#264]: https://github.com/actix/actix-net/pull/264 +[#265]: https://github.com/actix/actix-net/pull/265 ## 2.0.0-beta.2 - 2021-01-03 diff --git a/actix-server/Cargo.toml b/actix-server/Cargo.toml index 845dc03e..db9d4d8b 100755 --- a/actix-server/Cargo.toml +++ b/actix-server/Cargo.toml @@ -36,7 +36,7 @@ slab = "0.4" tokio = { version = "1", features = ["sync"] } [dev-dependencies] -actix-rt = "2.0.0-beta.2" +actix-rt = "2.0.0" bytes = "1" env_logger = "0.8" futures-util = { version = "0.3.7", default-features = false, features = ["sink"] } diff --git a/actix-server/src/builder.rs b/actix-server/src/builder.rs index 7290f9dd..78a1323d 100644 --- a/actix-server/src/builder.rs +++ b/actix-server/src/builder.rs @@ -19,7 +19,7 @@ use crate::signals::{Signal, Signals}; use crate::socket::{MioListener, StdSocketAddr, StdTcpListener, ToSocketAddrs}; use crate::socket::{MioTcpListener, MioTcpSocket}; use crate::waker_queue::{WakerInterest, WakerQueue}; -use crate::worker::{self, ServerWorker, WorkerAvailability, WorkerHandle}; +use crate::worker::{self, ServerWorker, ServerWorkerConfig, WorkerAvailability, WorkerHandle}; use crate::{join_all, Token}; /// Server builder @@ -32,11 +32,11 @@ pub struct ServerBuilder { sockets: Vec<(Token, String, MioListener)>, accept: AcceptLoop, exit: bool, - shutdown_timeout: Duration, no_signals: bool, cmd: UnboundedReceiver, server: Server, notify: Vec>, + worker_config: ServerWorkerConfig, } impl Default for ServerBuilder { @@ -60,11 +60,11 @@ impl ServerBuilder { accept: AcceptLoop::new(server.clone()), backlog: 2048, exit: false, - shutdown_timeout: Duration::from_secs(30), no_signals: false, cmd: rx, notify: Vec::new(), server, + worker_config: ServerWorkerConfig::default(), } } @@ -78,6 +78,24 @@ impl ServerBuilder { self } + /// Set max number of threads for each worker's blocking task thread pool. + /// + /// One thread pool is set up **per worker**; not shared across workers. + /// + /// # Examples: + /// ``` + /// # use actix_server::ServerBuilder; + /// let builder = ServerBuilder::new() + /// .workers(4) // server has 4 worker thread. + /// .worker_max_blocking_threads(4); // every worker has 4 max blocking threads. + /// ``` + /// + /// See [tokio::runtime::Builder::max_blocking_threads] for behavior reference. + pub fn worker_max_blocking_threads(mut self, num: usize) -> Self { + self.worker_config.max_blocking_threads(num); + self + } + /// Set the maximum number of pending connections. /// /// This refers to the number of clients that can be waiting to be served. @@ -124,7 +142,8 @@ impl ServerBuilder { /// /// By default shutdown timeout sets to 30 seconds. pub fn shutdown_timeout(mut self, sec: u64) -> Self { - self.shutdown_timeout = Duration::from_secs(sec); + self.worker_config + .shutdown_timeout(Duration::from_secs(sec)); self } @@ -297,7 +316,7 @@ impl ServerBuilder { let avail = WorkerAvailability::new(waker); let services = self.services.iter().map(|v| v.clone_factory()).collect(); - ServerWorker::start(idx, services, avail, self.shutdown_timeout) + ServerWorker::start(idx, services, avail, self.worker_config) } fn handle_cmd(&mut self, item: ServerCommand) { diff --git a/actix-server/src/worker.rs b/actix-server/src/worker.rs index 25a0429c..defc7306 100644 --- a/actix-server/src/worker.rs +++ b/actix-server/src/worker.rs @@ -133,7 +133,7 @@ pub(crate) struct ServerWorker { conns: Counter, factories: Vec>, state: WorkerState, - shutdown_timeout: Duration, + config: ServerWorkerConfig, } struct WorkerService { @@ -159,26 +159,62 @@ enum WorkerServiceStatus { Stopped, } +/// Config for worker behavior passed down from server builder. +#[derive(Copy, Clone)] +pub(crate) struct ServerWorkerConfig { + shutdown_timeout: Duration, + max_blocking_threads: usize, +} + +impl Default for ServerWorkerConfig { + fn default() -> Self { + // 512 is the default max blocking thread count of tokio runtime. + let max_blocking_threads = std::cmp::max(512 / num_cpus::get(), 1); + Self { + shutdown_timeout: Duration::from_secs(30), + max_blocking_threads, + } + } +} + +impl ServerWorkerConfig { + pub(crate) fn max_blocking_threads(&mut self, num: usize) { + self.max_blocking_threads = num; + } + + pub(crate) fn shutdown_timeout(&mut self, dur: Duration) { + self.shutdown_timeout = dur; + } +} + impl ServerWorker { pub(crate) fn start( idx: usize, factories: Vec>, availability: WorkerAvailability, - shutdown_timeout: Duration, + config: ServerWorkerConfig, ) -> WorkerHandle { let (tx1, rx) = unbounded_channel(); let (tx2, rx2) = unbounded_channel(); let avail = availability.clone(); // every worker runs in it's own arbiter. - Arbiter::new().spawn(Box::pin(async move { + // use a custom tokio runtime builder to change the settings of runtime. + Arbiter::with_tokio_rt(move || { + tokio::runtime::Builder::new_current_thread() + .enable_all() + .max_blocking_threads(config.max_blocking_threads) + .build() + .unwrap() + }) + .spawn(async move { availability.set(false); let mut wrk = MAX_CONNS_COUNTER.with(move |conns| ServerWorker { rx, rx2, availability, factories, - shutdown_timeout, + config, services: Vec::new(), conns: conns.clone(), state: WorkerState::Unavailable, @@ -198,6 +234,8 @@ impl ServerWorker { }) .collect::>(); + // a second spawn to make sure worker future runs as non boxed future. + // As Arbiter::spawn would box the future before send it to arbiter. spawn(async move { let res: Result, _> = join_all(fut).await.into_iter().collect(); match res { @@ -220,7 +258,7 @@ impl ServerWorker { } wrk.await }); - })); + }); WorkerHandle::new(idx, tx1, tx2, avail) } @@ -324,7 +362,7 @@ impl Future for ServerWorker { info!("Graceful worker shutdown, {} connections", num); self.state = WorkerState::Shutdown( Box::pin(sleep_until(Instant::now() + Duration::from_secs(1))), - Box::pin(sleep_until(Instant::now() + self.shutdown_timeout)), + Box::pin(sleep_until(Instant::now() + self.config.shutdown_timeout)), Some(result), ); } else { From b5399c56315b3ea4aa2d1b68fb40568481147721 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Thu, 4 Feb 2021 15:14:48 +0000 Subject: [PATCH 61/73] use reusable box future in tls connector --- actix-tls/Cargo.toml | 1 + actix-tls/src/connect/connector.rs | 13 +++++++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/actix-tls/Cargo.toml b/actix-tls/Cargo.toml index acdd0419..aa5f0c89 100755 --- a/actix-tls/Cargo.toml +++ b/actix-tls/Cargo.toml @@ -49,6 +49,7 @@ derive_more = "0.99.5" futures-core = { version = "0.3.7", default-features = false, features = ["alloc"] } http = { version = "0.2.3", optional = true } log = "0.4" +tokio-util = { version = "0.6.3", default-features = false } # openssl tls-openssl = { package = "openssl", version = "0.10", optional = true } diff --git a/actix-tls/src/connect/connector.rs b/actix-tls/src/connect/connector.rs index 5284eff4..9acb1dd5 100755 --- a/actix-tls/src/connect/connector.rs +++ b/actix-tls/src/connect/connector.rs @@ -11,6 +11,7 @@ use actix_rt::net::TcpStream; use actix_service::{Service, ServiceFactory}; use futures_core::{future::LocalBoxFuture, ready}; use log::{error, trace}; +use tokio_util::sync::ReusableBoxFuture; use super::connect::{Address, Connect, ConnectAddrs, Connection}; use super::error::ConnectError; @@ -65,7 +66,7 @@ pub enum TcpConnectorResponse { req: Option, port: u16, addrs: Option>, - stream: Option>>, + stream: Option>>, }, Error(Option), } @@ -90,7 +91,7 @@ impl TcpConnectorResponse { req: Some(req), port, addrs: None, - stream: Some(Box::pin(TcpStream::connect(addr))), + stream: Some(ReusableBoxFuture::new(TcpStream::connect(addr))), }, // when resolver returns multiple socket addr for request they would be popped from @@ -119,7 +120,7 @@ impl Future for TcpConnectorResponse { stream, } => loop { if let Some(new) = stream.as_mut() { - match ready!(new.as_mut().poll(cx)) { + match ready!(new.poll(cx)) { Ok(sock) => { let req = req.take().unwrap(); trace!( @@ -146,7 +147,11 @@ impl Future for TcpConnectorResponse { // try to connect let addr = addrs.as_mut().unwrap().pop_front().unwrap(); - *stream = Some(Box::pin(TcpStream::connect(addr))); + + match stream { + Some(rbf) => rbf.set(TcpStream::connect(addr)), + None => *stream = Some(ReusableBoxFuture::new(TcpStream::connect(addr))), + } }, } } From c918da906bea572477f1a75c1f31240aabc8b20b Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Thu, 4 Feb 2021 15:22:38 +0000 Subject: [PATCH 62/73] use reexported tls crates when possible --- actix-tls/Cargo.toml | 9 ++------- actix-tls/examples/basic.rs | 4 ++-- actix-tls/src/accept/nativetls.rs | 2 +- actix-tls/src/accept/rustls.rs | 2 +- actix-tls/src/connect/ssl/rustls.rs | 4 ++-- actix-tls/src/lib.rs | 4 ---- 6 files changed, 8 insertions(+), 17 deletions(-) diff --git a/actix-tls/Cargo.toml b/actix-tls/Cargo.toml index aa5f0c89..61853b60 100755 --- a/actix-tls/Cargo.toml +++ b/actix-tls/Cargo.toml @@ -31,10 +31,10 @@ connect = [] openssl = ["tls-openssl", "tokio-openssl"] # use rustls impls -rustls = ["tls-rustls", "webpki", "webpki-roots", "tokio-rustls"] +rustls = ["tokio-rustls", "webpki-roots"] # use native-tls impls -native-tls = ["tls-native-tls", "tokio-native-tls"] +native-tls = ["tokio-native-tls"] # support http::Uri as connect address uri = ["http"] @@ -55,16 +55,11 @@ tokio-util = { version = "0.6.3", default-features = false } tls-openssl = { package = "openssl", version = "0.10", optional = true } tokio-openssl = { version = "0.6", optional = true } -# TODO: Reduce dependencies where tokio wrappers re-export base crate. - # rustls -tls-rustls = { package = "rustls", version = "0.19", optional = true } tokio-rustls = { version = "0.22", optional = true } -webpki = { version = "0.21", optional = true } webpki-roots = { version = "0.21", optional = true } # native-tls -tls-native-tls = { package = "native-tls", version = "0.2", optional = true } tokio-native-tls = { version = "0.3", optional = true } [dev-dependencies] diff --git a/actix-tls/examples/basic.rs b/actix-tls/examples/basic.rs index 3f4cdb24..d1762b08 100644 --- a/actix-tls/examples/basic.rs +++ b/actix-tls/examples/basic.rs @@ -15,9 +15,9 @@ //! http --verify=false https://127.0.0.1:8443 //! ``` -// this rename only exists because of how we have organised the crate's feature flags +// this use only exists because of how we have organised the crate // it is not necessary for your actual code -extern crate tls_rustls as rustls; +use tokio_rustls::rustls; use std::{ env, diff --git a/actix-tls/src/accept/nativetls.rs b/actix-tls/src/accept/nativetls.rs index 73090de6..236ce973 100644 --- a/actix-tls/src/accept/nativetls.rs +++ b/actix-tls/src/accept/nativetls.rs @@ -5,7 +5,7 @@ use actix_service::{Service, ServiceFactory}; use actix_utils::counter::Counter; use futures_core::future::LocalBoxFuture; -pub use native_tls::Error; +pub use tokio_native_tls::native_tls::Error; pub use tokio_native_tls::{TlsAcceptor, TlsStream}; use super::MAX_CONN_COUNTER; diff --git a/actix-tls/src/accept/rustls.rs b/actix-tls/src/accept/rustls.rs index c65d4657..ff5cf3e5 100644 --- a/actix-tls/src/accept/rustls.rs +++ b/actix-tls/src/accept/rustls.rs @@ -12,7 +12,7 @@ use actix_utils::counter::{Counter, CounterGuard}; use futures_core::future::LocalBoxFuture; use tokio_rustls::{Accept, TlsAcceptor}; -pub use rustls::{ServerConfig, Session}; +pub use tokio_rustls::rustls::{ServerConfig, Session}; pub use tokio_rustls::server::TlsStream; use super::MAX_CONN_COUNTER; diff --git a/actix-tls/src/connect/ssl/rustls.rs b/actix-tls/src/connect/ssl/rustls.rs index 9bb5f80f..811c7adc 100755 --- a/actix-tls/src/connect/ssl/rustls.rs +++ b/actix-tls/src/connect/ssl/rustls.rs @@ -6,7 +6,7 @@ use std::{ task::{Context, Poll}, }; -pub use rustls::Session; +pub use tokio_rustls::rustls::Session; pub use tokio_rustls::{client::TlsStream, rustls::ClientConfig}; pub use webpki_roots::TLS_SERVER_ROOTS; @@ -15,7 +15,7 @@ use actix_service::{Service, ServiceFactory}; use futures_core::{future::LocalBoxFuture, ready}; use log::trace; use tokio_rustls::{Connect, TlsConnector}; -use webpki::DNSNameRef; +use tokio_rustls::webpki::DNSNameRef; use crate::connect::{Address, Connection}; diff --git a/actix-tls/src/lib.rs b/actix-tls/src/lib.rs index 1fa08b6b..83e18d58 100644 --- a/actix-tls/src/lib.rs +++ b/actix-tls/src/lib.rs @@ -4,12 +4,8 @@ #![doc(html_logo_url = "https://actix.rs/img/logo.png")] #![doc(html_favicon_url = "https://actix.rs/favicon.ico")] -#[cfg(feature = "native-tls")] -extern crate tls_native_tls as native_tls; #[cfg(feature = "openssl")] extern crate tls_openssl as openssl; -#[cfg(feature = "rustls")] -extern crate tls_rustls as rustls; #[cfg(feature = "accept")] pub mod accept; From a77b70aed2f1cc58a34bc7598ca39b533008157b Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Thu, 4 Feb 2021 20:44:13 +0000 Subject: [PATCH 63/73] prepare service 2.0.0-beta.4 release (#269) --- actix-macros/Cargo.toml | 2 +- actix-server/Cargo.toml | 2 +- actix-service/CHANGES.md | 11 +++++++---- actix-service/Cargo.toml | 3 ++- actix-service/README.md | 8 ++++++++ actix-tls/Cargo.toml | 2 +- actix-tls/src/connect/ssl/rustls.rs | 2 +- actix-tracing/Cargo.toml | 2 +- actix-utils/Cargo.toml | 2 +- 9 files changed, 23 insertions(+), 11 deletions(-) diff --git a/actix-macros/Cargo.toml b/actix-macros/Cargo.toml index 5da49b16..0555f990 100644 --- a/actix-macros/Cargo.toml +++ b/actix-macros/Cargo.toml @@ -17,7 +17,7 @@ quote = "1.0.3" syn = { version = "^1", features = ["full"] } [dev-dependencies] -actix-rt = "2.0.0-beta.3" +actix-rt = "2.0.0" futures-util = { version = "0.3", default-features = false } trybuild = "1" diff --git a/actix-server/Cargo.toml b/actix-server/Cargo.toml index db9d4d8b..8b968352 100755 --- a/actix-server/Cargo.toml +++ b/actix-server/Cargo.toml @@ -25,7 +25,7 @@ default = [] [dependencies] actix-codec = "0.4.0-beta.1" actix-rt = { version = "2.0.0", default-features = false } -actix-service = "2.0.0-beta.3" +actix-service = "2.0.0-beta.4" actix-utils = "3.0.0-beta.1" futures-core = { version = "0.3.7", default-features = false, features = ["alloc"] } diff --git a/actix-service/CHANGES.md b/actix-service/CHANGES.md index 4a99d60a..f5da9d2e 100644 --- a/actix-service/CHANGES.md +++ b/actix-service/CHANGES.md @@ -1,10 +1,13 @@ # Changes ## Unreleased - 2021-xx-xx -* `Service::poll_ready` and `Service::call` take `&self`. [#247] -* `apply_fn` and `apply_fn_factory` would take `Fn(Req, &Service)` function type [#247] -* `apply_cfg` and `apply_cfg_factory` would take `Fn(Req, &Service)` function type [#247] -* `fn_service` module would take `Fn(Req)` function type. [#247] + + +## 2.0.0-beta.4 - 2021-02-04 +* `Service::poll_ready` and `Service::call` receive `&self`. [#247] +* `apply_fn` and `apply_fn_factory` now receive `Fn(Req, &Service)` function type. [#247] +* `apply_cfg` and `apply_cfg_factory` now receive `Fn(Req, &Service)` function type. [#247] +* `fn_service` and friends now receive `Fn(Req)` function type. [#247] [#247]: https://github.com/actix/actix-net/pull/247 diff --git a/actix-service/Cargo.toml b/actix-service/Cargo.toml index ce18ba66..eecf4669 100644 --- a/actix-service/Cargo.toml +++ b/actix-service/Cargo.toml @@ -1,9 +1,10 @@ [package] name = "actix-service" -version = "2.0.0-beta.3" +version = "2.0.0-beta.4" authors = [ "Nikolay Kim ", "Rob Ede ", + "fakeshadow <24548779@qq.com>", ] description = "Service trait and combinators for representing asynchronous request/response operations." keywords = ["network", "framework", "async", "futures", "service"] diff --git a/actix-service/README.md b/actix-service/README.md index 1656f6b5..28c38295 100644 --- a/actix-service/README.md +++ b/actix-service/README.md @@ -2,4 +2,12 @@ > Service trait and combinators for representing asynchronous request/response operations. +[![crates.io](https://img.shields.io/crates/v/actix-service?label=latest)](https://crates.io/crates/actix-service) +[![Documentation](https://docs.rs/actix-service/badge.svg?version=2.0.0-beta.4)](https://docs.rs/actix-service/2.0.0-beta.4) +[![Version](https://img.shields.io/badge/rustc-1.46+-ab6000.svg)](https://blog.rust-lang.org/2020/03/12/Rust-1.46.html) +![License](https://img.shields.io/crates/l/actix-service.svg) +[![Dependency Status](https://deps.rs/crate/actix-service/2.0.0-beta.4/status.svg)](https://deps.rs/crate/actix-service/2.0.0-beta.4) +[![Download](https://img.shields.io/crates/d/actix-service.svg)](https://crates.io/crates/actix-service) +[![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x) + See documentation for detailed explanations of these components: https://docs.rs/actix-service. diff --git a/actix-tls/Cargo.toml b/actix-tls/Cargo.toml index 61853b60..09d47157 100755 --- a/actix-tls/Cargo.toml +++ b/actix-tls/Cargo.toml @@ -42,7 +42,7 @@ uri = ["http"] [dependencies] actix-codec = "0.4.0-beta.1" actix-rt = { version = "2.0.0", default-features = false } -actix-service = "2.0.0-beta.3" +actix-service = "2.0.0-beta.4" actix-utils = "3.0.0-beta.1" derive_more = "0.99.5" diff --git a/actix-tls/src/connect/ssl/rustls.rs b/actix-tls/src/connect/ssl/rustls.rs index 811c7adc..46b4b11d 100755 --- a/actix-tls/src/connect/ssl/rustls.rs +++ b/actix-tls/src/connect/ssl/rustls.rs @@ -14,8 +14,8 @@ use actix_codec::{AsyncRead, AsyncWrite}; use actix_service::{Service, ServiceFactory}; use futures_core::{future::LocalBoxFuture, ready}; use log::trace; -use tokio_rustls::{Connect, TlsConnector}; use tokio_rustls::webpki::DNSNameRef; +use tokio_rustls::{Connect, TlsConnector}; use crate::connect::{Address, Connection}; diff --git a/actix-tracing/Cargo.toml b/actix-tracing/Cargo.toml index 23d785cb..60ad1454 100644 --- a/actix-tracing/Cargo.toml +++ b/actix-tracing/Cargo.toml @@ -16,7 +16,7 @@ name = "actix_tracing" path = "src/lib.rs" [dependencies] -actix-service = "2.0.0-beta.3" +actix-service = "2.0.0-beta.4" futures-util = { version = "0.3.4", default-features = false } tracing = "0.1" diff --git a/actix-utils/Cargo.toml b/actix-utils/Cargo.toml index 8b19937f..4c1e6569 100644 --- a/actix-utils/Cargo.toml +++ b/actix-utils/Cargo.toml @@ -18,7 +18,7 @@ path = "src/lib.rs" [dependencies] actix-codec = "0.4.0-beta.1" actix-rt = { version = "2.0.0", default-features = false } -actix-service = "2.0.0-beta.3" +actix-service = "2.0.0-beta.4" futures-core = { version = "0.3.7", default-features = false } futures-sink = { version = "0.3.7", default-features = false } From ebb9cd055f9dfb19fabdcedb506ba78c43cb43dd Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Fri, 5 Feb 2021 19:38:11 -0800 Subject: [PATCH 64/73] use static dispatch on signal handling. reduce allocation (#272) --- actix-rt/Cargo.toml | 4 ++-- actix-server/src/signals.rs | 40 +++++++++++++++++-------------------- 2 files changed, 20 insertions(+), 24 deletions(-) diff --git a/actix-rt/Cargo.toml b/actix-rt/Cargo.toml index f8f3984d..db232090 100644 --- a/actix-rt/Cargo.toml +++ b/actix-rt/Cargo.toml @@ -26,8 +26,8 @@ macros = ["actix-macros"] actix-macros = { version = "0.2.0", optional = true } futures-core = { version = "0.3", default-features = false } -tokio = { version = "1", features = ["rt", "net", "parking_lot", "signal", "sync", "time"] } +tokio = { version = "1.2", features = ["rt", "net", "parking_lot", "signal", "sync", "time"] } [dev-dependencies] -tokio = { version = "1", features = ["full"] } +tokio = { version = "1.2", features = ["full"] } hyper = { version = "0.14", default-features = false, features = ["server", "tcp", "http1"] } diff --git a/actix-server/src/signals.rs b/actix-server/src/signals.rs index ea1de47e..0e3cef13 100644 --- a/actix-server/src/signals.rs +++ b/actix-server/src/signals.rs @@ -2,8 +2,6 @@ use std::future::Future; use std::pin::Pin; use std::task::{Context, Poll}; -use futures_core::future::LocalBoxFuture; - use crate::server::Server; /// Different types of process signals @@ -23,9 +21,9 @@ pub(crate) enum Signal { pub(crate) struct Signals { srv: Server, #[cfg(not(unix))] - signals: LocalBoxFuture<'static, std::io::Result<()>>, + signals: futures_core::future::LocalBoxFuture<'static, std::io::Result<()>>, #[cfg(unix)] - signals: Vec<(Signal, LocalBoxFuture<'static, ()>)>, + signals: Vec<(Signal, actix_rt::signal::unix::Signal)>, } impl Signals { @@ -48,23 +46,21 @@ impl Signals { (unix::SignalKind::quit(), Signal::Quit), ]; - let mut signals = Vec::new(); - - for (kind, sig) in sig_map.iter() { - match unix::signal(*kind) { - Ok(mut stream) => { - let fut = Box::pin(async move { - let _ = stream.recv().await; - }) as _; - signals.push((*sig, fut)); - } - Err(e) => log::error!( - "Can not initialize stream handler for {:?} err: {}", - sig, - e - ), - } - } + let signals = sig_map + .iter() + .filter_map(|(kind, sig)| { + unix::signal(*kind) + .map(|tokio_sig| (*sig, tokio_sig)) + .map_err(|e| { + log::error!( + "Can not initialize stream handler for {:?} err: {}", + sig, + e + ) + }) + .ok() + }) + .collect::>(); actix_rt::spawn(Signals { srv, signals }); } @@ -86,7 +82,7 @@ impl Future for Signals { #[cfg(unix)] { for (sig, fut) in self.signals.iter_mut() { - if fut.as_mut().poll(cx).is_ready() { + if Pin::new(fut).poll_recv(cx).is_ready() { let sig = *sig; self.srv.signal(sig); return Poll::Ready(()); From 9d0901e07f499935551f0b0e5cc261a043476d57 Mon Sep 17 00:00:00 2001 From: Riley Date: Sat, 6 Feb 2021 09:50:38 -0600 Subject: [PATCH 65/73] actix-rt: expose JoinError (#271) --- actix-rt/CHANGES.md | 3 +++ actix-rt/src/lib.rs | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/actix-rt/CHANGES.md b/actix-rt/CHANGES.md index 15052613..2e026425 100644 --- a/actix-rt/CHANGES.md +++ b/actix-rt/CHANGES.md @@ -1,6 +1,9 @@ # Changes ## Unreleased - 2021-xx-xx +* Expose JoinError from tokio. [#271] + +[#271]: https://github.com/actix/actix-net/pull/271 ## 2.0.0 - 2021-02-02 diff --git a/actix-rt/src/lib.rs b/actix-rt/src/lib.rs index 831958aa..a7e9f309 100644 --- a/actix-rt/src/lib.rs +++ b/actix-rt/src/lib.rs @@ -91,7 +91,7 @@ pub mod time { pub mod task { //! Task management (Tokio re-exports). - pub use tokio::task::{spawn_blocking, yield_now, JoinHandle}; + pub use tokio::task::{spawn_blocking, yield_now, JoinError, JoinHandle}; } /// Spawns a future on the current thread. From b4a3f51659962b09d3c5044cfa39afbf5bd1800a Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sat, 6 Feb 2021 15:54:11 +0000 Subject: [PATCH 66/73] prepare rt release 2.0.1 --- actix-rt/CHANGES.md | 5 ++++- actix-rt/Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/actix-rt/CHANGES.md b/actix-rt/CHANGES.md index 2e026425..05613e81 100644 --- a/actix-rt/CHANGES.md +++ b/actix-rt/CHANGES.md @@ -1,7 +1,10 @@ # Changes ## Unreleased - 2021-xx-xx -* Expose JoinError from tokio. [#271] + + +## 2.0.1 - 2021-02-06 +* Expose `JoinError` from Tokio. [#271] [#271]: https://github.com/actix/actix-net/pull/271 diff --git a/actix-rt/Cargo.toml b/actix-rt/Cargo.toml index db232090..375b5885 100644 --- a/actix-rt/Cargo.toml +++ b/actix-rt/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-rt" -version = "2.0.0" +version = "2.0.1" authors = [ "Nikolay Kim ", "Rob Ede ", From 16ba77c4c8f08c0cb0876f43de781fd29ddf0012 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sat, 6 Feb 2021 19:24:52 +0000 Subject: [PATCH 67/73] prepare next set of betas (#273) --- actix-server/CHANGES.md | 9 +++++++-- actix-server/Cargo.toml | 2 +- actix-tls/CHANGES.md | 7 ++++++- actix-tls/Cargo.toml | 4 ++-- actix-utils/CHANGES.md | 6 ++++++ actix-utils/Cargo.toml | 3 ++- bytestring/Cargo.toml | 2 +- 7 files changed, 25 insertions(+), 8 deletions(-) diff --git a/actix-server/CHANGES.md b/actix-server/CHANGES.md index be59f125..5eca1f91 100644 --- a/actix-server/CHANGES.md +++ b/actix-server/CHANGES.md @@ -1,13 +1,18 @@ # Changes ## Unreleased - 2021-xx-xx + + +## 2.0.0-beta.3 - 2021-02-06 * Hidden `ServerBuilder::start` method has been removed. Use `ServerBuilder::run`. [#246] -* Add retry for EINTR(`io::Interrupted`) in `Accept`'s poll loop. [#264] -* Add `ServerBuilder::worker_max_blocking_threads` for customize blocking thread pool. [#265] +* Add retry for EINTR signal (`io::Interrupted`) in `Accept`'s poll loop. [#264] +* Add `ServerBuilder::worker_max_blocking_threads` to customize blocking thread pool size. [#265] +* Update `actix-rt` to `2.0.0`. [#273] [#246]: https://github.com/actix/actix-net/pull/246 [#264]: https://github.com/actix/actix-net/pull/264 [#265]: https://github.com/actix/actix-net/pull/265 +[#273]: https://github.com/actix/actix-net/pull/273 ## 2.0.0-beta.2 - 2021-01-03 diff --git a/actix-server/Cargo.toml b/actix-server/Cargo.toml index 8b968352..1c3da21e 100755 --- a/actix-server/Cargo.toml +++ b/actix-server/Cargo.toml @@ -26,7 +26,7 @@ default = [] actix-codec = "0.4.0-beta.1" actix-rt = { version = "2.0.0", default-features = false } actix-service = "2.0.0-beta.4" -actix-utils = "3.0.0-beta.1" +actix-utils = "3.0.0-beta.2" futures-core = { version = "0.3.7", default-features = false, features = ["alloc"] } log = "0.4" diff --git a/actix-tls/CHANGES.md b/actix-tls/CHANGES.md index 11a1a410..a87f0fc5 100644 --- a/actix-tls/CHANGES.md +++ b/actix-tls/CHANGES.md @@ -1,17 +1,22 @@ # Changes ## Unreleased - 2021-xx-xx + + +## 3.0.0-beta.3 - 2021-02-06 * Remove `trust-dns-proto` and `trust-dns-resolver`. [#248] * Use `std::net::ToSocketAddrs` as simple and basic default resolver. [#248] -* Add `Resolve` trait for custom dns resolver. [#248] +* Add `Resolve` trait for custom DNS resolvers. [#248] * Add `Resolver::new_custom` function to construct custom resolvers. [#248] * Export `webpki_roots::TLS_SERVER_ROOTS` in `actix_tls::connect` mod and remove the export from `actix_tls::accept` [#248] * Remove `ConnectTakeAddrsIter`. `Connect::take_addrs` now returns `ConnectAddrsIter<'static>` as owned iterator. [#248] * Rename `Address::{host => hostname}` to more accurately describe which URL segment is returned. +* Update `actix-rt` to `2.0.0`. [#273] [#248]: https://github.com/actix/actix-net/pull/248 +[#273]: https://github.com/actix/actix-net/pull/273 ## 3.0.0-beta.2 - 2021-xx-xx diff --git a/actix-tls/Cargo.toml b/actix-tls/Cargo.toml index 09d47157..81da3d56 100755 --- a/actix-tls/Cargo.toml +++ b/actix-tls/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-tls" -version = "3.0.0-beta.2" +version = "3.0.0-beta.3" authors = ["Nikolay Kim "] description = "TLS acceptor and connector services for Actix ecosystem" keywords = ["network", "tls", "ssl", "async", "transport"] @@ -43,7 +43,7 @@ uri = ["http"] actix-codec = "0.4.0-beta.1" actix-rt = { version = "2.0.0", default-features = false } actix-service = "2.0.0-beta.4" -actix-utils = "3.0.0-beta.1" +actix-utils = "3.0.0-beta.2" derive_more = "0.99.5" futures-core = { version = "0.3.7", default-features = false, features = ["alloc"] } diff --git a/actix-utils/CHANGES.md b/actix-utils/CHANGES.md index 2504f012..a7871612 100644 --- a/actix-utils/CHANGES.md +++ b/actix-utils/CHANGES.md @@ -3,6 +3,12 @@ ## Unreleased - 2021-xx-xx +## 3.0.0-beta.2 - 2021-02-06 +* Update `actix-rt` to `2.0.0`. [#273] + +[#273]: https://github.com/actix/actix-net/pull/273 + + ## 3.0.0-beta.1 - 2020-12-28 * Update `bytes` dependency to `1`. [#237] * Use `pin-project-lite` to replace `pin-project`. [#229] diff --git a/actix-utils/Cargo.toml b/actix-utils/Cargo.toml index 4c1e6569..da46256e 100644 --- a/actix-utils/Cargo.toml +++ b/actix-utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-utils" -version = "3.0.0-beta.1" +version = "3.0.0-beta.2" authors = ["Nikolay Kim "] description = "Various network related services and utilities for the Actix ecosystem" keywords = ["network", "framework", "async", "futures"] @@ -26,4 +26,5 @@ log = "0.4" pin-project-lite = "0.2.0" [dev-dependencies] +actix-rt = "2.0.0" futures-util = { version = "0.3.7", default-features = false } diff --git a/bytestring/Cargo.toml b/bytestring/Cargo.toml index 3e53417c..3dbf07b7 100644 --- a/bytestring/Cargo.toml +++ b/bytestring/Cargo.toml @@ -24,4 +24,4 @@ serde = { version = "1.0", optional = true } [dev-dependencies] serde_json = "1.0" -ahash = { version = "0.6", default-features = false } +ahash = { version = "0.7", default-features = false } From 5f80d8501027082ad2b027e67e5fd3112b23b305 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sat, 6 Feb 2021 19:34:58 +0000 Subject: [PATCH 68/73] fix server version --- actix-server/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actix-server/Cargo.toml b/actix-server/Cargo.toml index 1c3da21e..9d13e456 100755 --- a/actix-server/Cargo.toml +++ b/actix-server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-server" -version = "2.0.0-beta.2" +version = "2.0.0-beta.3" authors = [ "Nikolay Kim ", "fakeshadow <24548779@qq.com>", From 0da848e4aee39a064f463f3e388c07c379af101c Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sat, 6 Feb 2021 19:35:29 +0000 Subject: [PATCH 69/73] fix server dev dep --- actix-tls/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actix-tls/Cargo.toml b/actix-tls/Cargo.toml index 81da3d56..db79d6ab 100755 --- a/actix-tls/Cargo.toml +++ b/actix-tls/Cargo.toml @@ -64,7 +64,7 @@ tokio-native-tls = { version = "0.3", optional = true } [dev-dependencies] actix-rt = "2.0.0" -actix-server = "2.0.0-beta.2" +actix-server = "2.0.0-beta.3" bytes = "1" env_logger = "0.8" futures-util = { version = "0.3.7", default-features = false, features = ["sink"] } From 7ee42b50b406b0d8b9ba809e55b96b9bd4c6dc52 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sat, 6 Feb 2021 19:50:48 +0000 Subject: [PATCH 70/73] prepare router 0.2.7 release --- actix-router/CHANGES.md | 3 +++ actix-router/Cargo.toml | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/actix-router/CHANGES.md b/actix-router/CHANGES.md index 581243fb..4c19aedc 100644 --- a/actix-router/CHANGES.md +++ b/actix-router/CHANGES.md @@ -1,6 +1,9 @@ # Changes ## Unreleased - 2021-xx-xx + + +## 0.2.7 - 2021-02-06 * Add `Router::recognize_checked` [#247] [#247]: https://github.com/actix/actix-net/pull/247 diff --git a/actix-router/Cargo.toml b/actix-router/Cargo.toml index 2446fc1b..f55c2c38 100644 --- a/actix-router/Cargo.toml +++ b/actix-router/Cargo.toml @@ -1,9 +1,9 @@ [package] name = "actix-router" -version = "0.2.6" +version = "0.2.7" authors = ["Nikolay Kim "] description = "Resource path matching library" -keywords = ["actix"] +keywords = ["actix", "router", "routing"] homepage = "https://actix.rs" repository = "https://github.com/actix/actix-net.git" documentation = "https://docs.rs/actix-router" From eb4d29e15e66d050045ed16c536028bfe41bf13b Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sat, 6 Feb 2021 22:27:56 +0000 Subject: [PATCH 71/73] add arbiter handle assoc fn (#274) * add arbiter handle assoc fn --- actix-rt/CHANGES.md | 3 +++ actix-rt/src/arbiter.rs | 7 ++++++- actix-rt/tests/tests.rs | 22 ++++++++++++++++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/actix-rt/CHANGES.md b/actix-rt/CHANGES.md index 05613e81..74e94aed 100644 --- a/actix-rt/CHANGES.md +++ b/actix-rt/CHANGES.md @@ -1,6 +1,9 @@ # Changes ## Unreleased - 2021-xx-xx +* Add `Arbiter::handle` to get a handle of an owned Arbiter. [#274] + +[#274]: https://github.com/actix/actix-net/pull/274 ## 2.0.1 - 2021-02-06 diff --git a/actix-rt/src/arbiter.rs b/actix-rt/src/arbiter.rs index 7eae662a..9ff1419d 100644 --- a/actix-rt/src/arbiter.rs +++ b/actix-rt/src/arbiter.rs @@ -172,13 +172,18 @@ impl Arbiter { hnd } + /// Return a handle to the this Arbiter's message sender. + pub fn handle(&self) -> ArbiterHandle { + ArbiterHandle::new(self.tx.clone()) + } + /// Return a handle to the current thread's Arbiter's message sender. /// /// # Panics /// Panics if no Arbiter is running on the current thread. pub fn current() -> ArbiterHandle { HANDLE.with(|cell| match *cell.borrow() { - Some(ref addr) => addr.clone(), + Some(ref hnd) => hnd.clone(), None => panic!("Arbiter is not running."), }) } diff --git a/actix-rt/tests/tests.rs b/actix-rt/tests/tests.rs index 56b5e8a6..5a292b31 100644 --- a/actix-rt/tests/tests.rs +++ b/actix-rt/tests/tests.rs @@ -122,6 +122,28 @@ fn arbiter_spawn_fn_runs() { arbiter.join().unwrap(); } +#[test] +fn arbiter_handle_spawn_fn_runs() { + let sys = System::new(); + + let (tx, rx) = channel::(); + + let arbiter = Arbiter::new(); + let handle = arbiter.handle(); + drop(arbiter); + + handle.spawn_fn(move || { + tx.send(42).unwrap(); + System::current().stop() + }); + + let num = rx.recv_timeout(Duration::from_secs(2)).unwrap(); + assert_eq!(num, 42); + + handle.stop(); + sys.run().unwrap(); +} + #[test] fn arbiter_drop_no_panic_fn() { let _ = System::new(); From 32543809f94685d2b01951fdfe68d1e327d99d18 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sat, 6 Feb 2021 22:45:03 +0000 Subject: [PATCH 72/73] add System::try_current (#275) --- actix-rt/CHANGES.md | 2 ++ actix-rt/src/system.rs | 9 +++++++++ actix-rt/tests/tests.rs | 10 ++++++++++ 3 files changed, 21 insertions(+) diff --git a/actix-rt/CHANGES.md b/actix-rt/CHANGES.md index 74e94aed..72c450c6 100644 --- a/actix-rt/CHANGES.md +++ b/actix-rt/CHANGES.md @@ -2,8 +2,10 @@ ## Unreleased - 2021-xx-xx * Add `Arbiter::handle` to get a handle of an owned Arbiter. [#274] +* Add `System::try_current` for situations where actix may or may not be running a System. [#275] [#274]: https://github.com/actix/actix-net/pull/274 +[#275]: https://github.com/actix/actix-net/pull/275 ## 2.0.1 - 2021-02-06 diff --git a/actix-rt/src/system.rs b/actix-rt/src/system.rs index b7f134cb..3bc8a6e3 100644 --- a/actix-rt/src/system.rs +++ b/actix-rt/src/system.rs @@ -100,6 +100,15 @@ impl System { }) } + /// Try to get current running system. + /// + /// Returns `None` if no System has been started. + /// + /// Contrary to `current`, this never panics. + pub fn try_current() -> Option { + CURRENT.with(|cell| cell.borrow().clone()) + } + /// Get handle to a the System's initial [Arbiter]. pub fn arbiter(&self) -> &ArbiterHandle { &self.arbiter_handle diff --git a/actix-rt/tests/tests.rs b/actix-rt/tests/tests.rs index 5a292b31..86fba96d 100644 --- a/actix-rt/tests/tests.rs +++ b/actix-rt/tests/tests.rs @@ -288,3 +288,13 @@ fn new_arbiter_with_tokio() { assert_eq!(false, counter.load(Ordering::SeqCst)); } + +#[test] +fn try_current_no_system() { + assert!(System::try_current().is_none()) +} + +#[test] +fn try_current_with_system() { + System::new().block_on(async { assert!(System::try_current().is_some()) }); +} From cb07ead39251994e9deeaabe3eadb5bd337bc463 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sat, 6 Feb 2021 22:52:53 +0000 Subject: [PATCH 73/73] prepare rt release 2.0.2 --- actix-rt/CHANGES.md | 3 +++ actix-rt/Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/actix-rt/CHANGES.md b/actix-rt/CHANGES.md index 72c450c6..6754ca33 100644 --- a/actix-rt/CHANGES.md +++ b/actix-rt/CHANGES.md @@ -1,6 +1,9 @@ # Changes ## Unreleased - 2021-xx-xx + + +## 2.0.2 - 2021-02-06 * Add `Arbiter::handle` to get a handle of an owned Arbiter. [#274] * Add `System::try_current` for situations where actix may or may not be running a System. [#275] diff --git a/actix-rt/Cargo.toml b/actix-rt/Cargo.toml index 375b5885..7990e67d 100644 --- a/actix-rt/Cargo.toml +++ b/actix-rt/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-rt" -version = "2.0.1" +version = "2.0.2" authors = [ "Nikolay Kim ", "Rob Ede ",