From 2cfe1d88ad8c60fab49441b4eace52394d419836 Mon Sep 17 00:00:00 2001 From: Juan Aguilar Date: Fri, 19 Feb 2021 18:12:30 +0100 Subject: [PATCH 01/72] Refactor LocalWaker for use Cell and remove deprecated methods (#278) --- actix-utils/src/task.rs | 26 ++++++-------------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/actix-utils/src/task.rs b/actix-utils/src/task.rs index 2a3469cf..d40ed286 100644 --- a/actix-utils/src/task.rs +++ b/actix-utils/src/task.rs @@ -1,4 +1,4 @@ -use core::cell::UnsafeCell; +use core::cell::Cell; use core::fmt; use core::marker::PhantomData; use core::task::Waker; @@ -19,10 +19,9 @@ use core::task::Waker; /// /// A single `AtomicWaker` may be reused for any number of calls to `register` or /// `wake`. -// TODO: Refactor to Cell when remove deprecated methods (@botika) #[derive(Default)] pub struct LocalWaker { - pub(crate) waker: UnsafeCell>, + pub(crate) waker: Cell>, // mark LocalWaker as a !Send type. _t: PhantomData<*const ()>, } @@ -31,31 +30,18 @@ impl LocalWaker { /// Create an `LocalWaker`. pub fn new() -> Self { LocalWaker { - waker: UnsafeCell::new(None), + waker: Cell::new(None), _t: PhantomData, } } - #[deprecated( - since = "2.1.0", - note = "In favor of `wake`. State of the register doesn't matter at `wake` up" - )] - /// Check if waker has been registered. - #[inline] - pub fn is_registered(&self) -> bool { - unsafe { (*self.waker.get()).is_some() } - } - /// 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(); - let last_waker = w.replace(Some(waker.clone())); - last_waker.is_some() - } + let last_waker = self.waker.replace(Some(waker.clone())); + last_waker.is_some() } /// Calls `wake` on the last `Waker` passed to `register`. @@ -73,7 +59,7 @@ impl LocalWaker { /// If a waker has not been registered, this returns `None`. #[inline] pub fn take(&self) -> Option { - unsafe { (*self.waker.get()).take() } + self.waker.take() } } From 75d7ae3139221f6424565ad669e34f4e9ac844f8 Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Sat, 20 Feb 2021 09:25:22 -0800 Subject: [PATCH 02/72] add actix stream trait (#276) --- actix-rt/src/lib.rs | 38 +++++++++++ actix-tls/examples/basic.rs | 7 +- actix-tls/src/accept/nativetls.rs | 97 +++++++++++++++++++++++---- actix-tls/src/accept/openssl.rs | 106 +++++++++++++++++++++++++----- actix-tls/src/accept/rustls.rs | 102 ++++++++++++++++++++++------ 5 files changed, 297 insertions(+), 53 deletions(-) diff --git a/actix-rt/src/lib.rs b/actix-rt/src/lib.rs index a7e9f309..1649a8b0 100644 --- a/actix-rt/src/lib.rs +++ b/actix-rt/src/lib.rs @@ -77,6 +77,44 @@ pub mod net { #[cfg(unix)] pub use tokio::net::{UnixDatagram, UnixListener, UnixStream}; + + use std::task::{Context, Poll}; + + use tokio::io::{AsyncRead, AsyncWrite}; + + /// Trait for generic over tokio stream types and various wrapper types around them. + pub trait ActixStream: AsyncRead + AsyncWrite + Unpin + 'static { + /// poll stream and check read readiness of Self. + /// + /// See [tokio::net::TcpStream::poll_read_ready] for detail + fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll>; + + /// poll stream and check write readiness of Self. + /// + /// See [tokio::net::TcpStream::poll_write_ready] for detail + fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll>; + } + + impl ActixStream for TcpStream { + fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll> { + TcpStream::poll_read_ready(self, cx) + } + + fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll> { + TcpStream::poll_write_ready(self, cx) + } + } + + #[cfg(unix)] + impl ActixStream for UnixStream { + fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll> { + UnixStream::poll_read_ready(self, cx) + } + + fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll> { + UnixStream::poll_write_ready(self, cx) + } + } } pub mod time { diff --git a/actix-tls/examples/basic.rs b/actix-tls/examples/basic.rs index d1762b08..d0c20428 100644 --- a/actix-tls/examples/basic.rs +++ b/actix-tls/examples/basic.rs @@ -29,9 +29,10 @@ use std::{ }, }; +use actix_rt::net::TcpStream; use actix_server::Server; use actix_service::pipeline_factory; -use actix_tls::accept::rustls::Acceptor as RustlsAcceptor; +use actix_tls::accept::rustls::{Acceptor as RustlsAcceptor, TlsStream}; use futures_util::future::ok; use log::info; use rustls::{ @@ -74,9 +75,9 @@ async fn main() -> io::Result<()> { // Set up TLS service factory pipeline_factory(tls_acceptor.clone()) .map_err(|err| println!("Rustls error: {:?}", err)) - .and_then(move |stream| { + .and_then(move |stream: TlsStream| { let num = count.fetch_add(1, Ordering::Relaxed); - info!("[{}] Got TLS connection: {:?}", num, stream); + info!("[{}] Got TLS connection: {:?}", num, &*stream); ok(()) }) })? diff --git a/actix-tls/src/accept/nativetls.rs b/actix-tls/src/accept/nativetls.rs index 236ce973..15e43c99 100644 --- a/actix-tls/src/accept/nativetls.rs +++ b/actix-tls/src/accept/nativetls.rs @@ -1,15 +1,94 @@ -use std::task::{Context, Poll}; +use std::{ + io::{self, IoSlice}, + ops::{Deref, DerefMut}, + pin::Pin, + task::{Context, Poll}, +}; -use actix_codec::{AsyncRead, AsyncWrite}; +use actix_codec::{AsyncRead, AsyncWrite, ReadBuf}; +use actix_rt::net::ActixStream; use actix_service::{Service, ServiceFactory}; use actix_utils::counter::Counter; use futures_core::future::LocalBoxFuture; pub use tokio_native_tls::native_tls::Error; -pub use tokio_native_tls::{TlsAcceptor, TlsStream}; +pub use tokio_native_tls::TlsAcceptor; use super::MAX_CONN_COUNTER; +/// wrapper type for `tokio_native_tls::TlsStream` in order to impl `ActixStream` trait. +pub struct TlsStream(tokio_native_tls::TlsStream); + +impl From> for TlsStream { + fn from(stream: tokio_native_tls::TlsStream) -> Self { + Self(stream) + } +} + +impl Deref for TlsStream { + type Target = tokio_native_tls::TlsStream; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for TlsStream { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl AsyncRead for TlsStream { + fn poll_read( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut ReadBuf<'_>, + ) -> Poll> { + Pin::new(&mut **self.get_mut()).poll_read(cx, buf) + } +} + +impl AsyncWrite for TlsStream { + fn poll_write( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + Pin::new(&mut **self.get_mut()).poll_write(cx, buf) + } + + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut **self.get_mut()).poll_flush(cx) + } + + fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut **self.get_mut()).poll_shutdown(cx) + } + + fn poll_write_vectored( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + bufs: &[IoSlice<'_>], + ) -> Poll> { + Pin::new(&mut **self.get_mut()).poll_write_vectored(cx, bufs) + } + + fn is_write_vectored(&self) -> bool { + (&**self).is_write_vectored() + } +} + +impl ActixStream for TlsStream { + fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll> { + T::poll_read_ready((&**self).get_ref().get_ref().get_ref(), cx) + } + + fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll> { + T::poll_write_ready((&**self).get_ref().get_ref().get_ref(), cx) + } +} + /// Accept TLS connections via `native-tls` package. /// /// `native-tls` feature enables this `Acceptor` type. @@ -34,10 +113,7 @@ impl Clone for Acceptor { } } -impl ServiceFactory for Acceptor -where - T: AsyncRead + AsyncWrite + Unpin + 'static, -{ +impl ServiceFactory for Acceptor { type Response = TlsStream; type Error = Error; type Config = (); @@ -71,10 +147,7 @@ impl Clone for NativeTlsAcceptorService { } } -impl Service for NativeTlsAcceptorService -where - T: AsyncRead + AsyncWrite + Unpin + 'static, -{ +impl Service for NativeTlsAcceptorService { type Response = TlsStream; type Error = Error; type Future = LocalBoxFuture<'static, Result, Error>>; @@ -93,7 +166,7 @@ where Box::pin(async move { let io = this.acceptor.accept(io).await; drop(guard); - io + io.map(Into::into) }) } } diff --git a/actix-tls/src/accept/openssl.rs b/actix-tls/src/accept/openssl.rs index 8ca88578..b490888a 100644 --- a/actix-tls/src/accept/openssl.rs +++ b/actix-tls/src/accept/openssl.rs @@ -1,10 +1,13 @@ use std::{ future::Future, + io::{self, IoSlice}, + ops::{Deref, DerefMut}, pin::Pin, task::{Context, Poll}, }; -use actix_codec::{AsyncRead, AsyncWrite}; +use actix_codec::{AsyncRead, AsyncWrite, ReadBuf}; +use actix_rt::net::ActixStream; use actix_service::{Service, ServiceFactory}; use actix_utils::counter::{Counter, CounterGuard}; use futures_core::{future::LocalBoxFuture, ready}; @@ -12,10 +15,82 @@ use futures_core::{future::LocalBoxFuture, ready}; pub use openssl::ssl::{ AlpnError, Error as SslError, HandshakeError, Ssl, SslAcceptor, SslAcceptorBuilder, }; -pub use tokio_openssl::SslStream; use super::MAX_CONN_COUNTER; +/// wrapper type for `tokio_openssl::SslStream` in order to impl `ActixStream` trait. +pub struct SslStream(tokio_openssl::SslStream); + +impl From> for SslStream { + fn from(stream: tokio_openssl::SslStream) -> Self { + Self(stream) + } +} + +impl Deref for SslStream { + type Target = tokio_openssl::SslStream; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for SslStream { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl AsyncRead for SslStream { + fn poll_read( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut ReadBuf<'_>, + ) -> Poll> { + Pin::new(&mut **self.get_mut()).poll_read(cx, buf) + } +} + +impl AsyncWrite for SslStream { + fn poll_write( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + Pin::new(&mut **self.get_mut()).poll_write(cx, buf) + } + + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut **self.get_mut()).poll_flush(cx) + } + + fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut **self.get_mut()).poll_shutdown(cx) + } + + fn poll_write_vectored( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + bufs: &[IoSlice<'_>], + ) -> Poll> { + Pin::new(&mut **self.get_mut()).poll_write_vectored(cx, bufs) + } + + fn is_write_vectored(&self) -> bool { + (&**self).is_write_vectored() + } +} + +impl ActixStream for SslStream { + fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll> { + T::poll_read_ready((&**self).get_ref(), cx) + } + + fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll> { + T::poll_write_ready((&**self).get_ref(), cx) + } +} + /// Accept TLS connections via `openssl` package. /// /// `openssl` feature enables this `Acceptor` type. @@ -40,10 +115,7 @@ impl Clone for Acceptor { } } -impl ServiceFactory for Acceptor -where - T: AsyncRead + AsyncWrite + Unpin + 'static, -{ +impl ServiceFactory for Acceptor { type Response = SslStream; type Error = SslError; type Config = (); @@ -67,10 +139,7 @@ pub struct AcceptorService { conns: Counter, } -impl Service for AcceptorService -where - T: AsyncRead + AsyncWrite + Unpin + 'static, -{ +impl Service for AcceptorService { type Response = SslStream; type Error = SslError; type Future = AcceptorServiceResponse; @@ -88,24 +157,25 @@ where let ssl = Ssl::new(ssl_ctx).expect("Provided SSL acceptor was invalid."); AcceptorServiceResponse { _guard: self.conns.get(), - stream: Some(SslStream::new(ssl, io).unwrap()), + stream: Some(tokio_openssl::SslStream::new(ssl, io).unwrap()), } } } -pub struct AcceptorServiceResponse -where - T: AsyncRead + AsyncWrite, -{ - stream: Option>, +pub struct AcceptorServiceResponse { + stream: Option>, _guard: CounterGuard, } -impl Future for AcceptorServiceResponse { +impl Future for AcceptorServiceResponse { type Output = Result, SslError>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { ready!(Pin::new(self.stream.as_mut().unwrap()).poll_accept(cx))?; - Poll::Ready(Ok(self.stream.take().expect("SSL connect has resolved."))) + Poll::Ready(Ok(self + .stream + .take() + .expect("SSL connect has resolved.") + .into())) } } diff --git a/actix-tls/src/accept/rustls.rs b/actix-tls/src/accept/rustls.rs index ff5cf3e5..c19a6cec 100644 --- a/actix-tls/src/accept/rustls.rs +++ b/actix-tls/src/accept/rustls.rs @@ -1,22 +1,96 @@ use std::{ future::Future, - io, + io::{self, IoSlice}, + ops::{Deref, DerefMut}, pin::Pin, sync::Arc, task::{Context, Poll}, }; -use actix_codec::{AsyncRead, AsyncWrite}; +use actix_codec::{AsyncRead, AsyncWrite, ReadBuf}; +use actix_rt::net::ActixStream; use actix_service::{Service, ServiceFactory}; use actix_utils::counter::{Counter, CounterGuard}; use futures_core::future::LocalBoxFuture; use tokio_rustls::{Accept, TlsAcceptor}; pub use tokio_rustls::rustls::{ServerConfig, Session}; -pub use tokio_rustls::server::TlsStream; use super::MAX_CONN_COUNTER; +/// wrapper type for `tokio_openssl::SslStream` in order to impl `ActixStream` trait. +pub struct TlsStream(tokio_rustls::server::TlsStream); + +impl From> for TlsStream { + fn from(stream: tokio_rustls::server::TlsStream) -> Self { + Self(stream) + } +} + +impl Deref for TlsStream { + type Target = tokio_rustls::server::TlsStream; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for TlsStream { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl AsyncRead for TlsStream { + fn poll_read( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut ReadBuf<'_>, + ) -> Poll> { + Pin::new(&mut **self.get_mut()).poll_read(cx, buf) + } +} + +impl AsyncWrite for TlsStream { + fn poll_write( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + Pin::new(&mut **self.get_mut()).poll_write(cx, buf) + } + + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut **self.get_mut()).poll_flush(cx) + } + + fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut **self.get_mut()).poll_shutdown(cx) + } + + fn poll_write_vectored( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + bufs: &[IoSlice<'_>], + ) -> Poll> { + Pin::new(&mut **self.get_mut()).poll_write_vectored(cx, bufs) + } + + fn is_write_vectored(&self) -> bool { + (&**self).is_write_vectored() + } +} + +impl ActixStream for TlsStream { + fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll> { + T::poll_read_ready((&**self).get_ref().0, cx) + } + + fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll> { + T::poll_write_ready((&**self).get_ref().0, cx) + } +} + /// Accept TLS connections via `rustls` package. /// /// `rustls` feature enables this `Acceptor` type. @@ -43,10 +117,7 @@ impl Clone for Acceptor { } } -impl ServiceFactory for Acceptor -where - T: AsyncRead + AsyncWrite + Unpin, -{ +impl ServiceFactory for Acceptor { type Response = TlsStream; type Error = io::Error; type Config = (); @@ -72,10 +143,7 @@ pub struct AcceptorService { conns: Counter, } -impl Service for AcceptorService -where - T: AsyncRead + AsyncWrite + Unpin, -{ +impl Service for AcceptorService { type Response = TlsStream; type Error = io::Error; type Future = AcceptorServiceFut; @@ -96,22 +164,16 @@ where } } -pub struct AcceptorServiceFut -where - T: AsyncRead + AsyncWrite + Unpin, -{ +pub struct AcceptorServiceFut { fut: Accept, _guard: CounterGuard, } -impl Future for AcceptorServiceFut -where - T: AsyncRead + AsyncWrite + Unpin, -{ +impl Future for AcceptorServiceFut { type Output = Result, io::Error>; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let this = self.get_mut(); - Pin::new(&mut this.fut).poll(cx) + Pin::new(&mut this.fut).poll(cx).map_ok(TlsStream) } } From 7e483cc356cff98aa6776bae5de2a4dcdcd420d5 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sat, 20 Feb 2021 17:34:04 +0000 Subject: [PATCH 03/72] tweak task and stream docs --- actix-rt/src/lib.rs | 19 +++++++++---------- actix-utils/src/task.rs | 36 ++++++++++++++---------------------- 2 files changed, 23 insertions(+), 32 deletions(-) diff --git a/actix-rt/src/lib.rs b/actix-rt/src/lib.rs index 1649a8b0..a529bdb0 100644 --- a/actix-rt/src/lib.rs +++ b/actix-rt/src/lib.rs @@ -70,28 +70,27 @@ pub mod signal { } pub mod net { - //! TCP/UDP/Unix bindings (Tokio re-exports). + //! TCP/UDP/Unix bindings (mostly Tokio re-exports). + use std::task::{Context, Poll}; + + use tokio::io::{AsyncRead, AsyncWrite}; pub use tokio::net::UdpSocket; pub use tokio::net::{TcpListener, TcpStream}; #[cfg(unix)] pub use tokio::net::{UnixDatagram, UnixListener, UnixStream}; - use std::task::{Context, Poll}; - - use tokio::io::{AsyncRead, AsyncWrite}; - - /// Trait for generic over tokio stream types and various wrapper types around them. + /// Extension trait over async read+write types that can also signal readiness. pub trait ActixStream: AsyncRead + AsyncWrite + Unpin + 'static { - /// poll stream and check read readiness of Self. + /// Poll stream and check read readiness of Self. /// - /// See [tokio::net::TcpStream::poll_read_ready] for detail + /// See [tokio::net::TcpStream::poll_read_ready] for detail on intended use. fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll>; - /// poll stream and check write readiness of Self. + /// Poll stream and check write readiness of Self. /// - /// See [tokio::net::TcpStream::poll_write_ready] for detail + /// See [tokio::net::TcpStream::poll_write_ready] for detail on intended use. fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll>; } diff --git a/actix-utils/src/task.rs b/actix-utils/src/task.rs index d40ed286..d0793488 100644 --- a/actix-utils/src/task.rs +++ b/actix-utils/src/task.rs @@ -1,38 +1,30 @@ -use core::cell::Cell; -use core::fmt; -use core::marker::PhantomData; -use core::task::Waker; +use core::{cell::Cell, fmt, marker::PhantomData, task::Waker}; /// A synchronization primitive for task wakeup. /// -/// Sometimes the task interested in a given event will change over time. -/// An `LocalWaker` can coordinate concurrent notifications with the consumer -/// potentially "updating" the underlying task to wake up. This is useful in -/// scenarios where a computation completes in another task and wants to -/// notify the consumer, but the consumer is in the process of being migrated to -/// a new logical task. +/// Sometimes the task interested in a given event will change over time. A `LocalWaker` can +/// coordinate concurrent notifications with the consumer, potentially "updating" the underlying +/// task to wake up. This is useful in scenarios where a computation completes in another task and +/// wants to notify the consumer, but the consumer is in the process of being migrated to a new +/// logical task. /// -/// Consumers should call `register` before checking the result of a computation -/// and producers should call `wake` after producing the computation (this -/// differs from the usual `thread::park` pattern). It is also permitted for -/// `wake` to be called **before** `register`. This results in a no-op. +/// Consumers should call [`register`] before checking the result of a computation and producers +/// should call `wake` after producing the computation (this differs from the usual `thread::park` +/// pattern). It is also permitted for [`wake`] to be called _before_ [`register`]. This results in +/// a no-op. /// -/// A single `AtomicWaker` may be reused for any number of calls to `register` or -/// `wake`. +/// A single `LocalWaker` may be reused for any number of calls to [`register`] or [`wake`]. #[derive(Default)] pub struct LocalWaker { pub(crate) waker: Cell>, // mark LocalWaker as a !Send type. - _t: PhantomData<*const ()>, + _phantom: PhantomData<*const ()>, } impl LocalWaker { - /// Create an `LocalWaker`. + /// Creates a new, empty `LocalWaker`. pub fn new() -> Self { - LocalWaker { - waker: Cell::new(None), - _t: PhantomData, - } + LocalWaker::default() } /// Registers the waker to be notified on calls to `wake`. From 8d74cf387dc20b4eb022ec5a52c2e1fb2bff0a8a Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sat, 20 Feb 2021 18:04:05 +0000 Subject: [PATCH 04/72] standardize openssl based stream name --- actix-rt/CHANGES.md | 3 +++ actix-tls/CHANGES.md | 1 + actix-tls/src/accept/nativetls.rs | 2 +- actix-tls/src/accept/openssl.rs | 22 +++++++++++----------- actix-tls/src/accept/rustls.rs | 2 +- 5 files changed, 17 insertions(+), 13 deletions(-) diff --git a/actix-rt/CHANGES.md b/actix-rt/CHANGES.md index 6754ca33..59a4641c 100644 --- a/actix-rt/CHANGES.md +++ b/actix-rt/CHANGES.md @@ -1,6 +1,9 @@ # Changes ## Unreleased - 2021-xx-xx +* Add `ActixStream` extension trait to include readiness methods. [#276] + +[#276]: https://github.com/actix/actix-net/pull/276 ## 2.0.2 - 2021-02-06 diff --git a/actix-tls/CHANGES.md b/actix-tls/CHANGES.md index a87f0fc5..d47d46ec 100644 --- a/actix-tls/CHANGES.md +++ b/actix-tls/CHANGES.md @@ -1,6 +1,7 @@ # Changes ## Unreleased - 2021-xx-xx +* Rename `accept::openssl::{SslStream => TlsStream}`. ## 3.0.0-beta.3 - 2021-02-06 diff --git a/actix-tls/src/accept/nativetls.rs b/actix-tls/src/accept/nativetls.rs index 15e43c99..98a103a8 100644 --- a/actix-tls/src/accept/nativetls.rs +++ b/actix-tls/src/accept/nativetls.rs @@ -16,7 +16,7 @@ pub use tokio_native_tls::TlsAcceptor; use super::MAX_CONN_COUNTER; -/// wrapper type for `tokio_native_tls::TlsStream` in order to impl `ActixStream` trait. +/// Wrapper type for `tokio_native_tls::TlsStream` in order to impl `ActixStream` trait. pub struct TlsStream(tokio_native_tls::TlsStream); impl From> for TlsStream { diff --git a/actix-tls/src/accept/openssl.rs b/actix-tls/src/accept/openssl.rs index b490888a..f94e3c2d 100644 --- a/actix-tls/src/accept/openssl.rs +++ b/actix-tls/src/accept/openssl.rs @@ -18,16 +18,16 @@ pub use openssl::ssl::{ use super::MAX_CONN_COUNTER; -/// wrapper type for `tokio_openssl::SslStream` in order to impl `ActixStream` trait. -pub struct SslStream(tokio_openssl::SslStream); +/// Wrapper type for `tokio_openssl::SslStream` in order to impl `ActixStream` trait. +pub struct TlsStream(tokio_openssl::SslStream); -impl From> for SslStream { +impl From> for TlsStream { fn from(stream: tokio_openssl::SslStream) -> Self { Self(stream) } } -impl Deref for SslStream { +impl Deref for TlsStream { type Target = tokio_openssl::SslStream; fn deref(&self) -> &Self::Target { @@ -35,13 +35,13 @@ impl Deref for SslStream { } } -impl DerefMut for SslStream { +impl DerefMut for TlsStream { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } -impl AsyncRead for SslStream { +impl AsyncRead for TlsStream { fn poll_read( self: Pin<&mut Self>, cx: &mut Context<'_>, @@ -51,7 +51,7 @@ impl AsyncRead for SslStream { } } -impl AsyncWrite for SslStream { +impl AsyncWrite for TlsStream { fn poll_write( self: Pin<&mut Self>, cx: &mut Context<'_>, @@ -81,7 +81,7 @@ impl AsyncWrite for SslStream { } } -impl ActixStream for SslStream { +impl ActixStream for TlsStream { fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll> { T::poll_read_ready((&**self).get_ref(), cx) } @@ -116,7 +116,7 @@ impl Clone for Acceptor { } impl ServiceFactory for Acceptor { - type Response = SslStream; + type Response = TlsStream; type Error = SslError; type Config = (); type Service = AcceptorService; @@ -140,7 +140,7 @@ pub struct AcceptorService { } impl Service for AcceptorService { - type Response = SslStream; + type Response = TlsStream; type Error = SslError; type Future = AcceptorServiceResponse; @@ -168,7 +168,7 @@ pub struct AcceptorServiceResponse { } impl Future for AcceptorServiceResponse { - type Output = Result, SslError>; + type Output = Result, SslError>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { ready!(Pin::new(self.stream.as_mut().unwrap()).poll_accept(cx))?; diff --git a/actix-tls/src/accept/rustls.rs b/actix-tls/src/accept/rustls.rs index c19a6cec..753d68ac 100644 --- a/actix-tls/src/accept/rustls.rs +++ b/actix-tls/src/accept/rustls.rs @@ -18,7 +18,7 @@ pub use tokio_rustls::rustls::{ServerConfig, Session}; use super::MAX_CONN_COUNTER; -/// wrapper type for `tokio_openssl::SslStream` in order to impl `ActixStream` trait. +/// Wrapper type for `tokio_openssl::SslStream` in order to impl `ActixStream` trait. pub struct TlsStream(tokio_rustls::server::TlsStream); impl From> for TlsStream { From c3be839a6900c4c0fc4734eaed735ca6ac424cb8 Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Wed, 24 Feb 2021 01:13:17 +0800 Subject: [PATCH 05/72] add local_addr binding to connector service --- actix-rt/src/lib.rs | 2 +- actix-tls/src/connect/connect.rs | 22 ++++++++++- actix-tls/src/connect/connector.rs | 59 ++++++++++++++++++++++++++---- actix-tls/tests/test_connect.rs | 27 +++++++++++++- 4 files changed, 98 insertions(+), 12 deletions(-) diff --git a/actix-rt/src/lib.rs b/actix-rt/src/lib.rs index a529bdb0..e21cd651 100644 --- a/actix-rt/src/lib.rs +++ b/actix-rt/src/lib.rs @@ -76,7 +76,7 @@ pub mod net { use tokio::io::{AsyncRead, AsyncWrite}; pub use tokio::net::UdpSocket; - pub use tokio::net::{TcpListener, TcpStream}; + pub use tokio::net::{TcpListener, TcpSocket, TcpStream}; #[cfg(unix)] pub use tokio::net::{UnixDatagram, UnixListener, UnixStream}; diff --git a/actix-tls/src/connect/connect.rs b/actix-tls/src/connect/connect.rs index 9e5d417f..bd4b3fdf 100755 --- a/actix-tls/src/connect/connect.rs +++ b/actix-tls/src/connect/connect.rs @@ -3,7 +3,7 @@ use std::{ fmt, iter::{self, FromIterator as _}, mem, - net::SocketAddr, + net::{IpAddr, SocketAddr}, }; /// Parse a host into parts (hostname and port). @@ -67,6 +67,7 @@ pub struct Connect { pub(crate) req: T, pub(crate) port: u16, pub(crate) addr: ConnectAddrs, + pub(crate) local_addr: Option, } impl Connect { @@ -78,6 +79,7 @@ impl Connect { req, port: port.unwrap_or(0), addr: ConnectAddrs::None, + local_addr: None, } } @@ -88,6 +90,7 @@ impl Connect { req, port: 0, addr: ConnectAddrs::One(addr), + local_addr: None, } } @@ -119,6 +122,12 @@ impl Connect { self } + /// Set local_addr of connect. + pub fn set_local_addr(mut self, addr: impl Into) -> Self { + self.local_addr = Some(addr.into()); + self + } + /// Get hostname. pub fn hostname(&self) -> &str { self.req.hostname() @@ -285,7 +294,7 @@ fn parse_host(host: &str) -> (&str, Option) { #[cfg(test)] mod tests { - use std::net::{IpAddr, Ipv4Addr}; + use std::net::Ipv4Addr; use super::*; @@ -329,4 +338,13 @@ mod tests { let mut iter = ConnectAddrsIter::None; assert_eq!(iter.next(), None); } + + #[test] + fn test_local_addr() { + let conn = Connect::new("hello").set_local_addr([127, 0, 0, 1]); + assert_eq!( + conn.local_addr.unwrap(), + IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)) + ) + } } diff --git a/actix-tls/src/connect/connector.rs b/actix-tls/src/connect/connector.rs index 9acb1dd5..4d98ba78 100755 --- a/actix-tls/src/connect/connector.rs +++ b/actix-tls/src/connect/connector.rs @@ -2,12 +2,12 @@ use std::{ collections::VecDeque, future::Future, io, - net::SocketAddr, + net::{IpAddr, SocketAddr, SocketAddrV4, SocketAddrV6}, pin::Pin, task::{Context, Poll}, }; -use actix_rt::net::TcpStream; +use actix_rt::net::{TcpSocket, TcpStream}; use actix_service::{Service, ServiceFactory}; use futures_core::{future::LocalBoxFuture, ready}; use log::{error, trace}; @@ -54,9 +54,14 @@ impl Service> for TcpConnector { fn call(&self, req: Connect) -> Self::Future { let port = req.port(); - let Connect { req, addr, .. } = req; + let Connect { + req, + addr, + local_addr, + .. + } = req; - TcpConnectorResponse::new(req, port, addr) + TcpConnectorResponse::new(req, port, local_addr, addr) } } @@ -65,6 +70,7 @@ pub enum TcpConnectorResponse { Response { req: Option, port: u16, + local_addr: Option, addrs: Option>, stream: Option>>, }, @@ -72,7 +78,12 @@ pub enum TcpConnectorResponse { } impl TcpConnectorResponse { - pub(crate) fn new(req: T, port: u16, addr: ConnectAddrs) -> TcpConnectorResponse { + pub(crate) fn new( + req: T, + port: u16, + local_addr: Option, + addr: ConnectAddrs, + ) -> TcpConnectorResponse { if addr.is_none() { error!("TCP connector: unresolved connection address"); return TcpConnectorResponse::Error(Some(ConnectError::Unresolved)); @@ -90,8 +101,9 @@ impl TcpConnectorResponse { ConnectAddrs::One(addr) => TcpConnectorResponse::Response { req: Some(req), port, + local_addr, addrs: None, - stream: Some(ReusableBoxFuture::new(TcpStream::connect(addr))), + stream: Some(ReusableBoxFuture::new(connect(addr, local_addr))), }, // when resolver returns multiple socket addr for request they would be popped from @@ -99,6 +111,7 @@ impl TcpConnectorResponse { ConnectAddrs::Multi(addrs) => TcpConnectorResponse::Response { req: Some(req), port, + local_addr, addrs: Some(addrs), stream: None, }, @@ -116,6 +129,7 @@ impl Future for TcpConnectorResponse { TcpConnectorResponse::Response { req, port, + local_addr, addrs, stream, } => loop { @@ -148,11 +162,40 @@ impl Future for TcpConnectorResponse { // try to connect let addr = addrs.as_mut().unwrap().pop_front().unwrap(); + let fut = connect(addr, *local_addr); match stream { - Some(rbf) => rbf.set(TcpStream::connect(addr)), - None => *stream = Some(ReusableBoxFuture::new(TcpStream::connect(addr))), + Some(rbf) => rbf.set(fut), + None => *stream = Some(ReusableBoxFuture::new(fut)), } }, } } } + +async fn connect(addr: SocketAddr, local_addr: Option) -> io::Result { + // use local addr if connect asks for it. + match local_addr { + Some(ip_addr) => { + let socket = match ip_addr { + IpAddr::V4(ip_addr) => { + let socket = TcpSocket::new_v4()?; + let addr = SocketAddr::V4(SocketAddrV4::new(ip_addr, 0)); + socket.bind(addr)?; + socket + } + IpAddr::V6(ip_addr) => { + let socket = TcpSocket::new_v6()?; + let addr = SocketAddr::V6(SocketAddrV6::new(ip_addr, 0, 0, 0)); + socket.bind(addr)?; + socket + } + }; + + socket.set_reuseaddr(true)?; + + socket.connect(addr).await + } + + None => TcpStream::connect(addr).await, + } +} diff --git a/actix-tls/tests/test_connect.rs b/actix-tls/tests/test_connect.rs index 7ee7afda..e8e23757 100755 --- a/actix-tls/tests/test_connect.rs +++ b/actix-tls/tests/test_connect.rs @@ -1,4 +1,7 @@ -use std::io; +use std::{ + io, + net::{IpAddr, Ipv4Addr}, +}; use actix_codec::{BytesCodec, Framed}; use actix_rt::net::TcpStream; @@ -125,3 +128,25 @@ async fn test_rustls_uri() { let con = conn.call(addr.into()).await.unwrap(); assert_eq!(con.peer_addr().unwrap(), srv.addr()); } + +#[actix_rt::test] +async fn test_local_addr() { + 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 conn = actix_connect::default_connector(); + let local = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 3)); + + let (con, _) = conn + .call(Connect::with_addr("10", srv.addr()).set_local_addr(local)) + .await + .unwrap() + .into_parts(); + + assert_eq!(con.local_addr().unwrap().ip(), local) +} From 17f711a9d659a2bb6a598688243aae1f3ba21957 Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Wed, 24 Feb 2021 01:20:01 +0800 Subject: [PATCH 06/72] update changelog --- actix-rt/CHANGES.md | 2 ++ actix-tls/CHANGES.md | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/actix-rt/CHANGES.md b/actix-rt/CHANGES.md index 59a4641c..db9e538b 100644 --- a/actix-rt/CHANGES.md +++ b/actix-rt/CHANGES.md @@ -2,8 +2,10 @@ ## Unreleased - 2021-xx-xx * Add `ActixStream` extension trait to include readiness methods. [#276] +* Re-export `tokio::net::TcpSocket` in `net` module [#282] [#276]: https://github.com/actix/actix-net/pull/276 +[#282]: https://github.com/actix/actix-net/pull/282 ## 2.0.2 - 2021-02-06 diff --git a/actix-tls/CHANGES.md b/actix-tls/CHANGES.md index d47d46ec..352e79fa 100644 --- a/actix-tls/CHANGES.md +++ b/actix-tls/CHANGES.md @@ -2,6 +2,10 @@ ## Unreleased - 2021-xx-xx * Rename `accept::openssl::{SslStream => TlsStream}`. +* Add `connect::Connect::set_local_addr` to attach local `Ipaddr`. [#282] +* `connector::TcpConnector` service would try to bind to local_addr of `IpAddr` when given [#282] + +[#282]: https://github.com/actix/actix-net/pull/282 ## 3.0.0-beta.3 - 2021-02-06 From a6e79453d0bc8f25d971954cfb60f6719df9fb54 Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Wed, 24 Feb 2021 02:26:11 +0800 Subject: [PATCH 07/72] remove default reuse_addr --- actix-tls/src/connect/connector.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/actix-tls/src/connect/connector.rs b/actix-tls/src/connect/connector.rs index 4d98ba78..8f32270f 100755 --- a/actix-tls/src/connect/connector.rs +++ b/actix-tls/src/connect/connector.rs @@ -191,8 +191,6 @@ async fn connect(addr: SocketAddr, local_addr: Option) -> io::Result Date: Wed, 24 Feb 2021 01:39:02 +0000 Subject: [PATCH 08/72] doc nits --- actix-server/Cargo.toml | 1 - actix-server/src/waker_queue.rs | 14 +++++++------- actix-service/src/lib.rs | 4 ++-- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/actix-server/Cargo.toml b/actix-server/Cargo.toml index 9d13e456..4d153406 100755 --- a/actix-server/Cargo.toml +++ b/actix-server/Cargo.toml @@ -12,7 +12,6 @@ repository = "https://github.com/actix/actix-net.git" documentation = "https://docs.rs/actix-server" categories = ["network-programming", "asynchronous"] license = "MIT OR Apache-2.0" -exclude = [".gitignore", ".cargo/config"] edition = "2018" [lib] diff --git a/actix-server/src/waker_queue.rs b/actix-server/src/waker_queue.rs index f92363b5..e38a9782 100644 --- a/actix-server/src/waker_queue.rs +++ b/actix-server/src/waker_queue.rs @@ -8,7 +8,7 @@ use mio::{Registry, Token as MioToken, Waker}; use crate::worker::WorkerHandle; -/// waker token for `mio::Poll` instance +/// 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` @@ -30,7 +30,7 @@ impl Deref for WakerQueue { } impl WakerQueue { - /// construct a waker queue with given `Poll`'s `Registry` and capacity. + /// 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`. @@ -41,7 +41,7 @@ impl WakerQueue { Ok(Self(Arc::new((waker, queue)))) } - /// push a new interest to the queue and wake up the accept poll afterwards. + /// 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(); @@ -55,20 +55,20 @@ impl WakerQueue { .unwrap_or_else(|e| panic!("can not wake up Accept Poll: {}", e)); } - /// get a MutexGuard of the waker queue. + /// 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. + /// 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. +/// 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 +/// 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. diff --git a/actix-service/src/lib.rs b/actix-service/src/lib.rs index 7c3a271c..e591eb51 100644 --- a/actix-service/src/lib.rs +++ b/actix-service/src/lib.rs @@ -102,8 +102,8 @@ pub trait Service { /// call and the next invocation of `call` results in an error. /// /// # 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. + /// 1. `poll_ready` might be called on a different task to `call`. + /// 1. In cases of chained services, `.poll_ready()` is called for all services at once. fn poll_ready(&self, ctx: &mut task::Context<'_>) -> Poll>; /// Process the request and return the response asynchronously. From fa8ded3a348f3a0b6a615840c99397dd34a6add4 Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Wed, 24 Feb 2021 15:54:28 +0800 Subject: [PATCH 09/72] bump tokio version for actix-server --- 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 4d153406..0a395797 100755 --- a/actix-server/Cargo.toml +++ b/actix-server/Cargo.toml @@ -32,7 +32,7 @@ log = "0.4" mio = { version = "0.7.6", features = ["os-poll", "net"] } num_cpus = "1.13" slab = "0.4" -tokio = { version = "1", features = ["sync"] } +tokio = { version = "1.2", features = ["sync"] } [dev-dependencies] actix-rt = "2.0.0" From 789e6a8a4689ca0d098ae2a87f81a86248f30187 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Wed, 24 Feb 2021 09:48:41 +0000 Subject: [PATCH 10/72] update ci (#284) --- .github/workflows/ci.yml | 103 ++++++++++++++++++++++++++++ .github/workflows/clippy-fmt.yml | 28 +++++--- .github/workflows/linux.yml | 82 ---------------------- .github/workflows/macos.yml | 43 ------------ .github/workflows/upload-doc.yml | 35 ++++++++++ .github/workflows/windows-mingw.yml | 45 ------------ .github/workflows/windows.yml | 69 ------------------- actix-router/src/resource.rs | 7 +- actix-tls/Cargo.toml | 8 ++- actix-tls/tests/test_connect.rs | 4 +- actix-tls/tests/test_resolvers.rs | 2 + 11 files changed, 172 insertions(+), 254 deletions(-) create mode 100644 .github/workflows/ci.yml delete mode 100644 .github/workflows/linux.yml delete mode 100644 .github/workflows/macos.yml create mode 100644 .github/workflows/upload-doc.yml delete mode 100644 .github/workflows/windows-mingw.yml delete mode 100644 .github/workflows/windows.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..3945573e --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,103 @@ +name: CI + +on: + pull_request: + types: [opened, synchronize, reopened] + push: + branches: [master] + +jobs: + build_and_test: + strategy: + fail-fast: false + matrix: + target: + - { name: Linux, os: ubuntu-latest, triple: x86_64-unknown-linux-gnu } + - { name: macOS, os: macos-latest, triple: x86_64-apple-darwin } + - { name: Windows, os: windows-latest, triple: x86_64-pc-windows-msvc } + - { name: Windows (MinGW), os: windows-latest, triple: x86_64-pc-windows-gnu } + - { name: Windows (32-bit), os: windows-latest, triple: i686-pc-windows-msvc } + version: + - 1.46.0 # MSRV + - stable + - nightly + + name: ${{ matrix.target.name }} / ${{ matrix.version }} + runs-on: ${{ matrix.target.os }} + + steps: + - name: Setup Routing + if: matrix.target.os == 'macos-latest' + run: sudo ifconfig lo0 alias 127.0.0.3 + + - uses: actions/checkout@v2 + + - name: Install ${{ matrix.version }} + uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ matrix.version }}-${{ matrix.target.triple }} + profile: minimal + override: true + + - name: Install MSYS2 + if: matrix.target.triple == 'x86_64-pc-windows-gnu' + uses: msys2/setup-msys2@v2 + - name: Install MinGW Packages + if: matrix.target.triple == 'x86_64-pc-windows-gnu' + run: | + msys2 -c 'pacman -Sy --noconfirm pacman' + msys2 -c 'pacman --noconfirm -S base-devel pkg-config' + + - name: Generate Cargo.lock + uses: actions-rs/cargo@v1 + with: + command: generate-lockfile + - name: Cache Dependencies + uses: Swatinem/rust-cache@v1.2.0 + + - name: Install cargo-hack + uses: actions-rs/cargo@v1 + with: + command: install + args: cargo-hack + + - name: check minimal + uses: actions-rs/cargo@v1 + with: + command: hack + args: --clean-per-run check --workspace --no-default-features --tests + + - name: check full + uses: actions-rs/cargo@v1 + with: + command: check + args: --workspace --bins --examples --tests + + - name: tests + if: matrix.target.triple != 'x86_64-pc-windows-gnu' + uses: actions-rs/cargo@v1 + with: + command: test + args: --workspace --all-features --no-fail-fast -- --nocapture + + - name: Generate coverage file + if: > + matrix.target.os == 'ubuntu-latest' + && matrix.version == 'stable' + && github.ref == 'refs/heads/master' + run: | + cargo install cargo-tarpaulin + cargo tarpaulin --out Xml --verbose + - name: Upload to Codecov + if: > + matrix.target.os == 'ubuntu-latest' + && matrix.version == 'stable' + && github.ref == 'refs/heads/master' + uses: codecov/codecov-action@v1 + with: + file: cobertura.xml + + - name: Clear the cargo caches + run: | + cargo install cargo-cache --no-default-features --features ci-autoclean + cargo-cache diff --git a/.github/workflows/clippy-fmt.yml b/.github/workflows/clippy-fmt.yml index 12343dd4..3bef81db 100644 --- a/.github/workflows/clippy-fmt.yml +++ b/.github/workflows/clippy-fmt.yml @@ -1,34 +1,42 @@ +name: Lint + on: pull_request: types: [opened, synchronize, reopened] -name: Clippy and rustfmt Check jobs: - clippy_check: + fmt: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 + - name: Install Rust + uses: actions-rs/toolchain@v1 with: toolchain: stable - components: rustfmt profile: minimal + components: rustfmt override: true - - name: Check with rustfmt + - name: Rustfmt Check uses: actions-rs/cargo@v1 with: command: fmt args: --all -- --check - - uses: actions-rs/toolchain@v1 + clippy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - name: Install Rust + uses: actions-rs/toolchain@v1 with: - toolchain: nightly - components: clippy + toolchain: stable profile: minimal + components: clippy override: true - - name: Check with Clippy + - name: Clippy Check uses: actions-rs/clippy-check@v1 with: token: ${{ secrets.GITHUB_TOKEN }} - args: --workspace --tests + args: --workspace --tests --all-features diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml deleted file mode 100644 index 8ea7823d..00000000 --- a/.github/workflows/linux.yml +++ /dev/null @@ -1,82 +0,0 @@ -name: CI (Linux) - -on: - pull_request: - types: [opened, synchronize, reopened] - push: - branches: - - master - - '1.0' - -jobs: - build_and_test: - strategy: - fail-fast: false - matrix: - version: - - 1.46.0 - - stable - - nightly - - name: ${{ matrix.version }} - x86_64-unknown-linux-gnu - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - - name: Install ${{ matrix.version }} - uses: actions-rs/toolchain@v1 - with: - toolchain: ${{ matrix.version }}-x86_64-unknown-linux-gnu - profile: minimal - override: true - - - name: Generate Cargo.lock - uses: actions-rs/cargo@v1 - with: - command: generate-lockfile - - name: Cache cargo dirs - uses: actions/cache@v2 - with: - path: - ~/.cargo/registry - ~/.cargo/git - ~/.cargo/bin - key: ${{ matrix.version }}-x86_64-unknown-linux-gnu-cargo-trimmed-${{ hashFiles('**/Cargo.lock') }} - - name: Cache cargo build - uses: actions/cache@v2 - with: - path: target - key: ${{ matrix.version }}-x86_64-unknown-linux-gnu-cargo-build-trimmed-${{ hashFiles('**/Cargo.lock') }} - - - name: check build - uses: actions-rs/cargo@v1 - with: - command: check - args: --workspace --bins --examples --tests - - - name: tests - uses: actions-rs/cargo@v1 - timeout-minutes: 40 - with: - command: test - args: --workspace --exclude=actix-tls --no-fail-fast -- --nocapture - - - name: Generate coverage file - if: matrix.version == 'stable' && (github.ref == 'refs/heads/master' || github.event_name == 'pull_request') - run: | - cargo install cargo-tarpaulin - cargo tarpaulin --out Xml --workspace - - - name: Upload to Codecov - if: matrix.version == 'stable' && (github.ref == 'refs/heads/master' || github.event_name == 'pull_request') - uses: codecov/codecov-action@v1 - with: - file: cobertura.xml - - - name: Clear the cargo caches - run: | - rustup update stable - rustup override set stable - cargo install cargo-cache --no-default-features --features ci-autoclean - cargo-cache diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml deleted file mode 100644 index b2555bd3..00000000 --- a/.github/workflows/macos.yml +++ /dev/null @@ -1,43 +0,0 @@ -name: CI (macOS) - -on: - pull_request: - types: [opened, synchronize, reopened] - push: - branches: - - master - - '1.0' - -jobs: - build_and_test: - strategy: - fail-fast: false - matrix: - version: - - stable - - nightly - - name: ${{ matrix.version }} - x86_64-apple-darwin - runs-on: macos-latest - - steps: - - uses: actions/checkout@v2 - - - name: Install ${{ matrix.version }} - uses: actions-rs/toolchain@v1 - with: - toolchain: ${{ matrix.version }}-x86_64-apple-darwin - profile: minimal - override: true - - - name: check build - uses: actions-rs/cargo@v1 - with: - command: check - args: --workspace --bins --examples --tests - - - name: tests - uses: actions-rs/cargo@v1 - with: - command: test - args: --workspace --exclude=actix-tls --no-fail-fast -- --nocapture diff --git a/.github/workflows/upload-doc.yml b/.github/workflows/upload-doc.yml new file mode 100644 index 00000000..36044230 --- /dev/null +++ b/.github/workflows/upload-doc.yml @@ -0,0 +1,35 @@ +name: Upload documentation + +on: + push: + branches: [master] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: Install Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: nightly-x86_64-unknown-linux-gnu + profile: minimal + override: true + + - name: Build Docs + uses: actions-rs/cargo@v1 + with: + command: doc + args: --workspace --all-features --no-deps + + - name: Tweak HTML + run: echo '' > target/doc/index.html + + - name: Deploy to GitHub Pages + uses: JamesIves/github-pages-deploy-action@3.7.1 + with: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + BRANCH: gh-pages + FOLDER: target/doc diff --git a/.github/workflows/windows-mingw.yml b/.github/workflows/windows-mingw.yml deleted file mode 100644 index 1fd5fc59..00000000 --- a/.github/workflows/windows-mingw.yml +++ /dev/null @@ -1,45 +0,0 @@ -name: CI (Windows-mingw) - -on: - pull_request: - types: [opened, synchronize, reopened] - push: - branches: - - master - - '1.0' - -jobs: - build_and_test: - strategy: - fail-fast: false - matrix: - version: - - stable - - nightly - - name: ${{ matrix.version }} - x86_64-pc-windows-gnu - runs-on: windows-latest - - steps: - - uses: actions/checkout@v2 - - - name: Install ${{ matrix.version }} - uses: actions-rs/toolchain@v1 - with: - toolchain: ${{ matrix.version }}-x86_64-pc-windows-gnu - profile: minimal - override: true - - - name: Install MSYS2 - uses: msys2/setup-msys2@v2 - - - name: Install packages - run: | - msys2 -c 'pacman -Sy --noconfirm pacman' - msys2 -c 'pacman --noconfirm -S base-devel pkg-config' - - - name: check build - uses: actions-rs/cargo@v1 - with: - command: check - args: --workspace --bins --examples --tests diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml deleted file mode 100644 index b2b57989..00000000 --- a/.github/workflows/windows.yml +++ /dev/null @@ -1,69 +0,0 @@ -name: CI (Windows) - -on: - pull_request: - types: [opened, synchronize, reopened] - push: - branches: - - master - - '1.0' - -env: - VCPKGRS_DYNAMIC: 1 - -jobs: - build_and_test: - strategy: - fail-fast: false - matrix: - version: - - stable - - nightly - target: - - x86_64-pc-windows-msvc - - i686-pc-windows-msvc - - name: ${{ matrix.version }} - ${{ matrix.target }} - runs-on: windows-latest - - steps: - - uses: actions/checkout@v2 - - - name: Install ${{ matrix.version }} - uses: actions-rs/toolchain@v1 - with: - toolchain: ${{ matrix.version }}-${{ matrix.target }} - profile: minimal - override: true - - - name: Install OpenSSL (x64) - if: matrix.target == 'x86_64-pc-windows-msvc' - run: | - vcpkg integrate install - vcpkg install openssl:x64-windows - Get-ChildItem C:\vcpkg\installed\x64-windows\bin - Get-ChildItem C:\vcpkg\installed\x64-windows\lib - Copy-Item C:\vcpkg\installed\x64-windows\bin\libcrypto-1_1-x64.dll C:\vcpkg\installed\x64-windows\bin\libcrypto.dll - Copy-Item C:\vcpkg\installed\x64-windows\bin\libssl-1_1-x64.dll C:\vcpkg\installed\x64-windows\bin\libssl.dll - - - name: Install OpenSSL (x86) - if: matrix.target == 'i686-pc-windows-msvc' - run: | - vcpkg integrate install - vcpkg install openssl:x86-windows - Get-ChildItem C:\vcpkg\installed\x86-windows\bin - Get-ChildItem C:\vcpkg\installed\x86-windows\lib - Copy-Item C:\vcpkg\installed\x86-windows\bin\libcrypto-1_1.dll C:\vcpkg\installed\x86-windows\bin\libcrypto.dll - Copy-Item C:\vcpkg\installed\x86-windows\bin\libssl-1_1.dll C:\vcpkg\installed\x86-windows\bin\libssl.dll - - - name: check build - uses: actions-rs/cargo@v1 - with: - command: check - args: --workspace --bins --examples --tests - - - name: tests - uses: actions-rs/cargo@v1 - with: - command: test - args: --workspace --exclude=actix-tls --no-fail-fast -- --nocapture diff --git a/actix-router/src/resource.rs b/actix-router/src/resource.rs index 8dbef26c..98b4a709 100644 --- a/actix-router/src/resource.rs +++ b/actix-router/src/resource.rs @@ -670,8 +670,6 @@ pub(crate) fn insert_slash(path: &str) -> String { #[cfg(test)] mod tests { use super::*; - use http::Uri; - use std::convert::TryFrom; #[test] fn test_parse_static() { @@ -833,8 +831,11 @@ mod tests { assert!(re.is_match("/user/2345/sdg")); } + #[cfg(feature = "http")] #[test] fn test_parse_urlencoded_param() { + use std::convert::TryFrom; + let re = ResourceDef::new("/user/{id}/test"); let mut path = Path::new("/user/2345/test"); @@ -845,7 +846,7 @@ mod tests { assert!(re.match_path(&mut path)); assert_eq!(path.get("id").unwrap(), "qwe%25"); - let uri = Uri::try_from("/user/qwe%25/test").unwrap(); + let uri = http::Uri::try_from("/user/qwe%25/test").unwrap(); let mut path = Path::new(uri); assert!(re.match_path(&mut path)); assert_eq!(path.get("id").unwrap(), "qwe%25"); diff --git a/actix-tls/Cargo.toml b/actix-tls/Cargo.toml index db79d6ab..92f04e06 100755 --- a/actix-tls/Cargo.toml +++ b/actix-tls/Cargo.toml @@ -52,7 +52,7 @@ log = "0.4" tokio-util = { version = "0.6.3", default-features = false } # openssl -tls-openssl = { package = "openssl", version = "0.10", optional = true } +tls-openssl = { package = "openssl", version = "0.10.9", optional = true } tokio-openssl = { version = "0.6", optional = true } # rustls @@ -62,6 +62,12 @@ webpki-roots = { version = "0.21", optional = true } # native-tls tokio-native-tls = { version = "0.3", optional = true } +[target.'cfg(windows)'.dependencies.tls-openssl] +version = "0.10.9" +package = "openssl" +features = ["vendored"] +optional = true + [dev-dependencies] actix-rt = "2.0.0" actix-server = "2.0.0-beta.3" diff --git a/actix-tls/tests/test_connect.rs b/actix-tls/tests/test_connect.rs index e8e23757..564151ce 100755 --- a/actix-tls/tests/test_connect.rs +++ b/actix-tls/tests/test_connect.rs @@ -1,3 +1,5 @@ +#![cfg(feature = "connect")] + use std::{ io, net::{IpAddr, Ipv4Addr}, @@ -12,7 +14,7 @@ use futures_util::sink::SinkExt; use actix_tls::connect::{self as actix_connect, Connect}; -#[cfg(all(feature = "connect", feature = "openssl"))] +#[cfg(feature = "openssl")] #[actix_rt::test] async fn test_string() { let srv = TestServer::with(|| { diff --git a/actix-tls/tests/test_resolvers.rs b/actix-tls/tests/test_resolvers.rs index 0f49c486..40ee21fa 100644 --- a/actix-tls/tests/test_resolvers.rs +++ b/actix-tls/tests/test_resolvers.rs @@ -1,3 +1,5 @@ +#![cfg(feature = "connect")] + use std::{ io, net::{Ipv4Addr, SocketAddr}, From 06ddad00514ffef8638cc859e532891570808063 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Thu, 25 Feb 2021 11:50:24 +0000 Subject: [PATCH 11/72] prepare rt and tls releases (#287) --- .github/workflows/ci.yml | 48 ++++++++++++------- actix-rt/CHANGES.md | 3 ++ actix-rt/Cargo.toml | 4 +- actix-rt/README.md | 9 ++++ .../examples/{basic.rs => tcp-echo.rs} | 0 actix-tls/CHANGES.md | 7 ++- actix-tls/Cargo.toml | 8 ++-- .../examples/{basic.rs => tcp-rustls.rs} | 0 8 files changed, 54 insertions(+), 25 deletions(-) rename actix-server/examples/{basic.rs => tcp-echo.rs} (100%) rename actix-tls/examples/{basic.rs => tcp-rustls.rs} (100%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3945573e..7b5b47c4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,21 +39,21 @@ jobs: profile: minimal override: true - - name: Install MSYS2 - if: matrix.target.triple == 'x86_64-pc-windows-gnu' - uses: msys2/setup-msys2@v2 - - name: Install MinGW Packages - if: matrix.target.triple == 'x86_64-pc-windows-gnu' - run: | - msys2 -c 'pacman -Sy --noconfirm pacman' - msys2 -c 'pacman --noconfirm -S base-devel pkg-config' + # - name: Install MSYS2 + # if: matrix.target.triple == 'x86_64-pc-windows-gnu' + # uses: msys2/setup-msys2@v2 + # - name: Install MinGW Packages + # if: matrix.target.triple == 'x86_64-pc-windows-gnu' + # run: | + # msys2 -c 'pacman -Sy --noconfirm pacman' + # msys2 -c 'pacman --noconfirm -S base-devel pkg-config' - - name: Generate Cargo.lock - uses: actions-rs/cargo@v1 - with: - command: generate-lockfile - - name: Cache Dependencies - uses: Swatinem/rust-cache@v1.2.0 + # - name: Generate Cargo.lock + # uses: actions-rs/cargo@v1 + # with: + # command: generate-lockfile + # - name: Cache Dependencies + # uses: Swatinem/rust-cache@v1.2.0 - name: Install cargo-hack uses: actions-rs/cargo@v1 @@ -65,13 +65,27 @@ jobs: uses: actions-rs/cargo@v1 with: command: hack - args: --clean-per-run check --workspace --no-default-features --tests + args: check --workspace --no-default-features - - name: check full + - name: check minimal + tests + uses: actions-rs/cargo@v1 + with: + command: hack + args: check --workspace --no-default-features --tests --examples + + - name: check default uses: actions-rs/cargo@v1 with: command: check - args: --workspace --bins --examples --tests + args: --workspace --tests --examples + + - name: check full + # TODO: compile OpenSSL and run tests on MinGW + if: matrix.target.triple != 'x86_64-pc-windows-gnu' + uses: actions-rs/cargo@v1 + with: + command: check + args: --workspace --all-features --tests --examples - name: tests if: matrix.target.triple != 'x86_64-pc-windows-gnu' diff --git a/actix-rt/CHANGES.md b/actix-rt/CHANGES.md index db9e538b..1fd7b25b 100644 --- a/actix-rt/CHANGES.md +++ b/actix-rt/CHANGES.md @@ -1,6 +1,9 @@ # Changes ## Unreleased - 2021-xx-xx + + +## 2.1.0 - 2021-02-24 * Add `ActixStream` extension trait to include readiness methods. [#276] * Re-export `tokio::net::TcpSocket` in `net` module [#282] diff --git a/actix-rt/Cargo.toml b/actix-rt/Cargo.toml index 7990e67d..92f10b85 100644 --- a/actix-rt/Cargo.toml +++ b/actix-rt/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-rt" -version = "2.0.2" +version = "2.1.0" authors = [ "Nikolay Kim ", "Rob Ede ", @@ -8,7 +8,7 @@ authors = [ description = "Tokio-based single-threaded async runtime for the Actix ecosystem" keywords = ["async", "futures", "io", "runtime"] homepage = "https://actix.rs" -repository = "https://github.com/actix/actix-net.git" +repository = "https://github.com/actix/actix-net" documentation = "https://docs.rs/actix-rt" categories = ["network-programming", "asynchronous"] license = "MIT OR Apache-2.0" diff --git a/actix-rt/README.md b/actix-rt/README.md index c29d563d..f9a3ed31 100644 --- a/actix-rt/README.md +++ b/actix-rt/README.md @@ -2,4 +2,13 @@ > Tokio-based single-threaded async runtime for the Actix ecosystem. +[![crates.io](https://img.shields.io/crates/v/actix-rt?label=latest)](https://crates.io/crates/actix-rt) +[![Documentation](https://docs.rs/actix-rt/badge.svg?version=2.1.0)](https://docs.rs/actix-rt/2.1.0) +[![Version](https://img.shields.io/badge/rustc-1.46+-ab6000.svg)](https://blog.rust-lang.org/2020/03/12/Rust-1.46.html) +![MIT or Apache 2.0 licensed](https://img.shields.io/crates/l/actix-rt.svg) +
+[![dependency status](https://deps.rs/crate/actix-rt/2.1.0/status.svg)](https://deps.rs/crate/actix-rt/2.1.0) +![Download](https://img.shields.io/crates/d/actix-rt.svg) +[![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/WghFtEH6Hb) + See crate documentation for more: https://docs.rs/actix-rt. diff --git a/actix-server/examples/basic.rs b/actix-server/examples/tcp-echo.rs similarity index 100% rename from actix-server/examples/basic.rs rename to actix-server/examples/tcp-echo.rs diff --git a/actix-tls/CHANGES.md b/actix-tls/CHANGES.md index 352e79fa..824663b0 100644 --- a/actix-tls/CHANGES.md +++ b/actix-tls/CHANGES.md @@ -1,9 +1,12 @@ # Changes ## Unreleased - 2021-xx-xx + + +## 3.0.0-beta.4 - 2021-02-24 * Rename `accept::openssl::{SslStream => TlsStream}`. -* Add `connect::Connect::set_local_addr` to attach local `Ipaddr`. [#282] -* `connector::TcpConnector` service would try to bind to local_addr of `IpAddr` when given [#282] +* Add `connect::Connect::set_local_addr` to attach local `IpAddr`. [#282] +* `connector::TcpConnector` service will try to bind to local_addr of `IpAddr` when given. [#282] [#282]: https://github.com/actix/actix-net/pull/282 diff --git a/actix-tls/Cargo.toml b/actix-tls/Cargo.toml index 92f04e06..b3a0e30c 100755 --- a/actix-tls/Cargo.toml +++ b/actix-tls/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-tls" -version = "3.0.0-beta.3" +version = "3.0.0-beta.4" authors = ["Nikolay Kim "] description = "TLS acceptor and connector services for Actix ecosystem" keywords = ["network", "tls", "ssl", "async", "transport"] @@ -41,7 +41,7 @@ uri = ["http"] [dependencies] actix-codec = "0.4.0-beta.1" -actix-rt = { version = "2.0.0", default-features = false } +actix-rt = { version = "2.1.0", default-features = false } actix-service = "2.0.0-beta.4" actix-utils = "3.0.0-beta.2" @@ -69,7 +69,7 @@ features = ["vendored"] optional = true [dev-dependencies] -actix-rt = "2.0.0" +actix-rt = "2.1.0" actix-server = "2.0.0-beta.3" bytes = "1" env_logger = "0.8" @@ -78,5 +78,5 @@ log = "0.4" trust-dns-resolver = "0.20.0" [[example]] -name = "basic" +name = "tcp-rustls" required-features = ["accept", "rustls"] diff --git a/actix-tls/examples/basic.rs b/actix-tls/examples/tcp-rustls.rs similarity index 100% rename from actix-tls/examples/basic.rs rename to actix-tls/examples/tcp-rustls.rs From 50a195e9ce9d8deb7df550164843d97595cf85bd Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Sun, 28 Feb 2021 11:42:11 -0800 Subject: [PATCH 12/72] add impl Service for Rc (#288) --- actix-service/src/lib.rs | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/actix-service/src/lib.rs b/actix-service/src/lib.rs index e591eb51..66132961 100644 --- a/actix-service/src/lib.rs +++ b/actix-service/src/lib.rs @@ -150,6 +150,7 @@ pub trait ServiceFactory { fn new_service(&self, cfg: Self::Config) -> Self::Future; } +// TODO: remove implement on mut reference. impl<'a, S, Req> Service for &'a mut S where S: Service + 'a, @@ -167,6 +168,23 @@ where } } +impl<'a, S, Req> Service for &'a S +where + S: Service + 'a, +{ + type Response = S::Response; + type Error = S::Error; + type Future = S::Future; + + fn poll_ready(&self, ctx: &mut Context<'_>) -> Poll> { + (**self).poll_ready(ctx) + } + + fn call(&self, request: Req) -> S::Future { + (**self).call(request) + } +} + impl Service for Box where S: Service + ?Sized, @@ -184,7 +202,7 @@ where } } -impl Service for RefCell +impl Service for Rc where S: Service, { @@ -193,15 +211,15 @@ where type Future = S::Future; fn poll_ready(&self, ctx: &mut Context<'_>) -> Poll> { - self.borrow().poll_ready(ctx) + (&**self).poll_ready(ctx) } fn call(&self, request: Req) -> S::Future { - self.borrow().call(request) + (&**self).call(request) } } -impl Service for Rc> +impl Service for RefCell where S: Service, { From 493a1a32c08c59c205104b4bfbaaff098306b94b Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Sun, 28 Feb 2021 11:54:57 -0800 Subject: [PATCH 13/72] rc service changelog (#289) --- actix-service/CHANGES.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/actix-service/CHANGES.md b/actix-service/CHANGES.md index f5da9d2e..20fb2cf8 100644 --- a/actix-service/CHANGES.md +++ b/actix-service/CHANGES.md @@ -1,6 +1,9 @@ # Changes ## Unreleased - 2021-xx-xx +* Add default `Service` trait impl for `Rc` and `&S: Service`. [#288] + +[#288]: https://github.com/actix/actix-net/pull/288 ## 2.0.0-beta.4 - 2021-02-04 From 382830a37e9810769690642a98215e2041296b87 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sun, 28 Feb 2021 21:11:16 +0000 Subject: [PATCH 14/72] refactor dispatcher / add Receiver::recv (#286) --- actix-utils/CHANGES.md | 9 ++++ actix-utils/src/dispatcher.rs | 28 +++++----- actix-utils/src/lib.rs | 3 ++ actix-utils/src/mpsc.rs | 97 +++++++++++++++++++++++------------ actix-utils/src/poll_fn.rs | 65 +++++++++++++++++++++++ actix-utils/src/task.rs | 5 +- 6 files changed, 157 insertions(+), 50 deletions(-) create mode 100644 actix-utils/src/poll_fn.rs diff --git a/actix-utils/CHANGES.md b/actix-utils/CHANGES.md index a7871612..8d97b741 100644 --- a/actix-utils/CHANGES.md +++ b/actix-utils/CHANGES.md @@ -1,6 +1,15 @@ # Changes ## Unreleased - 2021-xx-xx +* Add `async fn mpsc::Receiver::recv`. [#286] +* `SendError` inner field is now public. [#286] +* Rename `Dispatcher::{get_sink => tx}`. [#286] +* Rename `Dispatcher::{get_ref => service}`. [#286] +* Rename `Dispatcher::{get_mut => service_mut}`. [#286] +* Rename `Dispatcher::{get_framed => framed}`. [#286] +* Rename `Dispatcher::{get_framed_mut => framed_mut}`. [#286] + +[#286]: https://github.com/actix/actix-net/pull/286 ## 3.0.0-beta.2 - 2021-02-06 diff --git a/actix-utils/src/dispatcher.rs b/actix-utils/src/dispatcher.rs index 1e55aa2c..94ac9971 100644 --- a/actix-utils/src/dispatcher.rs +++ b/actix-utils/src/dispatcher.rs @@ -163,29 +163,28 @@ where } } - /// Get sink - pub fn get_sink(&self) -> mpsc::Sender, S::Error>> { + /// Get sender handle. + pub fn tx(&self) -> mpsc::Sender, S::Error>> { self.tx.clone() } /// Get reference to a service wrapped by `Dispatcher` instance. - pub fn get_ref(&self) -> &S { + pub fn service(&self) -> &S { &self.service } /// Get mutable reference to a service wrapped by `Dispatcher` instance. - pub fn get_mut(&mut self) -> &mut S { + pub fn service_mut(&mut self) -> &mut S { &mut self.service } - /// Get reference to a framed instance wrapped by `Dispatcher` - /// instance. - pub fn get_framed(&self) -> &Framed { + /// Get reference to a framed instance wrapped by `Dispatcher` instance. + pub fn framed(&self) -> &Framed { &self.framed } /// Get mutable reference to a framed instance wrapped by `Dispatcher` instance. - pub fn get_framed_mut(&mut self) -> &mut Framed { + pub fn framed_mut(&mut self) -> &mut Framed { &mut self.framed } @@ -268,7 +267,7 @@ where if !this.framed.is_write_buf_empty() { match this.framed.flush(cx) { Poll::Pending => break, - Poll::Ready(Ok(_)) => (), + Poll::Ready(Ok(_)) => {} Poll::Ready(Err(err)) => { debug!("Error sending data: {:?}", err); *this.state = State::FramedError(DispatcherError::Encoder(err)); @@ -318,14 +317,13 @@ where } State::FlushAndStop => { if !this.framed.is_write_buf_empty() { - match this.framed.flush(cx) { - Poll::Ready(Err(err)) => { + this.framed.flush(cx).map(|res| { + if let Err(err) = res { debug!("Error sending data: {:?}", err); - Poll::Ready(Ok(())) } - Poll::Pending => Poll::Pending, - Poll::Ready(Ok(_)) => Poll::Ready(Ok(())), - } + + Ok(()) + }) } else { Poll::Ready(Ok(())) } diff --git a/actix-utils/src/lib.rs b/actix-utils/src/lib.rs index 5c10bac6..6658cba8 100644 --- a/actix-utils/src/lib.rs +++ b/actix-utils/src/lib.rs @@ -8,5 +8,8 @@ pub mod counter; pub mod dispatcher; pub mod mpsc; +mod poll_fn; pub mod task; pub mod timeout; + +use self::poll_fn::poll_fn; diff --git a/actix-utils/src/mpsc.rs b/actix-utils/src/mpsc.rs index 2f2b3f04..9c7a5a0e 100644 --- a/actix-utils/src/mpsc.rs +++ b/actix-utils/src/mpsc.rs @@ -1,31 +1,35 @@ //! A multi-producer, single-consumer, futures-aware, FIFO queue. -use core::any::Any; -use core::cell::RefCell; -use core::fmt; -use core::pin::Pin; -use core::task::{Context, Poll}; +use core::{ + cell::RefCell, + fmt, + pin::Pin, + task::{Context, Poll}, +}; -use std::collections::VecDeque; -use std::error::Error; -use std::rc::Rc; +use std::{collections::VecDeque, error::Error, rc::Rc}; use futures_core::stream::Stream; use futures_sink::Sink; -use crate::task::LocalWaker; +use crate::{poll_fn, task::LocalWaker}; /// Creates a unbounded in-memory channel with buffered storage. +/// +/// [Sender]s and [Receiver]s are `!Send`. pub fn channel() -> (Sender, Receiver) { let shared = Rc::new(RefCell::new(Shared { has_receiver: true, buffer: VecDeque::new(), blocked_recv: LocalWaker::new(), })); + let sender = Sender { shared: shared.clone(), }; + let receiver = Receiver { shared }; + (sender, receiver) } @@ -50,18 +54,22 @@ impl Sender { /// Sends the provided message along this channel. pub fn send(&self, item: T) -> Result<(), SendError> { let mut shared = self.shared.borrow_mut(); + if !shared.has_receiver { - return Err(SendError(item)); // receiver was dropped + // receiver was dropped + return Err(SendError(item)); }; + shared.buffer.push_back(item); shared.blocked_recv.wake(); + Ok(()) } - /// Closes the sender half + /// Closes the sender half. /// - /// This prevents any further messages from being sent on the channel while - /// still enabling the receiver to drain messages that are buffered. + /// This prevents any further messages from being sent on the channel, by any sender, while + /// still enabling the receiver to drain messages that are already buffered. pub fn close(&mut self) { self.shared.borrow_mut().has_receiver = false; } @@ -110,14 +118,24 @@ impl Drop for Sender { /// The receiving end of a channel which implements the `Stream` trait. /// -/// This is created by the `channel` function. +/// This is created by the [`channel`] function. #[derive(Debug)] pub struct Receiver { shared: Rc>>, } impl Receiver { - /// Create Sender + /// Receive the next value. + /// + /// Returns `None` if the channel is empty and has been [closed](Sender::close) explicitly or + /// when all senders have been dropped and, therefore, no more values can ever be sent though + /// this channel. + pub async fn recv(&mut self) -> Option { + let mut this = Pin::new(self); + poll_fn(|cx| this.as_mut().poll_next(cx)).await + } + + /// Create an associated [Sender]. pub fn sender(&self) -> Sender { Sender { shared: self.shared.clone(), @@ -132,11 +150,13 @@ impl Stream for Receiver { fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let mut shared = self.shared.borrow_mut(); + if Rc::strong_count(&self.shared) == 1 { - // All senders have been dropped, so drain the buffer and end the - // stream. - Poll::Ready(shared.buffer.pop_front()) - } else if let Some(msg) = shared.buffer.pop_front() { + // All senders have been dropped, so drain the buffer and end the stream. + return Poll::Ready(shared.buffer.pop_front()); + } + + if let Some(msg) = shared.buffer.pop_front() { Poll::Ready(Some(msg)) } else { shared.blocked_recv.register(cx.waker()); @@ -153,9 +173,15 @@ impl Drop for Receiver { } } -/// Error type for sending, used when the receiving end of a channel is -/// dropped -pub struct SendError(T); +/// Error returned when attempting to send after the channels' [Receiver] is dropped or closed. +pub struct SendError(pub T); + +impl SendError { + /// Returns the message that was attempted to be sent but failed. + pub fn into_inner(self) -> T { + self.0 + } +} impl fmt::Debug for SendError { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -169,18 +195,7 @@ impl fmt::Display for SendError { } } -impl Error for SendError { - fn description(&self) -> &str { - "send failed because receiver is gone" - } -} - -impl SendError { - /// Returns the message that was attempted to be sent but failed. - pub fn into_inner(self) -> T { - self.0 - } -} +impl Error for SendError {} #[cfg(test)] mod tests { @@ -221,4 +236,18 @@ mod tests { assert!(tx.send("test").is_err()); assert!(tx2.send("test").is_err()); } + + #[actix_rt::test] + async fn test_recv() { + let (tx, mut rx) = channel(); + tx.send("test").unwrap(); + assert_eq!(rx.recv().await.unwrap(), "test"); + drop(tx); + + let (tx, mut rx) = channel(); + tx.send("test").unwrap(); + assert_eq!(rx.recv().await.unwrap(), "test"); + drop(tx); + assert!(rx.recv().await.is_none()); + } } diff --git a/actix-utils/src/poll_fn.rs b/actix-utils/src/poll_fn.rs new file mode 100644 index 00000000..2180f4a4 --- /dev/null +++ b/actix-utils/src/poll_fn.rs @@ -0,0 +1,65 @@ +//! Simple "poll function" future and factory. + +use core::{ + fmt, + future::Future, + task::{self, Poll}, +}; +use std::pin::Pin; + +/// Create a future driven by the provided function that receives a task context. +pub(crate) fn poll_fn(f: F) -> PollFn +where + F: FnMut(&mut task::Context<'_>) -> Poll, +{ + PollFn { f } +} + +/// A Future driven by the inner function. +pub(crate) struct PollFn { + f: F, +} + +impl Unpin for PollFn {} + +impl fmt::Debug for PollFn { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("PollFn").finish() + } +} + +impl Future for PollFn +where + F: FnMut(&mut task::Context<'_>) -> task::Poll, +{ + type Output = T; + + fn poll(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll { + (self.f)(cx) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[actix_rt::test] + async fn test_poll_fn() { + let res = poll_fn(|_| Poll::Ready(42)).await; + assert_eq!(res, 42); + + let mut i = 5; + let res = poll_fn(|cx| { + i -= 1; + + if i > 0 { + cx.waker().wake_by_ref(); + Poll::Pending + } else { + Poll::Ready(42) + } + }) + .await; + assert_eq!(res, 42); + } +} diff --git a/actix-utils/src/task.rs b/actix-utils/src/task.rs index d0793488..507bfc14 100644 --- a/actix-utils/src/task.rs +++ b/actix-utils/src/task.rs @@ -9,11 +9,14 @@ use core::{cell::Cell, fmt, marker::PhantomData, task::Waker}; /// logical task. /// /// Consumers should call [`register`] before checking the result of a computation and producers -/// should call `wake` after producing the computation (this differs from the usual `thread::park` +/// should call [`wake`] after producing the computation (this differs from the usual `thread::park` /// pattern). It is also permitted for [`wake`] to be called _before_ [`register`]. This results in /// a no-op. /// /// A single `LocalWaker` may be reused for any number of calls to [`register`] or [`wake`]. +/// +/// [`register`]: LocalWaker::register +/// [`wake`]: LocalWaker::wake #[derive(Default)] pub struct LocalWaker { pub(crate) waker: Cell>, From 9e2bcec226a5e0ad2aa40894d2eac2e09734efb8 Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Sun, 28 Feb 2021 15:01:05 -0800 Subject: [PATCH 15/72] add RcService type and rc_service construct function (#290) --- actix-service/CHANGES.md | 2 + actix-service/src/boxed.rs | 79 +++++++++++++++++--------------------- actix-service/src/lib.rs | 6 +-- 3 files changed, 40 insertions(+), 47 deletions(-) diff --git a/actix-service/CHANGES.md b/actix-service/CHANGES.md index 20fb2cf8..ac340bb6 100644 --- a/actix-service/CHANGES.md +++ b/actix-service/CHANGES.md @@ -2,8 +2,10 @@ ## Unreleased - 2021-xx-xx * Add default `Service` trait impl for `Rc` and `&S: Service`. [#288] +* Add `boxed::rc_service` function for constructing `boxed::RcService` type [#290] [#288]: https://github.com/actix/actix-net/pull/288 +[#290]: https://github.com/actix/actix-net/pull/290 ## 2.0.0-beta.4 - 2021-02-04 diff --git a/actix-service/src/boxed.rs b/actix-service/src/boxed.rs index 6ad2eaf4..4afaa6c3 100644 --- a/actix-service/src/boxed.rs +++ b/actix-service/src/boxed.rs @@ -1,7 +1,6 @@ -use alloc::boxed::Box; +use alloc::{boxed::Box, rc::Rc}; use core::{ future::Future, - marker::PhantomData, pin::Pin, task::{Context, Poll}, }; @@ -10,12 +9,34 @@ use crate::{Service, ServiceFactory}; pub type BoxFuture = Pin>>; -pub type BoxService = - Box>>>; +macro_rules! service_object { + ($name: ident, $type: tt, $fn_name: ident) => { + /// Type alias for service trait object. + pub type $name = $type< + dyn Service>>, + >; + /// Create service trait object. + pub fn $fn_name(service: S) -> $name + where + S: Service + 'static, + Req: 'static, + S::Future: 'static, + { + $type::new(ServiceWrapper(service)) + } + }; +} + +service_object!(BoxService, Box, service); + +service_object!(RcService, Rc, rc_service); + +/// Type alias for service factory trait object that would produce a trait object service +/// (`BoxService`, `RcService`, etc.) pub struct BoxServiceFactory(Inner); -/// Create boxed service factory +/// Create service factory trait object. pub fn factory( factory: SF, ) -> BoxServiceFactory @@ -28,20 +49,7 @@ where SF::Error: 'static, SF::InitError: 'static, { - BoxServiceFactory(Box::new(FactoryWrapper { - factory, - _t: PhantomData, - })) -} - -/// Create boxed service -pub fn service(service: S) -> BoxService -where - S: Service + 'static, - Req: 'static, - S::Future: 'static, -{ - Box::new(ServiceWrapper(service, PhantomData)) + BoxServiceFactory(Box::new(FactoryWrapper(factory))) } type Inner = Box< @@ -66,9 +74,9 @@ where { type Response = Res; type Error = Err; - type InitError = InitErr; type Config = C; type Service = BoxService; + type InitError = InitErr; type Future = BoxFuture>; @@ -77,12 +85,9 @@ where } } -struct FactoryWrapper { - factory: SF, - _t: PhantomData<(Req, Cfg)>, -} +struct FactoryWrapper(SF); -impl ServiceFactory for FactoryWrapper +impl ServiceFactory for FactoryWrapper where Req: 'static, Res: 'static, @@ -95,34 +100,20 @@ where { type Response = Res; type Error = Err; - type InitError = InitErr; type Config = Cfg; type Service = BoxService; + type InitError = InitErr; type Future = BoxFuture>; fn new_service(&self, cfg: Cfg) -> Self::Future { - let fut = self.factory.new_service(cfg); - Box::pin(async { - let res = fut.await; - res.map(ServiceWrapper::boxed) - }) + let f = self.0.new_service(cfg); + Box::pin(async { f.await.map(|s| Box::new(ServiceWrapper(s)) as _) }) } } -struct ServiceWrapper, Req>(S, PhantomData); +struct ServiceWrapper(S); -impl ServiceWrapper -where - S: Service + 'static, - Req: 'static, - S::Future: 'static, -{ - fn boxed(service: S) -> BoxService { - Box::new(ServiceWrapper(service, PhantomData)) - } -} - -impl Service for ServiceWrapper +impl Service for ServiceWrapper where S: Service, S::Future: 'static, diff --git a/actix-service/src/lib.rs b/actix-service/src/lib.rs index 66132961..a4f6c5b4 100644 --- a/actix-service/src/lib.rs +++ b/actix-service/src/lib.rs @@ -204,18 +204,18 @@ where impl Service for Rc where - S: Service, + S: Service + ?Sized, { type Response = S::Response; type Error = S::Error; type Future = S::Future; fn poll_ready(&self, ctx: &mut Context<'_>) -> Poll> { - (&**self).poll_ready(ctx) + (**self).poll_ready(ctx) } fn call(&self, request: Req) -> S::Future { - (&**self).call(request) + (**self).call(request) } } From 0a705b1023862a22d52b6f6b85123e7ac40a7465 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Wed, 10 Mar 2021 02:23:19 +0000 Subject: [PATCH 16/72] add docs for *_ready macros --- actix-service/src/fn_service.rs | 2 +- actix-service/src/lib.rs | 29 +---- actix-service/src/macros.rs | 181 ++++++++++++++++++++++++++++++++ actix-utils/src/timeout.rs | 1 - 4 files changed, 183 insertions(+), 30 deletions(-) create mode 100644 actix-service/src/macros.rs diff --git a/actix-service/src/fn_service.rs b/actix-service/src/fn_service.rs index 230f437b..19151eed 100644 --- a/actix-service/src/fn_service.rs +++ b/actix-service/src/fn_service.rs @@ -1,4 +1,4 @@ -use core::{future::Future, marker::PhantomData, task::Poll}; +use core::{future::Future, marker::PhantomData}; use crate::{ok, IntoService, IntoServiceFactory, Ready, Service, ServiceFactory}; diff --git a/actix-service/src/lib.rs b/actix-service/src/lib.rs index a4f6c5b4..b690553f 100644 --- a/actix-service/src/lib.rs +++ b/actix-service/src/lib.rs @@ -21,6 +21,7 @@ mod apply_cfg; pub mod boxed; mod ext; mod fn_service; +mod macros; mod map; mod map_config; mod map_err; @@ -325,31 +326,3 @@ pub mod dev { pub use crate::transform::ApplyTransform; pub use crate::transform_err::TransformMapInitErr; } - -#[macro_export] -macro_rules! always_ready { - () => { - #[inline] - fn poll_ready( - &self, - _: &mut ::core::task::Context<'_>, - ) -> ::core::task::Poll> { - Poll::Ready(Ok(())) - } - }; -} - -#[macro_export] -macro_rules! forward_ready { - ($field:ident) => { - #[inline] - fn poll_ready( - &self, - cx: &mut ::core::task::Context<'_>, - ) -> ::core::task::Poll> { - self.$field - .poll_ready(cx) - .map_err(::core::convert::Into::into) - } - }; -} diff --git a/actix-service/src/macros.rs b/actix-service/src/macros.rs new file mode 100644 index 00000000..4a083895 --- /dev/null +++ b/actix-service/src/macros.rs @@ -0,0 +1,181 @@ +/// A boilerplate implementation of [`Service::poll_ready`] that always signals readiness. +/// +/// [`Service::poll_ready`]: crate::Service::poll_ready +/// +/// # Examples +/// ```no_run +/// use actix_service::Service; +/// use futures_util::future::{ready, Ready}; +/// +/// struct IdentityService; +/// +/// impl Service for IdentityService { +/// type Response = u32; +/// type Error = (); +/// type Future = Ready>; +/// +/// actix_service::always_ready!(); +/// +/// fn call(&self, req: u32) -> Self::Future { +/// ready(Ok(req)) +/// } +/// } +/// ``` +#[macro_export] +macro_rules! always_ready { + () => { + #[inline] + fn poll_ready( + &self, + _: &mut ::core::task::Context<'_>, + ) -> ::core::task::Poll> { + ::core::task::Poll::Ready(Ok(())) + } + }; +} + +/// A boilerplate implementation of [`Service::poll_ready`] that forwards readiness checks to a +/// named struct field. +/// +/// Tuple structs are not supported. +/// +/// [`Service::poll_ready`]: crate::Service::poll_ready +/// +/// # Examples +/// ```no_run +/// use actix_service::Service; +/// use futures_util::future::{ready, Ready}; +/// +/// struct WrapperService { +/// inner: S, +/// } +/// +/// impl Service<()> for WrapperService +/// where +/// S: Service<()>, +/// { +/// type Response = S::Response; +/// type Error = S::Error; +/// type Future = S::Future; +/// +/// actix_service::forward_ready!(inner); +/// +/// fn call(&self, req: ()) -> Self::Future { +/// self.inner.call(req) +/// } +/// } +/// ``` +#[macro_export] +macro_rules! forward_ready { + ($field:ident) => { + #[inline] + fn poll_ready( + &self, + cx: &mut ::core::task::Context<'_>, + ) -> ::core::task::Poll> { + self.$field + .poll_ready(cx) + .map_err(::core::convert::Into::into) + } + }; +} + +#[cfg(test)] +mod tests { + use core::{ + cell::Cell, + convert::Infallible, + task::{self, Context, Poll}, + }; + + use futures_util::{ + future::{ready, Ready}, + task::noop_waker, + }; + + use crate::Service; + + struct IdentityService; + + impl Service for IdentityService { + type Response = u32; + type Error = Infallible; + type Future = Ready>; + + always_ready!(); + + fn call(&self, req: u32) -> Self::Future { + ready(Ok(req)) + } + } + + struct CountdownService(Cell); + + impl Service<()> for CountdownService { + type Response = (); + type Error = Infallible; + type Future = Ready>; + + fn poll_ready(&self, cx: &mut Context<'_>) -> Poll> { + let count = self.0.get(); + + if count == 0 { + Poll::Ready(Ok(())) + } else { + self.0.set(count - 1); + cx.waker().wake_by_ref(); + Poll::Pending + } + } + + fn call(&self, _: ()) -> Self::Future { + ready(Ok(())) + } + } + + struct WrapperService { + inner: S, + } + + impl Service<()> for WrapperService + where + S: Service<()>, + { + type Response = S::Response; + type Error = S::Error; + type Future = S::Future; + + forward_ready!(inner); + + fn call(&self, req: ()) -> Self::Future { + self.inner.call(req) + } + } + + #[test] + fn test_always_ready_macro() { + let waker = noop_waker(); + let mut cx = task::Context::from_waker(&waker); + + let svc = IdentityService; + + assert!(svc.poll_ready(&mut cx).is_ready()); + assert!(svc.poll_ready(&mut cx).is_ready()); + assert!(svc.poll_ready(&mut cx).is_ready()); + } + + #[test] + fn test_forward_ready_macro() { + let waker = noop_waker(); + let mut cx = task::Context::from_waker(&waker); + + let svc = WrapperService { + inner: CountdownService(Cell::new(3)), + }; + + assert!(svc.poll_ready(&mut cx).is_pending()); + assert!(svc.poll_ready(&mut cx).is_pending()); + assert!(svc.poll_ready(&mut cx).is_pending()); + assert!(svc.poll_ready(&mut cx).is_ready()); + } +} diff --git a/actix-utils/src/timeout.rs b/actix-utils/src/timeout.rs index 9304e5f6..f13c7ffa 100644 --- a/actix-utils/src/timeout.rs +++ b/actix-utils/src/timeout.rs @@ -197,7 +197,6 @@ where #[cfg(test)] mod tests { - use core::task::Poll; use core::time::Duration; use super::*; From 91ea8c5dad612e5b6745522330ff25cc75a31b3a Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Wed, 10 Mar 2021 03:18:09 +0000 Subject: [PATCH 17/72] remove service dev module and add transformext trait also improve docs on transform and boxed mods --- actix-service/src/boxed.rs | 63 ++++++++++++++++-------------- actix-service/src/ext.rs | 28 ++++++++++--- actix-service/src/fn_service.rs | 10 ++--- actix-service/src/lib.rs | 18 ++------- actix-service/src/transform.rs | 41 +++++++------------ actix-service/src/transform_err.rs | 6 +-- actix-tracing/src/lib.rs | 2 +- 7 files changed, 79 insertions(+), 89 deletions(-) diff --git a/actix-service/src/boxed.rs b/actix-service/src/boxed.rs index 4afaa6c3..a872ca9f 100644 --- a/actix-service/src/boxed.rs +++ b/actix-service/src/boxed.rs @@ -1,12 +1,11 @@ +//! Trait object forms of services and service factories. + use alloc::{boxed::Box, rc::Rc}; -use core::{ - future::Future, - pin::Pin, - task::{Context, Poll}, -}; +use core::{future::Future, pin::Pin}; use crate::{Service, ServiceFactory}; +/// A boxed future without a Send bound or lifetime parameters. pub type BoxFuture = Pin>>; macro_rules! service_object { @@ -23,17 +22,41 @@ macro_rules! service_object { Req: 'static, S::Future: 'static, { - $type::new(ServiceWrapper(service)) + $type::new(ServiceWrapper::new(service)) } }; } service_object!(BoxService, Box, service); - service_object!(RcService, Rc, rc_service); -/// Type alias for service factory trait object that would produce a trait object service -/// (`BoxService`, `RcService`, etc.) +struct ServiceWrapper { + inner: S, +} + +impl ServiceWrapper { + fn new(inner: S) -> Self { + Self { inner } + } +} + +impl Service for ServiceWrapper +where + S: Service, + S::Future: 'static, +{ + type Response = Res; + type Error = Err; + type Future = BoxFuture>; + + crate::forward_ready!(inner); + + fn call(&self, req: Req) -> Self::Future { + Box::pin(self.inner.call(req)) + } +} + +/// Wrapper for a service factory trait object that will produce a boxed trait object service. pub struct BoxServiceFactory(Inner); /// Create service factory trait object. @@ -107,26 +130,6 @@ where fn new_service(&self, cfg: Cfg) -> Self::Future { let f = self.0.new_service(cfg); - Box::pin(async { f.await.map(|s| Box::new(ServiceWrapper(s)) as _) }) - } -} - -struct ServiceWrapper(S); - -impl Service for ServiceWrapper -where - S: Service, - S::Future: 'static, -{ - type Response = Res; - type Error = Err; - type Future = BoxFuture>; - - fn poll_ready(&self, ctx: &mut Context<'_>) -> Poll> { - self.0.poll_ready(ctx) - } - - fn call(&self, req: Req) -> Self::Future { - Box::pin(self.0.call(req)) + Box::pin(async { f.await.map(|s| Box::new(ServiceWrapper::new(s)) as _) }) } } diff --git a/actix-service/src/ext.rs b/actix-service/src/ext.rs index e778d11e..d931596b 100644 --- a/actix-service/src/ext.rs +++ b/actix-service/src/ext.rs @@ -1,4 +1,7 @@ -use crate::{dev, Service, ServiceFactory}; +use crate::{ + map::Map, map_err::MapErr, transform_err::TransformMapInitErr, Service, ServiceFactory, + Transform, +}; pub trait ServiceExt: Service { /// Map this service's output to a different type, returning a new service @@ -10,12 +13,12 @@ pub trait ServiceExt: 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 + fn map(self, f: F) -> Map where Self: Sized, F: FnMut(Self::Response) -> R, { - dev::Map::new(self, f) + Map::new(self, f) } /// Map this service's error to a different error, returning a new service. @@ -26,12 +29,12 @@ pub trait ServiceExt: Service { /// /// Note that this function consumes the receiving service and returns a /// wrapped version of it. - fn map_err(self, f: F) -> dev::MapErr + fn map_err(self, f: F) -> MapErr where Self: Sized, F: Fn(Self::Error) -> E, { - dev::MapErr::new(self, f) + MapErr::new(self, f) } } @@ -67,4 +70,17 @@ pub trait ServiceFactoryExt: ServiceFactory { } } -impl ServiceFactoryExt for S where S: ServiceFactory {} +impl ServiceFactoryExt for SF where SF: ServiceFactory {} + +pub trait TransformExt: Transform { + /// Return a new `Transform` whose init error is mapped to to a different type. + fn map_init_err(self, f: F) -> TransformMapInitErr + where + Self: Sized, + F: Fn(Self::InitError) -> E + Clone, + { + TransformMapInitErr::new(self, f) + } +} + +impl TransformExt for T where T: Transform {} diff --git a/actix-service/src/fn_service.rs b/actix-service/src/fn_service.rs index 19151eed..8c1a6f51 100644 --- a/actix-service/src/fn_service.rs +++ b/actix-service/src/fn_service.rs @@ -15,8 +15,7 @@ where /// Create `ServiceFactory` for function that can produce services /// -/// # Example -/// +/// # Examples /// ``` /// use std::io; /// use actix_service::{fn_factory, fn_service, Service, ServiceFactory}; @@ -62,11 +61,10 @@ where /// Create `ServiceFactory` for function that accepts config argument and can produce services /// -/// Any function that has following form `Fn(Config) -> Future` could -/// act as a `ServiceFactory`. -/// -/// # Example +/// Any function that has following form `Fn(Config) -> Future` could act as +/// a `ServiceFactory`. /// +/// # Examples /// ``` /// use std::io; /// use actix_service::{fn_factory_with_config, fn_service, Service, ServiceFactory}; diff --git a/actix-service/src/lib.rs b/actix-service/src/lib.rs index b690553f..cc82bfa6 100644 --- a/actix-service/src/lib.rs +++ b/actix-service/src/lib.rs @@ -34,11 +34,11 @@ 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::ext::{ServiceExt, ServiceFactoryExt, TransformExt}; 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}; +pub use self::transform::{apply, ApplyTransform, Transform}; #[allow(unused_imports)] use self::ready::{err, ok, ready, Ready}; @@ -220,6 +220,7 @@ where } } +/// This impl is deprecated since v2 because the `Service` trait now receives shared reference. impl Service for RefCell where S: Service, @@ -313,16 +314,3 @@ where { tp.into_service() } - -pub mod dev { - pub use crate::apply::{Apply, ApplyFactory}; - pub use crate::fn_service::{ - FnService, FnServiceConfig, FnServiceFactory, FnServiceNoConfig, - }; - pub use crate::map::{Map, MapServiceFactory}; - pub use crate::map_config::{MapConfig, UnitConfig}; - pub use crate::map_err::{MapErr, MapErrServiceFactory}; - pub use crate::map_init_err::MapInitErr; - pub use crate::transform::ApplyTransform; - pub use crate::transform_err::TransformMapInitErr; -} diff --git a/actix-service/src/transform.rs b/actix-service/src/transform.rs index 7f477e54..b0abe72b 100644 --- a/actix-service/src/transform.rs +++ b/actix-service/src/transform.rs @@ -9,10 +9,9 @@ use core::{ use futures_core::ready; use pin_project_lite::pin_project; -use crate::transform_err::TransformMapInitErr; use crate::{IntoServiceFactory, Service, ServiceFactory}; -/// Apply transform to a service. +/// Apply a [`Transform`] to a [`Service`]. pub fn apply(t: T, factory: I) -> ApplyTransform where I: IntoServiceFactory, @@ -25,9 +24,8 @@ where /// The `Transform` trait defines the interface of a service factory that wraps inner service /// during construction. /// -/// Transform(middleware) wraps inner service and runs during -/// inbound and/or outbound processing in the request/response lifecycle. -/// It may modify request and/or response. +/// Transform(middleware) wraps inner service and runs during inbound and/or outbound processing in +/// the request/response lifecycle. It may modify request and/or response. /// /// For example, timeout transform: /// @@ -51,20 +49,19 @@ where /// fn call(&self, req: S::Request) -> Self::Future { /// TimeoutServiceResponse { /// fut: self.service.call(req), -/// sleep: Delay::new(clock::now() + self.timeout), +/// sleep: Sleep::new(clock::now() + self.timeout), /// } /// } /// } /// ``` /// -/// Timeout service in above example is decoupled from underlying service implementation -/// and could be applied to any service. +/// Timeout service in above example is decoupled from underlying service implementation and could +/// be applied to any service. /// -/// The `Transform` trait defines the interface of a Service factory. `Transform` -/// is often implemented for middleware, defining how to construct a -/// middleware Service. A Service that is constructed by the factory takes -/// the Service that follows it during execution as a parameter, assuming -/// ownership of the next Service. +/// The `Transform` trait defines the interface of a Service factory. `Transform` is often +/// implemented for middleware, defining how to construct a middleware Service. A Service that is +/// constructed by the factory takes the Service that follows it during execution as a parameter, +/// assuming ownership of the next Service. /// /// Factory for `Timeout` middleware from the above example could look like this: /// @@ -85,15 +82,15 @@ where /// type Future = Ready>; /// /// fn new_transform(&self, service: S) -> Self::Future { -/// ok(TimeoutService { +/// ready(Ok(TimeoutService { /// service, /// timeout: self.timeout, -/// }) +/// })) /// } /// } /// ``` pub trait Transform { - /// Responses given by the service. + /// Responses produced by the service. type Response; /// Errors produced by the service. @@ -110,16 +107,6 @@ pub trait Transform { /// Creates and returns a new Transform component, asynchronously fn new_transform(&self, service: S) -> Self::Future; - - /// Map this transform's factory error to a different error, - /// returning a new transform service factory. - fn map_init_err(self, f: F) -> TransformMapInitErr - where - Self: Sized, - F: Fn(Self::InitError) -> E + Clone, - { - TransformMapInitErr::new(self, f) - } } impl Transform for Rc @@ -152,7 +139,7 @@ where } } -/// `Apply` transform to new service +/// Apply a [`Transform`] to a [`Service`]. pub struct ApplyTransform(Rc<(T, S)>, PhantomData); impl ApplyTransform diff --git a/actix-service/src/transform_err.rs b/actix-service/src/transform_err.rs index cbf5fe3b..b4695d5c 100644 --- a/actix-service/src/transform_err.rs +++ b/actix-service/src/transform_err.rs @@ -9,10 +9,8 @@ use pin_project_lite::pin_project; use super::Transform; -/// Transform for the `map_init_err` combinator, changing the type of a new -/// transform's init error. -/// -/// This is created by the `Transform::map_init_err` method. +/// Transform for the [`TransformExt::map_init_err`] combinator, changing the type of a new +/// [`Transform`]'s initialization error. pub struct TransformMapInitErr { transform: T, mapper: F, diff --git a/actix-tracing/src/lib.rs b/actix-tracing/src/lib.rs index b34f40d6..89e93be1 100644 --- a/actix-tracing/src/lib.rs +++ b/actix-tracing/src/lib.rs @@ -7,7 +7,7 @@ use core::marker::PhantomData; use actix_service::{ - apply, dev::ApplyTransform, IntoServiceFactory, Service, ServiceFactory, Transform, + apply, ApplyTransform, IntoServiceFactory, Service, ServiceFactory, Transform, }; use futures_util::future::{ok, Either, Ready}; use tracing_futures::{Instrument, Instrumented}; From 746cc2ab89b5fe8e8a64dc7c37fc55a46927ab5e Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Mon, 15 Mar 2021 23:09:34 +0000 Subject: [PATCH 18/72] prepare service release 2.0.0-beta.5 --- actix-server/Cargo.toml | 2 +- actix-service/CHANGES.md | 3 +++ actix-service/Cargo.toml | 2 +- actix-service/README.md | 6 +++--- actix-tls/Cargo.toml | 2 +- actix-tracing/Cargo.toml | 2 +- actix-utils/Cargo.toml | 2 +- 7 files changed, 11 insertions(+), 8 deletions(-) diff --git a/actix-server/Cargo.toml b/actix-server/Cargo.toml index 0a395797..6d763d79 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", default-features = false } -actix-service = "2.0.0-beta.4" +actix-service = "2.0.0-beta.5" actix-utils = "3.0.0-beta.2" futures-core = { version = "0.3.7", default-features = false, features = ["alloc"] } diff --git a/actix-service/CHANGES.md b/actix-service/CHANGES.md index ac340bb6..51749ecd 100644 --- a/actix-service/CHANGES.md +++ b/actix-service/CHANGES.md @@ -1,6 +1,9 @@ # Changes ## Unreleased - 2021-xx-xx + + +## 2.0.0-beta.5 - 2021-03-15 * Add default `Service` trait impl for `Rc` and `&S: Service`. [#288] * Add `boxed::rc_service` function for constructing `boxed::RcService` type [#290] diff --git a/actix-service/Cargo.toml b/actix-service/Cargo.toml index eecf4669..84a0c172 100644 --- a/actix-service/Cargo.toml +++ b/actix-service/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-service" -version = "2.0.0-beta.4" +version = "2.0.0-beta.5" authors = [ "Nikolay Kim ", "Rob Ede ", diff --git a/actix-service/README.md b/actix-service/README.md index 28c38295..54171274 100644 --- a/actix-service/README.md +++ b/actix-service/README.md @@ -3,11 +3,11 @@ > 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) +[![Documentation](https://docs.rs/actix-service/badge.svg?version=2.0.0-beta.5)](https://docs.rs/actix-service/2.0.0-beta.5) [![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) +[![Dependency Status](https://deps.rs/crate/actix-service/2.0.0-beta.5/status.svg)](https://deps.rs/crate/actix-service/2.0.0-beta.5) +![Download](https://img.shields.io/crates/d/actix-service.svg) [![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 b3a0e30c..354bb428 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.1.0", default-features = false } -actix-service = "2.0.0-beta.4" +actix-service = "2.0.0-beta.5" actix-utils = "3.0.0-beta.2" derive_more = "0.99.5" diff --git a/actix-tracing/Cargo.toml b/actix-tracing/Cargo.toml index 60ad1454..7f043f4b 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.4" +actix-service = "2.0.0-beta.5" 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 da46256e..9c21dd1b 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.4" +actix-service = "2.0.0-beta.5" futures-core = { version = "0.3.7", default-features = false } futures-sink = { version = "0.3.7", default-features = false } From 945479e0c3e236fdb72800dc42db8a92a1e52b89 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Wed, 17 Mar 2021 00:26:04 +0000 Subject: [PATCH 19/72] unvendor openssl (#292) --- .github/workflows/ci.yml | 14 ++++++++++++++ actix-tls/Cargo.toml | 6 ------ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7b5b47c4..2bb9a234 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,6 +25,9 @@ jobs: name: ${{ matrix.target.name }} / ${{ matrix.version }} runs-on: ${{ matrix.target.os }} + env: + VCPKGRS_DYNAMIC: 1 + steps: - name: Setup Routing if: matrix.target.os == 'macos-latest' @@ -32,6 +35,17 @@ jobs: - uses: actions/checkout@v2 + # install OpenSSL on Windows + - name: Set vcpkg root + if: matrix.target.triple == 'x86_64-pc-windows-msvc' || matrix.target.triple == 'i686-pc-windows-msvc' + run: echo "VCPKG_ROOT=$env:VCPKG_INSTALLATION_ROOT" | Out-File -FilePath $env:GITHUB_ENV -Append + - name: Install OpenSSL + if: matrix.target.triple == 'x86_64-pc-windows-msvc' + run: vcpkg install openssl:x64-windows + - name: Install OpenSSL + if: matrix.target.triple == 'i686-pc-windows-msvc' + run: vcpkg install openssl:x86-windows + - name: Install ${{ matrix.version }} uses: actions-rs/toolchain@v1 with: diff --git a/actix-tls/Cargo.toml b/actix-tls/Cargo.toml index 354bb428..d14c65ac 100755 --- a/actix-tls/Cargo.toml +++ b/actix-tls/Cargo.toml @@ -62,12 +62,6 @@ webpki-roots = { version = "0.21", optional = true } # native-tls tokio-native-tls = { version = "0.3", optional = true } -[target.'cfg(windows)'.dependencies.tls-openssl] -version = "0.10.9" -package = "openssl" -features = ["vendored"] -optional = true - [dev-dependencies] actix-rt = "2.1.0" actix-server = "2.0.0-beta.3" From 0c73f13c8beebb42911566f3f98ac78d8a941453 Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Mon, 22 Mar 2021 22:50:48 -0700 Subject: [PATCH 20/72] ActixStream readiness methods return Ready object (#293) --- actix-rt/CHANGES.md | 3 +++ actix-rt/Cargo.toml | 2 +- actix-rt/src/lib.rs | 37 +++++++++++++++++++++---------- actix-tls/src/accept/nativetls.rs | 6 ++--- actix-tls/src/accept/openssl.rs | 6 ++--- actix-tls/src/accept/rustls.rs | 6 ++--- 6 files changed, 38 insertions(+), 22 deletions(-) diff --git a/actix-rt/CHANGES.md b/actix-rt/CHANGES.md index 1fd7b25b..83ecc5ed 100644 --- a/actix-rt/CHANGES.md +++ b/actix-rt/CHANGES.md @@ -1,6 +1,9 @@ # Changes ## Unreleased - 2021-xx-xx +* `ActixStream::{poll_read_ready, poll_write_ready}` would return `Ready` in Ok variant. [#293] + +[#293] https://github.com/actix/actix-net/pull/293 ## 2.1.0 - 2021-02-24 diff --git a/actix-rt/Cargo.toml b/actix-rt/Cargo.toml index 92f10b85..126056ec 100644 --- a/actix-rt/Cargo.toml +++ b/actix-rt/Cargo.toml @@ -26,7 +26,7 @@ macros = ["actix-macros"] actix-macros = { version = "0.2.0", optional = true } futures-core = { version = "0.3", default-features = false } -tokio = { version = "1.2", features = ["rt", "net", "parking_lot", "signal", "sync", "time"] } +tokio = { version = "1.3", features = ["rt", "net", "parking_lot", "signal", "sync", "time"] } [dev-dependencies] tokio = { version = "1.2", features = ["full"] } diff --git a/actix-rt/src/lib.rs b/actix-rt/src/lib.rs index e21cd651..bd2e165d 100644 --- a/actix-rt/src/lib.rs +++ b/actix-rt/src/lib.rs @@ -72,9 +72,14 @@ pub mod signal { pub mod net { //! TCP/UDP/Unix bindings (mostly Tokio re-exports). - use std::task::{Context, Poll}; + use std::{ + future::Future, + io, + task::{Context, Poll}, + }; - use tokio::io::{AsyncRead, AsyncWrite}; + pub use tokio::io::Ready; + use tokio::io::{AsyncRead, AsyncWrite, Interest}; pub use tokio::net::UdpSocket; pub use tokio::net::{TcpListener, TcpSocket, TcpStream}; @@ -86,32 +91,40 @@ pub mod net { /// Poll stream and check read readiness of Self. /// /// See [tokio::net::TcpStream::poll_read_ready] for detail on intended use. - fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll>; + fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll>; /// Poll stream and check write readiness of Self. /// /// See [tokio::net::TcpStream::poll_write_ready] for detail on intended use. - fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll>; + fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll>; } impl ActixStream for TcpStream { - fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll> { - TcpStream::poll_read_ready(self, cx) + fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll> { + let ready = self.ready(Interest::READABLE); + tokio::pin!(ready); + ready.poll(cx) } - fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll> { - TcpStream::poll_write_ready(self, cx) + fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll> { + let ready = self.ready(Interest::WRITABLE); + tokio::pin!(ready); + ready.poll(cx) } } #[cfg(unix)] impl ActixStream for UnixStream { - fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll> { - UnixStream::poll_read_ready(self, cx) + fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll> { + let ready = self.ready(Interest::READABLE); + tokio::pin!(ready); + ready.poll(cx) } - fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll> { - UnixStream::poll_write_ready(self, cx) + fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll> { + let ready = self.ready(Interest::WRITABLE); + tokio::pin!(ready); + ready.poll(cx) } } } diff --git a/actix-tls/src/accept/nativetls.rs b/actix-tls/src/accept/nativetls.rs index 98a103a8..614bdad3 100644 --- a/actix-tls/src/accept/nativetls.rs +++ b/actix-tls/src/accept/nativetls.rs @@ -6,7 +6,7 @@ use std::{ }; use actix_codec::{AsyncRead, AsyncWrite, ReadBuf}; -use actix_rt::net::ActixStream; +use actix_rt::net::{ActixStream, Ready}; use actix_service::{Service, ServiceFactory}; use actix_utils::counter::Counter; use futures_core::future::LocalBoxFuture; @@ -80,11 +80,11 @@ impl AsyncWrite for TlsStream { } impl ActixStream for TlsStream { - fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll> { + fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll> { T::poll_read_ready((&**self).get_ref().get_ref().get_ref(), cx) } - fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll> { + fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll> { T::poll_write_ready((&**self).get_ref().get_ref().get_ref(), cx) } } diff --git a/actix-tls/src/accept/openssl.rs b/actix-tls/src/accept/openssl.rs index f94e3c2d..4afcdcab 100644 --- a/actix-tls/src/accept/openssl.rs +++ b/actix-tls/src/accept/openssl.rs @@ -7,7 +7,7 @@ use std::{ }; use actix_codec::{AsyncRead, AsyncWrite, ReadBuf}; -use actix_rt::net::ActixStream; +use actix_rt::net::{ActixStream, Ready}; use actix_service::{Service, ServiceFactory}; use actix_utils::counter::{Counter, CounterGuard}; use futures_core::{future::LocalBoxFuture, ready}; @@ -82,11 +82,11 @@ impl AsyncWrite for TlsStream { } impl ActixStream for TlsStream { - fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll> { + fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll> { T::poll_read_ready((&**self).get_ref(), cx) } - fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll> { + fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll> { T::poll_write_ready((&**self).get_ref(), cx) } } diff --git a/actix-tls/src/accept/rustls.rs b/actix-tls/src/accept/rustls.rs index 753d68ac..ffac687a 100644 --- a/actix-tls/src/accept/rustls.rs +++ b/actix-tls/src/accept/rustls.rs @@ -8,7 +8,7 @@ use std::{ }; use actix_codec::{AsyncRead, AsyncWrite, ReadBuf}; -use actix_rt::net::ActixStream; +use actix_rt::net::{ActixStream, Ready}; use actix_service::{Service, ServiceFactory}; use actix_utils::counter::{Counter, CounterGuard}; use futures_core::future::LocalBoxFuture; @@ -82,11 +82,11 @@ impl AsyncWrite for TlsStream { } impl ActixStream for TlsStream { - fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll> { + fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll> { T::poll_read_ready((&**self).get_ref().0, cx) } - fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll> { + fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll> { T::poll_write_ready((&**self).get_ref().0, cx) } } From b7bfff2b32ea867cd4a5689fc45db7cd6398234d Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Wed, 24 Mar 2021 04:56:13 -0700 Subject: [PATCH 21/72] add example of using multi-thread tokio runtime (#294) * add example of using multi-thread tokio runtime * Update multi_thread_system.rs Co-authored-by: Rob Ede --- actix-rt/examples/multi_thread_system.rs | 60 ++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 actix-rt/examples/multi_thread_system.rs diff --git a/actix-rt/examples/multi_thread_system.rs b/actix-rt/examples/multi_thread_system.rs new file mode 100644 index 00000000..0ecd1ef1 --- /dev/null +++ b/actix-rt/examples/multi_thread_system.rs @@ -0,0 +1,60 @@ +//! An example on how to build a multi-thread tokio runtime for Actix System. +//! Then spawn async task that can make use of work stealing of tokio runtime. + +use actix_rt::System; + +fn main() { + System::with_tokio_rt(|| { + // build system with a multi-thread tokio runtime. + tokio::runtime::Builder::new_multi_thread() + .worker_threads(2) + .enable_all() + .build() + .unwrap() + }) + .block_on(async_main()); +} + +// async main function that acts like #[actix_web::main] or #[tokio::main] +async fn async_main() { + let (tx, rx) = tokio::sync::oneshot::channel(); + + // get a handle to system arbiter and spawn async task on it + System::current().arbiter().spawn(async { + // use tokio::spawn to get inside the context of multi thread tokio runtime + let h1 = tokio::spawn(async { + println!("thread id is {:?}", std::thread::current().id()); + std::thread::sleep(std::time::Duration::from_secs(2)); + }); + + // work stealing occurs for this task spawn + let h2 = tokio::spawn(async { + println!("thread id is {:?}", std::thread::current().id()); + }); + + h1.await.unwrap(); + h2.await.unwrap(); + let _ = tx.send(()); + }); + + rx.await.unwrap(); + + let (tx, rx) = tokio::sync::oneshot::channel(); + let now = std::time::Instant::now(); + + // without additional tokio::spawn, all spawned tasks run on single thread + System::current().arbiter().spawn(async { + println!("thread id is {:?}", std::thread::current().id()); + std::thread::sleep(std::time::Duration::from_secs(2)); + let _ = tx.send(()); + }); + + // previous spawn task has blocked the system arbiter thread + // so this task will wait for 2 seconds until it can be run + System::current().arbiter().spawn(async move { + println!("thread id is {:?}", std::thread::current().id()); + assert!(now.elapsed() > std::time::Duration::from_secs(2)); + }); + + rx.await.unwrap(); +} From a3c9ebc7fa4f05e855f47d183fa667db86d2c91e Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Wed, 24 Mar 2021 09:32:04 -0700 Subject: [PATCH 22/72] fix rustls panic when generating dns name from ip (#296) * fix rustls panic when generating dns name from ip * Update rustls.rs * update changelog Co-authored-by: Rob Ede --- actix-tls/CHANGES.md | 4 ++ actix-tls/src/connect/ssl/rustls.rs | 67 +++++++++++++++++------------ 2 files changed, 44 insertions(+), 27 deletions(-) diff --git a/actix-tls/CHANGES.md b/actix-tls/CHANGES.md index 824663b0..5bf21d4e 100644 --- a/actix-tls/CHANGES.md +++ b/actix-tls/CHANGES.md @@ -1,6 +1,10 @@ # Changes ## Unreleased - 2021-xx-xx +* Changed `connect::ssl::rustls::RustlsConnectorService` to return error when `DNSNameRef` + generation failed instead of panic. [#296] + +[#296]: https://github.com/actix/actix-net/pull/296 ## 3.0.0-beta.4 - 2021-02-24 diff --git a/actix-tls/src/connect/ssl/rustls.rs b/actix-tls/src/connect/ssl/rustls.rs index 46b4b11d..b03d4b7f 100755 --- a/actix-tls/src/connect/ssl/rustls.rs +++ b/actix-tls/src/connect/ssl/rustls.rs @@ -1,6 +1,6 @@ use std::{ - fmt, future::Future, + io, pin::Pin, sync::Arc, task::{Context, Poll}, @@ -10,7 +10,7 @@ pub use tokio_rustls::rustls::Session; pub use tokio_rustls::{client::TlsStream, rustls::ClientConfig}; pub use webpki_roots::TLS_SERVER_ROOTS; -use actix_codec::{AsyncRead, AsyncWrite}; +use actix_rt::net::ActixStream; use actix_service::{Service, ServiceFactory}; use futures_core::{future::LocalBoxFuture, ready}; use log::trace; @@ -44,12 +44,13 @@ impl Clone for RustlsConnector { } } -impl ServiceFactory> for RustlsConnector +impl ServiceFactory> for RustlsConnector where - U: AsyncRead + AsyncWrite + Unpin + fmt::Debug, + T: Address, + U: ActixStream, { type Response = Connection>; - type Error = std::io::Error; + type Error = io::Error; type Config = (); type Service = RustlsConnectorService; type InitError = (); @@ -76,43 +77,55 @@ impl Clone for RustlsConnectorService { impl Service> for RustlsConnectorService where T: Address, - U: AsyncRead + AsyncWrite + Unpin + fmt::Debug, + U: ActixStream, { type Response = Connection>; - type Error = std::io::Error; - type Future = ConnectAsyncExt; + type Error = io::Error; + type Future = RustlsConnectorServiceFuture; actix_service::always_ready!(); - fn call(&self, stream: Connection) -> Self::Future { - trace!("SSL Handshake start for: {:?}", stream.host()); - 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 { - fut: TlsConnector::from(self.connector.clone()).connect(host, io), - stream: Some(stream), + fn call(&self, connection: Connection) -> Self::Future { + trace!("SSL Handshake start for: {:?}", connection.host()); + let (stream, connection) = connection.replace_io(()); + + match DNSNameRef::try_from_ascii_str(connection.host()) { + Ok(host) => RustlsConnectorServiceFuture::Future { + connect: TlsConnector::from(self.connector.clone()).connect(host, stream), + connection: Some(connection), + }, + Err(_) => RustlsConnectorServiceFuture::InvalidDns, } } } -pub struct ConnectAsyncExt { - fut: Connect, - stream: Option>, +pub enum RustlsConnectorServiceFuture { + /// See issue https://github.com/briansmith/webpki/issues/54 + InvalidDns, + Future { + connect: Connect, + connection: Option>, + }, } -impl Future for ConnectAsyncExt +impl Future for RustlsConnectorServiceFuture where T: Address, - U: AsyncRead + AsyncWrite + Unpin + fmt::Debug, + U: ActixStream, { - type Output = Result>, std::io::Error>; + type Output = Result>, io::Error>; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let this = self.get_mut(); - 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_io(stream).1)) + match self.get_mut() { + Self::InvalidDns => Poll::Ready(Err( + io::Error::new(io::ErrorKind::Other, "rustls currently only handles hostname-based connections. See https://github.com/briansmith/webpki/issues/54") + )), + Self::Future { connect, connection } => { + let stream = ready!(Pin::new(connect).poll(cx))?; + let connection = connection.take().unwrap(); + trace!("SSL Handshake success: {:?}", connection.host()); + Poll::Ready(Ok(connection.replace_io(stream).1)) + } + } } } From 12d3942b982b3811f65bc2375f9415245c6406dc Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Fri, 26 Mar 2021 06:03:03 -0700 Subject: [PATCH 23/72] =?UTF-8?q?Remove=20unused=20types=20in=20actix-tls.?= =?UTF-8?q?=20Add=20ActixStream=20impl=20for=20Box ActixStream for Box { + fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll> { + (**self).poll_read_ready(cx) + } + + fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll> { + (**self).poll_write_ready(cx) + } + } } pub mod time { diff --git a/actix-tls/CHANGES.md b/actix-tls/CHANGES.md index 5bf21d4e..d78663d9 100644 --- a/actix-tls/CHANGES.md +++ b/actix-tls/CHANGES.md @@ -3,9 +3,11 @@ ## Unreleased - 2021-xx-xx * Changed `connect::ssl::rustls::RustlsConnectorService` to return error when `DNSNameRef` generation failed instead of panic. [#296] +* Remove `connect::ssl::openssl::OpensslConnectServiceFactory`. [#297] +* Remove `connect::ssl::openssl::OpensslConnectService`. [#297] [#296]: https://github.com/actix/actix-net/pull/296 - +[#297]: https://github.com/actix/actix-net/pull/297 ## 3.0.0-beta.4 - 2021-02-24 * Rename `accept::openssl::{SslStream => TlsStream}`. diff --git a/actix-tls/src/accept/nativetls.rs b/actix-tls/src/accept/nativetls.rs index 614bdad3..53294384 100644 --- a/actix-tls/src/accept/nativetls.rs +++ b/actix-tls/src/accept/nativetls.rs @@ -113,7 +113,7 @@ impl Clone for Acceptor { } } -impl ServiceFactory for Acceptor { +impl ServiceFactory for Acceptor { type Response = TlsStream; type Error = Error; type Config = (); @@ -138,16 +138,7 @@ pub struct NativeTlsAcceptorService { conns: Counter, } -impl Clone for NativeTlsAcceptorService { - fn clone(&self) -> Self { - Self { - acceptor: self.acceptor.clone(), - conns: self.conns.clone(), - } - } -} - -impl Service for NativeTlsAcceptorService { +impl Service for NativeTlsAcceptorService { type Response = TlsStream; type Error = Error; type Future = LocalBoxFuture<'static, Result, Error>>; @@ -162,9 +153,9 @@ impl Service for NativeTlsAcceptorService { fn call(&self, io: T) -> Self::Future { let guard = self.conns.get(); - let this = self.clone(); + let acceptor = self.acceptor.clone(); Box::pin(async move { - let io = this.acceptor.accept(io).await; + let io = acceptor.accept(io).await; drop(guard); io.map(Into::into) }) diff --git a/actix-tls/src/connect/ssl/openssl.rs b/actix-tls/src/connect/ssl/openssl.rs index b1c53f56..b4298fed 100755 --- a/actix-tls/src/connect/ssl/openssl.rs +++ b/actix-tls/src/connect/ssl/openssl.rs @@ -1,13 +1,11 @@ use std::{ - fmt, future::Future, io, pin::Pin, task::{Context, Poll}, }; -use actix_codec::{AsyncRead, AsyncWrite}; -use actix_rt::net::TcpStream; +use actix_rt::net::ActixStream; use actix_service::{Service, ServiceFactory}; use futures_core::{future::LocalBoxFuture, ready}; use log::trace; @@ -15,10 +13,7 @@ use log::trace; pub use openssl::ssl::{Error as SslError, HandshakeError, SslConnector, SslMethod}; pub use tokio_openssl::SslStream; -use crate::connect::resolve::Resolve; -use crate::connect::{ - Address, Connect, ConnectError, ConnectService, ConnectServiceFactory, Connection, Resolver, -}; +use crate::connect::{Address, Connection}; /// OpenSSL connector factory pub struct OpensslConnector { @@ -45,8 +40,8 @@ impl Clone for OpensslConnector { impl ServiceFactory> for OpensslConnector where - T: Address + 'static, - U: AsyncRead + AsyncWrite + Unpin + fmt::Debug + 'static, + T: Address, + U: ActixStream + 'static, { type Response = Connection>; type Error = io::Error; @@ -75,8 +70,8 @@ impl Clone for OpensslConnectorService { impl Service> for OpensslConnectorService where - T: Address + 'static, - U: AsyncRead + AsyncWrite + Unpin + fmt::Debug + 'static, + T: Address, + U: ActixStream, { type Response = Connection>; type Error = io::Error; @@ -112,7 +107,8 @@ pub struct ConnectAsyncExt { impl Future for ConnectAsyncExt where - U: AsyncRead + AsyncWrite + Unpin + fmt::Debug + 'static, + T: Address, + U: ActixStream, { type Output = Result>, io::Error>; @@ -132,115 +128,3 @@ where } } } - -pub struct OpensslConnectServiceFactory { - tcp: ConnectServiceFactory, - openssl: OpensslConnector, -} - -impl OpensslConnectServiceFactory { - /// Construct new OpensslConnectService factory - pub fn new(connector: SslConnector) -> Self { - OpensslConnectServiceFactory { - tcp: ConnectServiceFactory::new(Resolver::Default), - openssl: OpensslConnector::new(connector), - } - } - - /// Construct new connect service with custom DNS resolver - pub fn with_resolver(connector: SslConnector, resolver: impl Resolve + 'static) -> Self { - OpensslConnectServiceFactory { - tcp: ConnectServiceFactory::new(Resolver::new_custom(resolver)), - openssl: OpensslConnector::new(connector), - } - } - - /// Construct OpenSSL connect service - pub fn service(&self) -> OpensslConnectService { - OpensslConnectService { - tcp: self.tcp.service(), - openssl: OpensslConnectorService { - connector: self.openssl.connector.clone(), - }, - } - } -} - -impl Clone for OpensslConnectServiceFactory { - fn clone(&self) -> Self { - OpensslConnectServiceFactory { - tcp: self.tcp.clone(), - openssl: self.openssl.clone(), - } - } -} - -impl ServiceFactory> for OpensslConnectServiceFactory { - type Response = SslStream; - type Error = ConnectError; - type Config = (); - type Service = OpensslConnectService; - type InitError = (); - type Future = LocalBoxFuture<'static, Result>; - - fn new_service(&self, _: ()) -> Self::Future { - let service = self.service(); - Box::pin(async { Ok(service) }) - } -} - -#[derive(Clone)] -pub struct OpensslConnectService { - tcp: ConnectService, - openssl: OpensslConnectorService, -} - -impl Service> for OpensslConnectService { - type Response = SslStream; - type Error = ConnectError; - type Future = OpensslConnectServiceResponse; - - actix_service::always_ready!(); - - fn call(&self, req: Connect) -> Self::Future { - OpensslConnectServiceResponse { - fut1: Some(self.tcp.call(req)), - fut2: None, - openssl: self.openssl.clone(), - } - } -} - -pub struct OpensslConnectServiceResponse { - fut1: Option<>>::Future>, - fut2: Option<>>::Future>, - openssl: OpensslConnectorService, -} - -impl Future for OpensslConnectServiceResponse { - type Output = Result, ConnectError>; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - if let Some(ref mut fut) = self.fut1 { - match ready!(Pin::new(fut).poll(cx)) { - Ok(res) => { - let _ = self.fut1.take(); - self.fut2 = Some(self.openssl.call(res)); - } - Err(e) => return Poll::Ready(Err(e)), - } - } - - if let Some(ref mut fut) = self.fut2 { - 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, - e, - )))), - } - } else { - Poll::Pending - } - } -} diff --git a/actix-tls/src/connect/ssl/rustls.rs b/actix-tls/src/connect/ssl/rustls.rs index b03d4b7f..ee8ad02d 100755 --- a/actix-tls/src/connect/ssl/rustls.rs +++ b/actix-tls/src/connect/ssl/rustls.rs @@ -47,7 +47,7 @@ impl Clone for RustlsConnector { impl ServiceFactory> for RustlsConnector where T: Address, - U: ActixStream, + U: ActixStream + 'static, { type Response = Connection>; type Error = io::Error; From f9262dbec02dbdb777d055ee475634898b4ee248 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Fri, 26 Mar 2021 23:37:01 +0000 Subject: [PATCH 24/72] prevent large shutdown timeout from panicking closes #298 --- actix-server/src/accept.rs | 32 +++++++++++++++++--------------- actix-server/src/builder.rs | 37 ++++++++++++++++++------------------- actix-server/src/worker.rs | 8 ++++---- 3 files changed, 39 insertions(+), 38 deletions(-) diff --git a/actix-server/src/accept.rs b/actix-server/src/accept.rs index c8c1da47..8c64ca38 100644 --- a/actix-server/src/accept.rs +++ b/actix-server/src/accept.rs @@ -2,7 +2,7 @@ use std::time::Duration; use std::{io, thread}; use actix_rt::{ - time::{sleep_until, Instant}, + time::{sleep, Instant}, System, }; use log::{error, info}; @@ -16,14 +16,17 @@ use crate::worker::{Conn, WorkerHandle}; use crate::Token; struct ServerSocketInfo { - // addr for socket. mainly used for logging. + /// Address of 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 + + /// Beware this is the crate token for identify socket and should not be confused + /// with `mio::Token`. token: Token, + lst: MioListener, - // timeout is used to mark the deadline when this socket's listener should be registered again - // after an error. + + /// Timeout is used to mark the deadline when this socket's listener should be registered again + /// after an error. timeout: Option, } @@ -226,10 +229,9 @@ impl Accept { Some(WakerInterest::Stop) => { return self.deregister_all(&mut sockets); } - // waker queue is drained. + // waker queue is drained None => { - // Reset the WakerQueue before break so it does not grow - // infinitely. + // Reset the WakerQueue before break so it does not grow infinitely WakerQueue::reset(&mut guard); break 'waker; } @@ -328,8 +330,8 @@ impl Accept { } Err(tmp) => { // 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. + // `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.handles.swap_remove(self.next); @@ -403,15 +405,15 @@ impl Accept { error!("Can not deregister server socket {}", err); } - // 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. + // 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)); // after the sleep a Timer interest is sent to Accept Poll let waker = self.waker.clone(); System::current().arbiter().spawn(async move { - sleep_until(Instant::now() + Duration::from_millis(510)).await; + sleep(Duration::from_millis(510)).await; waker.wake(WakerInterest::Timer); }); diff --git a/actix-server/src/builder.rs b/actix-server/src/builder.rs index 78a1323d..c20bb4f5 100644 --- a/actix-server/src/builder.rs +++ b/actix-server/src/builder.rs @@ -1,12 +1,12 @@ -use std::future::Future; -use std::pin::Pin; -use std::task::{Context, Poll}; -use std::time::Duration; -use std::{io, mem}; +use std::{ + future::Future, + io, mem, + pin::Pin, + task::{Context, Poll}, + time::Duration, +}; -use actix_rt::net::TcpStream; -use actix_rt::time::{sleep_until, Instant}; -use actix_rt::{self as rt, System}; +use actix_rt::{self as rt, net::TcpStream, time::sleep, System}; use log::{error, info}; use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver}; use tokio::sync::oneshot; @@ -122,13 +122,13 @@ impl ServerBuilder { self } - /// Stop actix system. + /// Stop Actix system. pub fn system_exit(mut self) -> Self { self.exit = true; self } - /// Disable signal handling + /// Disable signal handling. pub fn disable_signals(mut self) -> Self { self.no_signals = true; self @@ -136,9 +136,8 @@ impl ServerBuilder { /// Timeout for graceful workers shutdown in seconds. /// - /// After receiving a stop signal, workers have this much time to finish - /// serving requests. Workers still alive after the timeout are force - /// dropped. + /// After receiving a stop signal, workers have this much time to finish serving requests. + /// Workers still alive after the timeout are force dropped. /// /// By default shutdown timeout sets to 30 seconds. pub fn shutdown_timeout(mut self, sec: u64) -> Self { @@ -147,11 +146,10 @@ impl ServerBuilder { self } - /// Execute external configuration as part of the server building - /// process. + /// Execute external configuration as part of the server building process. /// - /// This function is useful for moving parts of configuration to a - /// different module or even library. + /// This function is useful for moving parts of configuration to a different module or + /// even library. pub fn configure(mut self, f: F) -> io::Result where F: Fn(&mut ServiceConfig) -> io::Result<()>, @@ -268,6 +266,7 @@ impl ServerBuilder { self.sockets .push((token, name.as_ref().to_string(), MioListener::from(lst))); + Ok(self) } @@ -393,7 +392,7 @@ impl ServerBuilder { } if exit { rt::spawn(async { - sleep_until(Instant::now() + Duration::from_millis(300)).await; + sleep(Duration::from_millis(300)).await; System::current().stop(); }); } @@ -402,7 +401,7 @@ impl ServerBuilder { // we need to stop system if server was spawned if self.exit { rt::spawn(async { - sleep_until(Instant::now() + Duration::from_millis(300)).await; + sleep(Duration::from_millis(300)).await; System::current().stop(); }); } diff --git a/actix-server/src/worker.rs b/actix-server/src/worker.rs index defc7306..aa6d31fc 100644 --- a/actix-server/src/worker.rs +++ b/actix-server/src/worker.rs @@ -5,7 +5,7 @@ use std::sync::Arc; use std::task::{Context, Poll}; use std::time::Duration; -use actix_rt::time::{sleep_until, Instant, Sleep}; +use actix_rt::time::{sleep, Sleep}; use actix_rt::{spawn, Arbiter}; use actix_utils::counter::Counter; use futures_core::future::LocalBoxFuture; @@ -361,8 +361,8 @@ impl Future for ServerWorker { if num != 0 { 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.config.shutdown_timeout)), + Box::pin(sleep(Duration::from_secs(1))), + Box::pin(sleep(self.config.shutdown_timeout)), Some(result), ); } else { @@ -438,7 +438,7 @@ impl Future for ServerWorker { // sleep for 1 second and then check again if t1.as_mut().poll(cx).is_ready() { - *t1 = Box::pin(sleep_until(Instant::now() + Duration::from_secs(1))); + *t1 = Box::pin(sleep(Duration::from_secs(1))); let _ = t1.as_mut().poll(cx); } From bb27bac2168eb886229d4354af3d30e32311038a Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Fri, 26 Mar 2021 17:20:17 -0700 Subject: [PATCH 25/72] Add native tls support for actix_tls::connect module (#295) Co-authored-by: Rob Ede --- actix-router/src/url.rs | 10 +-- actix-server/CHANGES.md | 3 + actix-server/src/test_server.rs | 2 +- actix-service/src/map_err.rs | 2 +- actix-tls/CHANGES.md | 4 + actix-tls/src/accept/mod.rs | 2 +- .../accept/{nativetls.rs => native_tls.rs} | 0 actix-tls/src/connect/ssl/mod.rs | 3 + actix-tls/src/connect/ssl/native_tls.rs | 88 +++++++++++++++++++ 9 files changed, 105 insertions(+), 9 deletions(-) rename actix-tls/src/accept/{nativetls.rs => native_tls.rs} (100%) create mode 100644 actix-tls/src/connect/ssl/native_tls.rs diff --git a/actix-router/src/url.rs b/actix-router/src/url.rs index d2dd7a19..f669da99 100644 --- a/actix-router/src/url.rs +++ b/actix-router/src/url.rs @@ -170,13 +170,11 @@ impl Quoter { idx += 1; } - if let Some(data) = cloned { - // Unsafe: we get data from http::Uri, which does utf-8 checks already + cloned.map(|data| { + // SAFETY: we get data from http::Uri, which does UTF-8 checks already // this code only decodes valid pct encoded values - Some(unsafe { String::from_utf8_unchecked(data) }) - } else { - None - } + unsafe { String::from_utf8_unchecked(data) } + }) } } diff --git a/actix-server/CHANGES.md b/actix-server/CHANGES.md index 5eca1f91..aaa38911 100644 --- a/actix-server/CHANGES.md +++ b/actix-server/CHANGES.md @@ -1,6 +1,9 @@ # Changes ## Unreleased - 2021-xx-xx +* Prevent panic when shutdown_timeout is very large. [f9262db] + +[f9262db]: https://github.com/actix/actix-net/commit/f9262db ## 2.0.0-beta.3 - 2021-02-06 diff --git a/actix-server/src/test_server.rs b/actix-server/src/test_server.rs index 864f391c..0611cf4b 100644 --- a/actix-server/src/test_server.rs +++ b/actix-server/src/test_server.rs @@ -92,10 +92,10 @@ impl TestServer { let port = addr.port(); TestServerRuntime { - system, addr, host, port, + system, } } diff --git a/actix-service/src/map_err.rs b/actix-service/src/map_err.rs index ff25c4f7..7b1ac2ab 100644 --- a/actix-service/src/map_err.rs +++ b/actix-service/src/map_err.rs @@ -180,7 +180,7 @@ where F: Fn(A::Error) -> E, { fn new(fut: A::Future, f: F) -> Self { - MapErrServiceFuture { f, fut } + MapErrServiceFuture { fut, f } } } diff --git a/actix-tls/CHANGES.md b/actix-tls/CHANGES.md index d78663d9..400b1763 100644 --- a/actix-tls/CHANGES.md +++ b/actix-tls/CHANGES.md @@ -5,10 +5,14 @@ generation failed instead of panic. [#296] * Remove `connect::ssl::openssl::OpensslConnectServiceFactory`. [#297] * Remove `connect::ssl::openssl::OpensslConnectService`. [#297] +* Add `connect::ssl::native_tls` module for native tls support. [#295] +* Rename `accept::{nativetls => native_tls}`. [#295] +[#295]: https://github.com/actix/actix-net/pull/295 [#296]: https://github.com/actix/actix-net/pull/296 [#297]: https://github.com/actix/actix-net/pull/297 + ## 3.0.0-beta.4 - 2021-02-24 * Rename `accept::openssl::{SslStream => TlsStream}`. * Add `connect::Connect::set_local_addr` to attach local `IpAddr`. [#282] diff --git a/actix-tls/src/accept/mod.rs b/actix-tls/src/accept/mod.rs index 8b1fe47c..dd939e4a 100644 --- a/actix-tls/src/accept/mod.rs +++ b/actix-tls/src/accept/mod.rs @@ -16,7 +16,7 @@ pub mod openssl; pub mod rustls; #[cfg(feature = "native-tls")] -pub mod nativetls; +pub mod native_tls; pub(crate) static MAX_CONN: AtomicUsize = AtomicUsize::new(256); diff --git a/actix-tls/src/accept/nativetls.rs b/actix-tls/src/accept/native_tls.rs similarity index 100% rename from actix-tls/src/accept/nativetls.rs rename to actix-tls/src/accept/native_tls.rs diff --git a/actix-tls/src/connect/ssl/mod.rs b/actix-tls/src/connect/ssl/mod.rs index 8ace5ef1..6e0e8aac 100644 --- a/actix-tls/src/connect/ssl/mod.rs +++ b/actix-tls/src/connect/ssl/mod.rs @@ -5,3 +5,6 @@ pub mod openssl; #[cfg(feature = "rustls")] pub mod rustls; + +#[cfg(feature = "native-tls")] +pub mod native_tls; diff --git a/actix-tls/src/connect/ssl/native_tls.rs b/actix-tls/src/connect/ssl/native_tls.rs new file mode 100644 index 00000000..de08ea2a --- /dev/null +++ b/actix-tls/src/connect/ssl/native_tls.rs @@ -0,0 +1,88 @@ +use std::io; + +use actix_rt::net::ActixStream; +use actix_service::{Service, ServiceFactory}; +use futures_core::future::LocalBoxFuture; +use log::trace; +use tokio_native_tls::{TlsConnector as TokioNativetlsConnector, TlsStream}; + +pub use tokio_native_tls::native_tls::TlsConnector; + +use crate::connect::{Address, Connection}; + +/// Native-tls connector factory and service +pub struct NativetlsConnector { + connector: TokioNativetlsConnector, +} + +impl NativetlsConnector { + pub fn new(connector: TlsConnector) -> Self { + Self { + connector: TokioNativetlsConnector::from(connector), + } + } +} + +impl NativetlsConnector { + pub fn service(connector: TlsConnector) -> Self { + Self::new(connector) + } +} + +impl Clone for NativetlsConnector { + fn clone(&self) -> Self { + Self { + connector: self.connector.clone(), + } + } +} + +impl ServiceFactory> for NativetlsConnector +where + U: ActixStream + 'static, +{ + type Response = Connection>; + type Error = io::Error; + type Config = (); + type Service = Self; + type InitError = (); + type Future = LocalBoxFuture<'static, Result>; + + fn new_service(&self, _: ()) -> Self::Future { + let connector = self.clone(); + Box::pin(async { Ok(connector) }) + } +} + +// NativetlsConnector is both it's ServiceFactory and Service impl type. +// As the factory and service share the same type and state. +impl Service> for NativetlsConnector +where + T: Address, + U: ActixStream + 'static, +{ + type Response = Connection>; + type Error = io::Error; + type Future = LocalBoxFuture<'static, Result>; + + actix_service::always_ready!(); + + fn call(&self, stream: Connection) -> Self::Future { + let (io, stream) = stream.replace_io(()); + let connector = self.connector.clone(); + Box::pin(async move { + trace!("SSL Handshake start for: {:?}", stream.host()); + connector + .connect(stream.host(), io) + .await + .map(|res| { + trace!("SSL Handshake success: {:?}", stream.host()); + stream.replace_io(res).1 + }) + .map_err(|e| { + trace!("SSL Handshake error: {:?}", e); + io::Error::new(io::ErrorKind::Other, format!("{}", e)) + }) + }) + } +} From 4544562e1b8570257f391e795cdd310c9f437987 Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Sat, 27 Mar 2021 14:03:24 -0700 Subject: [PATCH 26/72] Remove unused TcpConnectService (#299) --- actix-tls/CHANGES.md | 3 ++ actix-tls/src/connect/connector.rs | 71 ++++++++++++++---------------- actix-tls/src/connect/mod.rs | 8 ++-- actix-tls/src/connect/service.rs | 51 +-------------------- 4 files changed, 41 insertions(+), 92 deletions(-) diff --git a/actix-tls/CHANGES.md b/actix-tls/CHANGES.md index 400b1763..067c4fe8 100644 --- a/actix-tls/CHANGES.md +++ b/actix-tls/CHANGES.md @@ -7,10 +7,13 @@ * Remove `connect::ssl::openssl::OpensslConnectService`. [#297] * Add `connect::ssl::native_tls` module for native tls support. [#295] * Rename `accept::{nativetls => native_tls}`. [#295] +* Remove `connect::TcpConnectService` type. service caller expect a `TcpStream` should use + `connect::ConnectService` instead and call `Connection::into_parts`. [#299] [#295]: https://github.com/actix/actix-net/pull/295 [#296]: https://github.com/actix/actix-net/pull/296 [#297]: https://github.com/actix/actix-net/pull/297 +[#299]: https://github.com/actix/actix-net/pull/299 ## 3.0.0-beta.4 - 2021-02-24 diff --git a/actix-tls/src/connect/connector.rs b/actix-tls/src/connect/connector.rs index 8f32270f..9438404e 100755 --- a/actix-tls/src/connect/connector.rs +++ b/actix-tls/src/connect/connector.rs @@ -72,7 +72,7 @@ pub enum TcpConnectorResponse { port: u16, local_addr: Option, addrs: Option>, - stream: Option>>, + stream: ReusableBoxFuture>, }, Error(Option), } @@ -103,18 +103,22 @@ impl TcpConnectorResponse { port, local_addr, addrs: None, - stream: Some(ReusableBoxFuture::new(connect(addr, local_addr))), + stream: ReusableBoxFuture::new(connect(addr, local_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 { - req: Some(req), - port, - local_addr, - addrs: Some(addrs), - stream: None, - }, + ConnectAddrs::Multi(mut addrs) => { + let addr = addrs.pop_front().unwrap(); + + TcpConnectorResponse::Response { + req: Some(req), + port, + local_addr, + addrs: Some(addrs), + stream: ReusableBoxFuture::new(connect(addr, local_addr)), + } + } } } } @@ -133,40 +137,31 @@ impl Future for TcpConnectorResponse { addrs, stream, } => loop { - if let Some(new) = stream.as_mut() { - match ready!(new.poll(cx)) { - Ok(sock) => { - let req = req.take().unwrap(); - trace!( - "TCP connector: successfully connected to {:?} - {:?}", - req.hostname(), - sock.peer_addr() - ); - return Poll::Ready(Ok(Connection::new(sock, req))); - } + match ready!(stream.poll(cx)) { + Ok(sock) => { + let req = req.take().unwrap(); + trace!( + "TCP connector: successfully connected to {:?} - {:?}", + req.hostname(), + sock.peer_addr() + ); + return Poll::Ready(Ok(Connection::new(sock, req))); + } - Err(err) => { - trace!( - "TCP connector: failed to connect to {:?} port: {}", - req.as_ref().unwrap().hostname(), - port, - ); + Err(err) => { + trace!( + "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))); - } + if let Some(addr) = addrs.as_mut().and_then(|addrs| addrs.pop_front()) { + stream.set(connect(addr, *local_addr)); + } else { + return Poll::Ready(Err(ConnectError::Io(err))); } } } - - // try to connect - let addr = addrs.as_mut().unwrap().pop_front().unwrap(); - - let fut = connect(addr, *local_addr); - match stream { - Some(rbf) => rbf.set(fut), - None => *stream = Some(ReusableBoxFuture::new(fut)), - } }, } } diff --git a/actix-tls/src/connect/mod.rs b/actix-tls/src/connect/mod.rs index 4010e3cb..ad4f40a3 100644 --- a/actix-tls/src/connect/mod.rs +++ b/actix-tls/src/connect/mod.rs @@ -26,20 +26,20 @@ pub mod ssl; mod uri; use actix_rt::net::TcpStream; -use actix_service::{pipeline, pipeline_factory, Service, ServiceFactory}; +use actix_service::{Service, ServiceFactory}; pub use self::connect::{Address, Connect, Connection}; pub use self::connector::{TcpConnector, TcpConnectorFactory}; pub use self::error::ConnectError; pub use self::resolve::{Resolve, Resolver, ResolverFactory}; -pub use self::service::{ConnectService, ConnectServiceFactory, TcpConnectService}; +pub use self::service::{ConnectService, ConnectServiceFactory}; /// Create TCP connector service. pub fn new_connector( resolver: Resolver, ) -> impl Service, Response = Connection, Error = ConnectError> + Clone { - pipeline(resolver).and_then(TcpConnector) + ConnectServiceFactory::new(resolver).service() } /// Create TCP connector service factory. @@ -52,7 +52,7 @@ pub fn new_connector_factory( Error = ConnectError, InitError = (), > + Clone { - pipeline_factory(ResolverFactory::new(resolver)).and_then(TcpConnectorFactory) + ConnectServiceFactory::new(resolver) } /// Create connector service with default parameters. diff --git a/actix-tls/src/connect/service.rs b/actix-tls/src/connect/service.rs index 98765ca1..9961498e 100755 --- a/actix-tls/src/connect/service.rs +++ b/actix-tls/src/connect/service.rs @@ -34,14 +34,6 @@ impl ConnectServiceFactory { resolver: self.resolver.service(), } } - - /// Construct new tcp stream service - pub fn tcp_service(&self) -> TcpConnectService { - TcpConnectService { - tcp: self.tcp.service(), - resolver: self.resolver.service(), - } - } } impl Clone for ConnectServiceFactory { @@ -63,7 +55,7 @@ impl ServiceFactory> for ConnectServiceFactory { fn new_service(&self, _: ()) -> Self::Future { let service = self.service(); - Box::pin(async move { Ok(service) }) + Box::pin(async { Ok(service) }) } } @@ -135,44 +127,3 @@ impl Future for ConnectServiceResponse { } } } - -#[derive(Clone)] -pub struct TcpConnectService { - tcp: TcpConnector, - resolver: Resolver, -} - -impl Service> for TcpConnectService { - type Response = TcpStream; - type Error = ConnectError; - type Future = TcpConnectServiceResponse; - - actix_service::always_ready!(); - - fn call(&self, req: Connect) -> Self::Future { - TcpConnectServiceResponse { - fut: ConnectFuture::Resolve(self.resolver.call(req)), - tcp: self.tcp, - } - } -} - -pub struct TcpConnectServiceResponse { - fut: ConnectFuture, - tcp: TcpConnector, -} - -impl Future for TcpConnectServiceResponse { - type Output = Result; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - 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)), - } - } - } -} From 3cf1c548fd4c546fb214dd67e2c63a05441e4412 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Mon, 29 Mar 2021 06:57:14 +0100 Subject: [PATCH 27/72] prepare actix-rt release 2.2.0 --- actix-rt/CHANGES.md | 95 ++++++++++++-------------------------------- actix-rt/Cargo.toml | 2 +- actix-rt/README.md | 4 +- actix-rt/src/lib.rs | 1 + actix-tls/Cargo.toml | 4 +- 5 files changed, 31 insertions(+), 75 deletions(-) diff --git a/actix-rt/CHANGES.md b/actix-rt/CHANGES.md index 83ecc5ed..459d91a7 100644 --- a/actix-rt/CHANGES.md +++ b/actix-rt/CHANGES.md @@ -1,7 +1,12 @@ # Changes ## Unreleased - 2021-xx-xx -* `ActixStream::{poll_read_ready, poll_write_ready}` would return `Ready` in Ok variant. [#293] + + +## 2.2.0 - 2021-03-29 +* **BREAKING** `ActixStream::{poll_read_ready, poll_write_ready}` methods now return + `Ready` object in ok variant. [#293] + * Breakage is acceptable since `ActixStream` was not intended to be public. [#293] https://github.com/actix/actix-net/pull/293 @@ -67,10 +72,7 @@ ## 2.0.0-beta.1 - 2020-12-28 -### Added * Add `System::attach_to_tokio` method. [#173] - -### Changed * 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] @@ -78,27 +80,19 @@ * 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 `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 - -### Fixed +## 1.1.1 - 2020-04-30 * Fix memory leak due to [#94] (see [#129] for more detail) [#129]: https://github.com/actix/actix-net/issues/129 -## [1.1.0] - 2020-04-08 - -**This version has been yanked.** - -### Added +## 1.1.0 - 2020-04-08 (YANKED) * Expose `System::is_set` to check if current system has ben started [#99] * Add `Arbiter::is_running` to check if event loop is running [#124] * Add `Arbiter::local_join` associated function @@ -108,96 +102,57 @@ [#99]: https://github.com/actix/actix-net/pull/99 [#124]: https://github.com/actix/actix-net/pull/124 -## [1.0.0] - 2019-12-11 +## 1.0.0 - 2019-12-11 * Update dependencies -## [1.0.0-alpha.3] - 2019-12-07 - -### Fixed +## 1.0.0-alpha.3 - 2019-12-07 +* Migrate to tokio 0.2 * Fix compilation on non-unix platforms -### Changed - -* Migrate to tokio 0.2 - - -## [1.0.0-alpha.2] - 2019-12-02 - -Added +## 1.0.0-alpha.2 - 2019-12-02 * Export `main` and `test` attribute macros - * Export `time` module (re-export of tokio-timer) - * Export `net` module (re-export of tokio-net) -## [1.0.0-alpha.1] - 2019-11-22 - -### Changed - +## 1.0.0-alpha.1 - 2019-11-22 * Migrate to std::future and tokio 0.2 -## [0.2.6] - 2019-11-14 - -### Fixed - +## 0.2.6 - 2019-11-14 +* Allow to join arbiter's thread. #60 * Fix arbiter's thread panic message. -### Added - -* Allow to join arbiter's thread. #60 - - -## [0.2.5] - 2019-09-02 - -### Added +## 0.2.5 - 2019-09-02 * Add arbiter specific storage -## [0.2.4] - 2019-07-17 - -### Changed - +## 0.2.4 - 2019-07-17 * Avoid a copy of the Future when initializing the Box. #29 -## [0.2.3] - 2019-06-22 - -### Added - -* Allow to start System using exsiting CurrentThread Handle #22 +## 0.2.3 - 2019-06-22 +* Allow to start System using existing CurrentThread Handle #22 -## [0.2.2] - 2019-03-28 - -### Changed - +## 0.2.2 - 2019-03-28 * Moved `blocking` module to `actix-threadpool` crate -## [0.2.1] - 2019-03-11 - -### Added - +## 0.2.1 - 2019-03-11 * Added `blocking` module - -* Arbiter::exec_fn - execute fn on the arbiter's thread - -* Arbiter::exec - execute fn on the arbiter's thread and wait result +* Added `Arbiter::exec_fn` - execute fn on the arbiter's thread +* Added `Arbiter::exec` - execute fn on the arbiter's thread and wait result -## [0.2.0] - 2019-03-06 - +## 0.2.0 - 2019-03-06 * `run` method returns `io::Result<()>` - * Removed `Handle` -## [0.1.0] - 2018-12-09 - +## 0.1.0 - 2018-12-09 * Initial release diff --git a/actix-rt/Cargo.toml b/actix-rt/Cargo.toml index 126056ec..f4a90d2c 100644 --- a/actix-rt/Cargo.toml +++ b/actix-rt/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-rt" -version = "2.1.0" +version = "2.2.0" authors = [ "Nikolay Kim ", "Rob Ede ", diff --git a/actix-rt/README.md b/actix-rt/README.md index f9a3ed31..4ad75f09 100644 --- a/actix-rt/README.md +++ b/actix-rt/README.md @@ -3,11 +3,11 @@ > Tokio-based single-threaded async runtime for the Actix ecosystem. [![crates.io](https://img.shields.io/crates/v/actix-rt?label=latest)](https://crates.io/crates/actix-rt) -[![Documentation](https://docs.rs/actix-rt/badge.svg?version=2.1.0)](https://docs.rs/actix-rt/2.1.0) +[![Documentation](https://docs.rs/actix-rt/badge.svg?version=2.2.0)](https://docs.rs/actix-rt/2.2.0) [![Version](https://img.shields.io/badge/rustc-1.46+-ab6000.svg)](https://blog.rust-lang.org/2020/03/12/Rust-1.46.html) ![MIT or Apache 2.0 licensed](https://img.shields.io/crates/l/actix-rt.svg)
-[![dependency status](https://deps.rs/crate/actix-rt/2.1.0/status.svg)](https://deps.rs/crate/actix-rt/2.1.0) +[![dependency status](https://deps.rs/crate/actix-rt/2.2.0/status.svg)](https://deps.rs/crate/actix-rt/2.2.0) ![Download](https://img.shields.io/crates/d/actix-rt.svg) [![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/WghFtEH6Hb) diff --git a/actix-rt/src/lib.rs b/actix-rt/src/lib.rs index afbe8642..4454b3c4 100644 --- a/actix-rt/src/lib.rs +++ b/actix-rt/src/lib.rs @@ -87,6 +87,7 @@ pub mod net { pub use tokio::net::{UnixDatagram, UnixListener, UnixStream}; /// Extension trait over async read+write types that can also signal readiness. + #[doc(hidden)] pub trait ActixStream: AsyncRead + AsyncWrite + Unpin { /// Poll stream and check read readiness of Self. /// diff --git a/actix-tls/Cargo.toml b/actix-tls/Cargo.toml index d14c65ac..40a116b0 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.1.0", default-features = false } +actix-rt = { version = "2.2.0", default-features = false } actix-service = "2.0.0-beta.5" actix-utils = "3.0.0-beta.2" @@ -63,7 +63,7 @@ webpki-roots = { version = "0.21", optional = true } tokio-native-tls = { version = "0.3", optional = true } [dev-dependencies] -actix-rt = "2.1.0" +actix-rt = "2.2.0" actix-server = "2.0.0-beta.3" bytes = "1" env_logger = "0.8" From 0ee8d032b6ad6c127afb675d58c909741b278c5c Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Mon, 29 Mar 2021 06:57:47 +0100 Subject: [PATCH 28/72] prepare actix-tls release 3.0.0-beta.5 --- actix-tls/CHANGES.md | 3 +++ actix-tls/Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/actix-tls/CHANGES.md b/actix-tls/CHANGES.md index 067c4fe8..28dc612a 100644 --- a/actix-tls/CHANGES.md +++ b/actix-tls/CHANGES.md @@ -1,6 +1,9 @@ # Changes ## Unreleased - 2021-xx-xx + + +## 3.0.0-beta.5 - 2021-03-29 * Changed `connect::ssl::rustls::RustlsConnectorService` to return error when `DNSNameRef` generation failed instead of panic. [#296] * Remove `connect::ssl::openssl::OpensslConnectServiceFactory`. [#297] diff --git a/actix-tls/Cargo.toml b/actix-tls/Cargo.toml index 40a116b0..3f767a28 100755 --- a/actix-tls/Cargo.toml +++ b/actix-tls/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-tls" -version = "3.0.0-beta.4" +version = "3.0.0-beta.5" authors = ["Nikolay Kim "] description = "TLS acceptor and connector services for Actix ecosystem" keywords = ["network", "tls", "ssl", "async", "transport"] From 26a5af70cbad142680d4e0924d3d6623514a3816 Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Mon, 29 Mar 2021 00:19:37 -0700 Subject: [PATCH 29/72] reduce branch in Accept::accept method (#300) --- actix-server/src/accept.rs | 87 ++++++++++++++++++-------------------- actix-server/src/socket.rs | 6 +-- 2 files changed, 45 insertions(+), 48 deletions(-) diff --git a/actix-server/src/accept.rs b/actix-server/src/accept.rs index 8c64ca38..f5484434 100644 --- a/actix-server/src/accept.rs +++ b/actix-server/src/accept.rs @@ -208,17 +208,7 @@ impl Accept { } 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) - } - } - }); + self.deregister_all(&mut sockets); } Some(WakerInterest::Resume) => { drop(guard); @@ -295,10 +285,18 @@ impl Accept { self.poll.registry().deregister(&mut info.lst) } + fn deregister_logged(&self, info: &mut ServerSocketInfo) { + match self.deregister(info) { + Ok(_) => info!("Paused accepting connections on {}", info.addr), + Err(e) => { + error!("Can not deregister server socket {}", e) + } + } + } + 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); + self.deregister_logged(info); }); } @@ -388,43 +386,42 @@ impl Accept { fn accept(&mut self, sockets: &mut Slab, token: usize) { loop { - let msg = if let Some(info) = sockets.get_mut(token) { - match info.lst.accept() { - Ok(Some((io, addr))) => Conn { + let info = sockets + .get_mut(token) + .expect("ServerSocketInfo is removed from Slab"); + + match info.lst.accept() { + Ok((io, addr)) => { + let msg = Conn { io, token: info.token, peer: Some(addr), - }, - Ok(None) => return, - 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.deregister(info) { - error!("Can not deregister server socket {}", err); - } - - // 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)); - - // after the sleep a Timer interest is sent to Accept Poll - let waker = self.waker.clone(); - System::current().arbiter().spawn(async move { - sleep(Duration::from_millis(510)).await; - waker.wake(WakerInterest::Timer); - }); - - return; - } + }; + self.accept_one(sockets, msg); } - } else { - return; - }; + Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => return, + Err(ref e) if connection_error(e) => continue, + Err(e) => { + error!("Error accepting connection: {}", e); - self.accept_one(sockets, msg); + // deregister listener temporary + self.deregister_logged(info); + + // 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)); + + // after the sleep a Timer interest is sent to Accept Poll + let waker = self.waker.clone(); + System::current().arbiter().spawn(async move { + sleep(Duration::from_millis(510)).await; + waker.wake(WakerInterest::Timer); + }); + + return; + } + }; } } } diff --git a/actix-server/src/socket.rs b/actix-server/src/socket.rs index 416e253b..baf02cbe 100644 --- a/actix-server/src/socket.rs +++ b/actix-server/src/socket.rs @@ -40,15 +40,15 @@ impl MioListener { } } - pub(crate) fn accept(&self) -> io::Result> { + pub(crate) fn accept(&self) -> io::Result<(MioStream, SocketAddr)> { match *self { MioListener::Tcp(ref lst) => lst .accept() - .map(|(stream, addr)| Some((MioStream::Tcp(stream), SocketAddr::Tcp(addr)))), + .map(|(stream, addr)| (MioStream::Tcp(stream), SocketAddr::Tcp(addr))), #[cfg(unix)] MioListener::Uds(ref lst) => lst .accept() - .map(|(stream, addr)| Some((MioStream::Uds(stream), SocketAddr::Uds(addr)))), + .map(|(stream, addr)| (MioStream::Uds(stream), SocketAddr::Uds(addr))), } } } From 8becb0db70765401d4c608cfcf385f426ca4a020 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Tue, 30 Mar 2021 13:39:10 +0100 Subject: [PATCH 30/72] refactor crates for better api stability (#301) --- .cargo/config.toml | 3 + Cargo.toml | 4 + actix-macros/Cargo.toml | 2 +- actix-server/Cargo.toml | 3 +- actix-server/tests/test_server.rs | 5 +- actix-service/src/macros.rs | 4 +- actix-tracing/Cargo.toml | 2 +- actix-utils/CHANGES.md | 14 +- actix-utils/Cargo.toml | 9 +- actix-utils/src/counter.rs | 6 +- actix-utils/src/dispatcher.rs | 336 ------------------ actix-utils/src/future/mod.rs | 7 + actix-utils/src/{ => future}/poll_fn.rs | 14 +- actix-utils/src/future/ready.rs | 122 +++++++ actix-utils/src/lib.rs | 8 +- actix-utils/src/timeout.rs | 255 ------------- local-channel/CHANGES.md | 7 + local-channel/Cargo.toml | 22 ++ local-channel/src/lib.rs | 3 + {actix-utils => local-channel}/src/mpsc.rs | 18 +- local-waker/CHANGES.md | 7 + local-waker/Cargo.toml | 16 + .../src/task.rs => local-waker/src/lib.rs | 6 + 23 files changed, 235 insertions(+), 638 deletions(-) create mode 100644 .cargo/config.toml delete mode 100644 actix-utils/src/dispatcher.rs create mode 100644 actix-utils/src/future/mod.rs rename actix-utils/src/{ => future}/poll_fn.rs (76%) create mode 100644 actix-utils/src/future/ready.rs delete mode 100644 actix-utils/src/timeout.rs create mode 100644 local-channel/CHANGES.md create mode 100644 local-channel/Cargo.toml create mode 100644 local-channel/src/lib.rs rename {actix-utils => local-channel}/src/mpsc.rs (95%) create mode 100644 local-waker/CHANGES.md create mode 100644 local-waker/Cargo.toml rename actix-utils/src/task.rs => local-waker/src/lib.rs (95%) diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 00000000..77788410 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,3 @@ +[alias] +lint = "hack --clean-per-run clippy --workspace --tests --examples" +chk = "hack check --workspace --tests --examples" diff --git a/Cargo.toml b/Cargo.toml index 78e54d35..5bf72300 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,8 @@ members = [ "actix-tracing", "actix-utils", "bytestring", + "local-channel", + "local-waker", ] [patch.crates-io] @@ -23,3 +25,5 @@ actix-tls = { path = "actix-tls" } actix-tracing = { path = "actix-tracing" } actix-utils = { path = "actix-utils" } bytestring = { path = "bytestring" } +local-channel = { path = "local-channel" } +local-waker = { path = "local-waker" } diff --git a/actix-macros/Cargo.toml b/actix-macros/Cargo.toml index 0555f990..1664fc27 100644 --- a/actix-macros/Cargo.toml +++ b/actix-macros/Cargo.toml @@ -19,5 +19,5 @@ syn = { version = "^1", features = ["full"] } [dev-dependencies] actix-rt = "2.0.0" -futures-util = { version = "0.3", default-features = false } +futures-util = { version = "0.3.7", default-features = false } trybuild = "1" diff --git a/actix-server/Cargo.toml b/actix-server/Cargo.toml index 6d763d79..620cbf51 100755 --- a/actix-server/Cargo.toml +++ b/actix-server/Cargo.toml @@ -22,7 +22,6 @@ path = "src/lib.rs" default = [] [dependencies] -actix-codec = "0.4.0-beta.1" actix-rt = { version = "2.0.0", default-features = false } actix-service = "2.0.0-beta.5" actix-utils = "3.0.0-beta.2" @@ -35,7 +34,9 @@ slab = "0.4" tokio = { version = "1.2", features = ["sync"] } [dev-dependencies] +actix-codec = "0.4.0-beta.1" 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/tests/test_server.rs b/actix-server/tests/test_server.rs index 86ec25e6..6d413eea 100644 --- a/actix-server/tests/test_server.rs +++ b/actix-server/tests/test_server.rs @@ -4,7 +4,8 @@ use std::{net, thread, time}; use actix_server::Server; use actix_service::fn_service; -use futures_util::future::{lazy, ok}; +use actix_utils::future::ok; +use futures_util::future::lazy; fn unused_addr() -> net::SocketAddr { let addr: net::SocketAddr = "127.0.0.1:0".parse().unwrap(); @@ -30,6 +31,7 @@ fn test_bind() { .unwrap() .run() })); + let _ = tx.send((srv, actix_rt::System::current())); let _ = sys.run(); }); @@ -175,6 +177,7 @@ fn test_configure() { .workers(1) .run() })); + let _ = tx.send((srv, actix_rt::System::current())); let _ = sys.run(); }); diff --git a/actix-service/src/macros.rs b/actix-service/src/macros.rs index 4a083895..d2ae9dbf 100644 --- a/actix-service/src/macros.rs +++ b/actix-service/src/macros.rs @@ -147,8 +147,8 @@ mod tests { forward_ready!(inner); - fn call(&self, req: ()) -> Self::Future { - self.inner.call(req) + fn call(&self, _: ()) -> Self::Future { + self.inner.call(()) } } diff --git a/actix-tracing/Cargo.toml b/actix-tracing/Cargo.toml index 7f043f4b..992edbf4 100644 --- a/actix-tracing/Cargo.toml +++ b/actix-tracing/Cargo.toml @@ -18,7 +18,7 @@ path = "src/lib.rs" [dependencies] actix-service = "2.0.0-beta.5" -futures-util = { version = "0.3.4", default-features = false } +futures-util = { version = "0.3.7", default-features = false } tracing = "0.1" tracing-futures = "0.2" diff --git a/actix-utils/CHANGES.md b/actix-utils/CHANGES.md index 8d97b741..c911a211 100644 --- a/actix-utils/CHANGES.md +++ b/actix-utils/CHANGES.md @@ -1,15 +1,13 @@ # Changes ## Unreleased - 2021-xx-xx -* Add `async fn mpsc::Receiver::recv`. [#286] -* `SendError` inner field is now public. [#286] -* Rename `Dispatcher::{get_sink => tx}`. [#286] -* Rename `Dispatcher::{get_ref => service}`. [#286] -* Rename `Dispatcher::{get_mut => service_mut}`. [#286] -* Rename `Dispatcher::{get_framed => framed}`. [#286] -* Rename `Dispatcher::{get_framed_mut => framed_mut}`. [#286] +* Moved `mpsc` to own crate `local-channel`. [#301] +* Moved `task::LocalWaker` to own crate `local-waker`. [#301] +* Remove `timeout` module. [#301] +* Remove `dispatcher` module. [#301] +* Expose `future` mod with `ready` and `poll_fn` helpers. [#301] -[#286]: https://github.com/actix/actix-net/pull/286 +[#301]: https://github.com/actix/actix-net/pull/301 ## 3.0.0-beta.2 - 2021-02-06 diff --git a/actix-utils/Cargo.toml b/actix-utils/Cargo.toml index 9c21dd1b..02bc3114 100644 --- a/actix-utils/Cargo.toml +++ b/actix-utils/Cargo.toml @@ -16,14 +16,7 @@ name = "actix_utils" 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.5" - -futures-core = { version = "0.3.7", default-features = false } -futures-sink = { version = "0.3.7", default-features = false } -log = "0.4" -pin-project-lite = "0.2.0" +local-waker = "0.1" [dev-dependencies] actix-rt = "2.0.0" diff --git a/actix-utils/src/counter.rs b/actix-utils/src/counter.rs index 0b5984d2..c0926b73 100644 --- a/actix-utils/src/counter.rs +++ b/actix-utils/src/counter.rs @@ -1,9 +1,9 @@ -use core::cell::Cell; -use core::task; +//! Task-notifying counter. +use core::{cell::Cell, task}; use std::rc::Rc; -use crate::task::LocalWaker; +use local_waker::LocalWaker; #[derive(Clone)] /// Simple counter with ability to notify task on reaching specific number diff --git a/actix-utils/src/dispatcher.rs b/actix-utils/src/dispatcher.rs deleted file mode 100644 index 94ac9971..00000000 --- a/actix-utils/src/dispatcher.rs +++ /dev/null @@ -1,336 +0,0 @@ -//! Framed dispatcher service and related utilities. - -#![allow(type_alias_bounds)] - -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_core::stream::Stream; -use log::debug; -use pin_project_lite::pin_project; - -use crate::mpsc; - -/// Framed transport errors -pub enum DispatcherError + Decoder, I> { - Service(E), - Encoder(>::Error), - Decoder(::Error), -} - -impl + Decoder, I> From for DispatcherError { - fn from(err: E) -> Self { - DispatcherError::Service(err) - } -} - -impl + Decoder, I> fmt::Debug for DispatcherError -where - E: fmt::Debug, - >::Error: fmt::Debug, - ::Error: fmt::Debug, -{ - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - DispatcherError::Service(ref e) => write!(fmt, "DispatcherError::Service({:?})", e), - DispatcherError::Encoder(ref e) => write!(fmt, "DispatcherError::Encoder({:?})", e), - DispatcherError::Decoder(ref e) => write!(fmt, "DispatcherError::Decoder({:?})", e), - } - } -} - -impl + Decoder, I> fmt::Display for DispatcherError -where - E: fmt::Display, - >::Error: fmt::Debug, - ::Error: fmt::Debug, -{ - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - DispatcherError::Service(ref e) => write!(fmt, "{}", e), - DispatcherError::Encoder(ref e) => write!(fmt, "{:?}", e), - DispatcherError::Decoder(ref e) => write!(fmt, "{:?}", e), - } - } -} - -pub enum Message { - Item(T), - Close, -} - -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 -where - S: Service<::Item>, - U: Encoder + Decoder, -{ - Processing, - Error(DispatcherError), - FramedError(DispatcherError), - FlushAndStop, - Stopping, -} - -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, - _ => panic!(), - } - } - - fn take_framed_error(&mut self) -> DispatcherError { - match mem::replace(self, State::Processing) { - State::FramedError(err) => err, - _ => panic!(), - } - } -} - -impl Dispatcher -where - S: Service<::Item, Response = I>, - S::Error: 'static, - S::Future: 'static, - T: AsyncRead + AsyncWrite, - U: Decoder + Encoder, - I: 'static, - ::Error: fmt::Debug, - >::Error: fmt::Debug, -{ - pub fn new(framed: Framed, service: F) -> Self - where - F: IntoService::Item>, - { - let (tx, rx) = mpsc::channel(); - Dispatcher { - framed, - rx, - tx, - service: service.into_service(), - state: State::Processing, - } - } - - /// Construct new `Dispatcher` instance with customer `mpsc::Receiver` - pub fn with_rx( - framed: Framed, - service: F, - rx: mpsc::Receiver, S::Error>>, - ) -> Self - where - F: IntoService::Item>, - { - let tx = rx.sender(); - Dispatcher { - framed, - rx, - tx, - service: service.into_service(), - state: State::Processing, - } - } - - /// Get sender handle. - pub fn tx(&self) -> mpsc::Sender, S::Error>> { - self.tx.clone() - } - - /// Get reference to a service wrapped by `Dispatcher` instance. - pub fn service(&self) -> &S { - &self.service - } - - /// Get mutable reference to a service wrapped by `Dispatcher` instance. - pub fn service_mut(&mut self) -> &mut S { - &mut self.service - } - - /// Get reference to a framed instance wrapped by `Dispatcher` instance. - pub fn framed(&self) -> &Framed { - &self.framed - } - - /// Get mutable reference to a framed instance wrapped by `Dispatcher` instance. - pub fn framed_mut(&mut self) -> &mut Framed { - &mut self.framed - } - - fn poll_read(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> bool - where - S: Service<::Item, Response = I>, - S::Error: 'static, - S::Future: 'static, - T: AsyncRead + AsyncWrite, - U: Decoder + Encoder, - I: 'static, - >::Error: fmt::Debug, - { - loop { - let this = self.as_mut().project(); - match this.service.poll_ready(cx) { - Poll::Ready(Ok(_)) => { - let item = match this.framed.next_item(cx) { - Poll::Ready(Some(Ok(el))) => el, - Poll::Ready(Some(Err(err))) => { - *this.state = State::FramedError(DispatcherError::Decoder(err)); - return true; - } - Poll::Pending => return false, - Poll::Ready(None) => { - *this.state = State::Stopping; - return true; - } - }; - - let tx = this.tx.clone(); - 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)) => { - *this.state = State::Error(DispatcherError::Service(err)); - return true; - } - } - } - } - - /// write to framed object - fn poll_write(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> bool - where - S: Service<::Item, Response = I>, - S::Error: 'static, - S::Future: 'static, - T: AsyncRead + AsyncWrite, - U: Decoder + Encoder, - I: 'static, - >::Error: fmt::Debug, - { - loop { - let mut this = self.as_mut().project(); - while !this.framed.is_write_buf_full() { - match Pin::new(&mut this.rx).poll_next(cx) { - Poll::Ready(Some(Ok(Message::Item(msg)))) => { - if let Err(err) = this.framed.as_mut().write(msg) { - *this.state = State::FramedError(DispatcherError::Encoder(err)); - return true; - } - } - Poll::Ready(Some(Ok(Message::Close))) => { - *this.state = State::FlushAndStop; - return true; - } - Poll::Ready(Some(Err(err))) => { - *this.state = State::Error(DispatcherError::Service(err)); - return true; - } - Poll::Ready(None) | Poll::Pending => break, - } - } - - if !this.framed.is_write_buf_empty() { - match this.framed.flush(cx) { - Poll::Pending => break, - Poll::Ready(Ok(_)) => {} - Poll::Ready(Err(err)) => { - debug!("Error sending data: {:?}", err); - *this.state = State::FramedError(DispatcherError::Encoder(err)); - return true; - } - } - } else { - break; - } - } - - false - } -} - -impl Future for Dispatcher -where - S: Service<::Item, Response = I>, - S::Error: 'static, - S::Future: 'static, - T: AsyncRead + AsyncWrite, - U: Decoder + Encoder, - I: 'static, - >::Error: fmt::Debug, - ::Error: fmt::Debug, -{ - type Output = Result<(), DispatcherError>; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - loop { - let this = self.as_mut().project(); - - return match this.state { - State::Processing => { - if self.as_mut().poll_read(cx) || self.as_mut().poll_write(cx) { - continue; - } else { - Poll::Pending - } - } - State::Error(_) => { - // flush write buffer - if !this.framed.is_write_buf_empty() && this.framed.flush(cx).is_pending() { - return Poll::Pending; - } - Poll::Ready(Err(this.state.take_error())) - } - State::FlushAndStop => { - if !this.framed.is_write_buf_empty() { - this.framed.flush(cx).map(|res| { - if let Err(err) = res { - debug!("Error sending data: {:?}", err); - } - - Ok(()) - }) - } else { - Poll::Ready(Ok(())) - } - } - State::FramedError(_) => Poll::Ready(Err(this.state.take_framed_error())), - State::Stopping => Poll::Ready(Ok(())), - }; - } - } -} diff --git a/actix-utils/src/future/mod.rs b/actix-utils/src/future/mod.rs new file mode 100644 index 00000000..0ad84ec7 --- /dev/null +++ b/actix-utils/src/future/mod.rs @@ -0,0 +1,7 @@ +//! Asynchronous values. + +mod poll_fn; +mod ready; + +pub use self::poll_fn::{poll_fn, PollFn}; +pub use self::ready::{err, ok, ready, Ready}; diff --git a/actix-utils/src/poll_fn.rs b/actix-utils/src/future/poll_fn.rs similarity index 76% rename from actix-utils/src/poll_fn.rs rename to actix-utils/src/future/poll_fn.rs index 2180f4a4..2e5285d8 100644 --- a/actix-utils/src/poll_fn.rs +++ b/actix-utils/src/future/poll_fn.rs @@ -3,20 +3,20 @@ use core::{ fmt, future::Future, - task::{self, Poll}, + pin::Pin, + task::{Context, Poll}, }; -use std::pin::Pin; /// Create a future driven by the provided function that receives a task context. -pub(crate) fn poll_fn(f: F) -> PollFn +pub fn poll_fn(f: F) -> PollFn where - F: FnMut(&mut task::Context<'_>) -> Poll, + F: FnMut(&mut Context<'_>) -> Poll, { PollFn { f } } /// A Future driven by the inner function. -pub(crate) struct PollFn { +pub struct PollFn { f: F, } @@ -30,11 +30,11 @@ impl fmt::Debug for PollFn { impl Future for PollFn where - F: FnMut(&mut task::Context<'_>) -> task::Poll, + F: FnMut(&mut Context<'_>) -> Poll, { type Output = T; - fn poll(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { (self.f)(cx) } } diff --git a/actix-utils/src/future/ready.rs b/actix-utils/src/future/ready.rs new file mode 100644 index 00000000..be2ee146 --- /dev/null +++ b/actix-utils/src/future/ready.rs @@ -0,0 +1,122 @@ +//! 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. +/// +/// Panic will occur if polled more than once. +/// +/// # Examples +/// ``` +/// use actix_utils::future::ready; +/// +/// // async +/// # async fn run() { +/// let a = ready(1); +/// assert_eq!(a.await, 1); +/// # } +/// +/// // sync +/// let a = ready(1); +/// assert_eq!(a.into_inner(), 1); +/// ``` +#[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 polled after completion"); + Poll::Ready(val) + } +} + +/// Creates a future that is immediately ready with a value. +/// +/// # Examples +/// ```no_run +/// use actix_utils::future::ready; +/// +/// # async fn run() { +/// let a = ready(1); +/// assert_eq!(a.await, 1); +/// # } +/// +/// // sync +/// let a = ready(1); +/// assert_eq!(a.into_inner(), 1); +/// ``` +pub fn ready(val: T) -> Ready { + Ready { val: Some(val) } +} + +/// Create a future that is immediately ready with a success value. +/// +/// # Examples +/// ```no_run +/// use actix_utils::future::ok; +/// +/// # async fn run() { +/// let a = ok::<_, ()>(1); +/// assert_eq!(a.await, Ok(1)); +/// # } +/// ``` +pub fn ok(val: T) -> Ready> { + Ready { val: Some(Ok(val)) } +} + +/// Create a future that is immediately ready with an error value. +/// +/// # Examples +/// ```no_run +/// use actix_utils::future::err; +/// +/// # async fn run() { +/// let a = err::<(), _>(1); +/// assert_eq!(a.await, Err(1)); +/// # } +/// ``` +pub fn err(err: E) -> Ready> { + Ready { + val: Some(Err(err)), + } +} + +#[cfg(test)] +mod tests { + use futures_util::task::noop_waker; + + use super::*; + + #[test] + #[should_panic] + fn multiple_poll_panics() { + let waker = noop_waker(); + let mut cx = Context::from_waker(&waker); + + let mut ready = ready(1); + assert_eq!(Pin::new(&mut ready).poll(&mut cx), Poll::Ready(1)); + + // panic! + let _ = Pin::new(&mut ready).poll(&mut cx); + } +} diff --git a/actix-utils/src/lib.rs b/actix-utils/src/lib.rs index 6658cba8..d0e057ff 100644 --- a/actix-utils/src/lib.rs +++ b/actix-utils/src/lib.rs @@ -6,10 +6,4 @@ #![doc(html_favicon_url = "https://actix.rs/favicon.ico")] pub mod counter; -pub mod dispatcher; -pub mod mpsc; -mod poll_fn; -pub mod task; -pub mod timeout; - -use self::poll_fn::poll_fn; +pub mod future; diff --git a/actix-utils/src/timeout.rs b/actix-utils/src/timeout.rs deleted file mode 100644 index f13c7ffa..00000000 --- a/actix-utils/src/timeout.rs +++ /dev/null @@ -1,255 +0,0 @@ -//! Service that applies a timeout to requests. -//! -//! 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; -use core::task::{Context, Poll}; -use core::{fmt, time}; - -use actix_rt::time::{sleep, Sleep}; -use actix_service::{IntoService, Service, Transform}; -use pin_project_lite::pin_project; - -/// Applies a timeout to requests. -#[derive(Debug)] -pub struct Timeout { - timeout: time::Duration, - _t: PhantomData, -} - -/// Timeout error -pub enum TimeoutError { - /// Service error - Service(E), - /// Service call timeout - Timeout, -} - -impl From for TimeoutError { - fn from(err: E) -> Self { - TimeoutError::Service(err) - } -} - -impl fmt::Debug for TimeoutError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - TimeoutError::Service(e) => write!(f, "TimeoutError::Service({:?})", e), - TimeoutError::Timeout => write!(f, "TimeoutError::Timeout"), - } - } -} - -impl fmt::Display for TimeoutError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - TimeoutError::Service(e) => e.fmt(f), - TimeoutError::Timeout => write!(f, "Service call timeout"), - } - } -} - -impl PartialEq for TimeoutError { - fn eq(&self, other: &TimeoutError) -> bool { - match self { - TimeoutError::Service(e1) => match other { - TimeoutError::Service(e2) => e1 == e2, - TimeoutError::Timeout => false, - }, - TimeoutError::Timeout => matches!(other, TimeoutError::Timeout), - } - } -} - -impl Timeout { - pub fn new(timeout: time::Duration) -> Self { - Timeout { - timeout, - _t: PhantomData, - } - } -} - -impl Clone for Timeout { - fn clone(&self) -> Self { - Timeout::new(self.timeout) - } -} - -impl Transform for Timeout -where - S: Service, -{ - type Response = S::Response; - type Error = TimeoutError; - type Transform = TimeoutService; - type InitError = E; - type Future = TimeoutFuture; - - fn new_transform(&self, service: S) -> Self::Future { - let service = TimeoutService { - service, - timeout: self.timeout, - _phantom: PhantomData, - }; - - 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())) - } -} - -/// Applies a timeout to requests. -#[derive(Debug, Clone)] -pub struct TimeoutService { - service: S, - timeout: time::Duration, - _phantom: PhantomData, -} - -impl TimeoutService -where - S: Service, -{ - pub fn new(timeout: time::Duration, service: U) -> Self - where - U: IntoService, - { - TimeoutService { - timeout, - service: service.into_service(), - _phantom: PhantomData, - } - } -} - -impl Service for TimeoutService -where - S: Service, -{ - type Response = S::Response; - type Error = TimeoutError; - type Future = TimeoutServiceResponse; - - actix_service::forward_ready!(service); - - fn call(&self, request: Req) -> Self::Future { - TimeoutServiceResponse { - fut: self.service.call(request), - sleep: sleep(self.timeout), - } - } -} - -pin_project! { - /// `TimeoutService` response future - #[derive(Debug)] - pub struct TimeoutServiceResponse - where - S: Service - { - #[pin] - fut: S::Future, - #[pin] - sleep: Sleep, - } -} - -impl Future for TimeoutServiceResponse -where - S: Service, -{ - type Output = Result>; - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let this = self.project(); - - // First, try polling the future - 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 - this.sleep.poll(cx).map(|_| Err(TimeoutError::Timeout)) - } -} - -#[cfg(test)] -mod tests { - use core::time::Duration; - - use super::*; - use actix_service::{apply, fn_factory, Service, ServiceFactory}; - use futures_core::future::LocalBoxFuture; - - struct SleepService(Duration); - - impl Service<()> for SleepService { - type Response = (); - type Error = (); - type Future = LocalBoxFuture<'static, Result<(), ()>>; - - actix_service::always_ready!(); - - fn call(&self, _: ()) -> Self::Future { - let sleep = actix_rt::time::sleep(self.0); - Box::pin(async move { - sleep.await; - Ok(()) - }) - } - } - - #[actix_rt::test] - async fn test_success() { - let resolution = Duration::from_millis(100); - let wait_time = Duration::from_millis(50); - - let timeout = TimeoutService::new(resolution, SleepService(wait_time)); - assert_eq!(timeout.call(()).await, Ok(())); - } - - #[actix_rt::test] - async fn test_timeout() { - let resolution = Duration::from_millis(100); - let wait_time = Duration::from_millis(500); - - let timeout = TimeoutService::new(resolution, SleepService(wait_time)); - assert_eq!(timeout.call(()).await, Err(TimeoutError::Timeout)); - } - - #[actix_rt::test] - async fn test_timeout_new_service() { - let resolution = Duration::from_millis(100); - let wait_time = Duration::from_millis(500); - - let timeout = apply( - Timeout::new(resolution), - fn_factory(|| async { Ok::<_, ()>(SleepService(wait_time)) }), - ); - let srv = timeout.new_service(&()).await.unwrap(); - - assert_eq!(srv.call(()).await, Err(TimeoutError::Timeout)); - } -} diff --git a/local-channel/CHANGES.md b/local-channel/CHANGES.md new file mode 100644 index 00000000..cccf9609 --- /dev/null +++ b/local-channel/CHANGES.md @@ -0,0 +1,7 @@ +# Changes + +## Unreleased - 2021-xx-xx + + +## 0.1.1 - 2021-03-29 +* Move local mpsc channel to it's own crate. diff --git a/local-channel/Cargo.toml b/local-channel/Cargo.toml new file mode 100644 index 00000000..a9d3691e --- /dev/null +++ b/local-channel/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "local-channel" +version = "0.1.1" +description = "A non-threadsafe multi-producer, single-consumer, futures-aware, FIFO queue" +authors = [ + "Nikolay Kim ", + "Rob Ede ", +] +edition = "2018" +license = "MIT OR Apache-2.0" +repository = "https://github.com/actix/actix-net.git" +documentation = "https://docs.rs/actix-server" +keywords = ["channel", "local", "futures"] + +[dependencies] +futures-core = { version = "0.3.7", default-features = false } +futures-sink = { version = "0.3.7", default-features = false } +futures-util = { version = "0.3.7", default-features = false } +local-waker = "0.1" + +[dev-dependencies] +tokio = { version = "1", features = ["rt", "macros"] } diff --git a/local-channel/src/lib.rs b/local-channel/src/lib.rs new file mode 100644 index 00000000..b88fd98a --- /dev/null +++ b/local-channel/src/lib.rs @@ -0,0 +1,3 @@ +//! Non-thread-safe channels. + +pub mod mpsc; diff --git a/actix-utils/src/mpsc.rs b/local-channel/src/mpsc.rs similarity index 95% rename from actix-utils/src/mpsc.rs rename to local-channel/src/mpsc.rs index 9c7a5a0e..627d7db0 100644 --- a/actix-utils/src/mpsc.rs +++ b/local-channel/src/mpsc.rs @@ -1,4 +1,4 @@ -//! A multi-producer, single-consumer, futures-aware, FIFO queue. +//! A non-thread-safe multi-producer, single-consumer, futures-aware, FIFO queue. use core::{ cell::RefCell, @@ -11,8 +11,8 @@ use std::{collections::VecDeque, error::Error, rc::Rc}; use futures_core::stream::Stream; use futures_sink::Sink; - -use crate::{poll_fn, task::LocalWaker}; +use futures_util::future::poll_fn; +use local_waker::LocalWaker; /// Creates a unbounded in-memory channel with buffered storage. /// @@ -174,6 +174,8 @@ impl Drop for Receiver { } /// Error returned when attempting to send after the channels' [Receiver] is dropped or closed. +/// +/// Allows access to message that failed to send with [`into_inner`](Self::into_inner). pub struct SendError(pub T); impl SendError { @@ -199,11 +201,11 @@ impl Error for SendError {} #[cfg(test)] mod tests { - use super::*; - use futures_util::future::lazy; - use futures_util::{stream::Stream, StreamExt}; + use futures_util::{future::lazy, StreamExt as _}; - #[actix_rt::test] + use super::*; + + #[tokio::test] async fn test_mpsc() { let (tx, mut rx) = channel(); tx.send("test").unwrap(); @@ -237,7 +239,7 @@ mod tests { assert!(tx2.send("test").is_err()); } - #[actix_rt::test] + #[tokio::test] async fn test_recv() { let (tx, mut rx) = channel(); tx.send("test").unwrap(); diff --git a/local-waker/CHANGES.md b/local-waker/CHANGES.md new file mode 100644 index 00000000..edb5aa3e --- /dev/null +++ b/local-waker/CHANGES.md @@ -0,0 +1,7 @@ +# Changes + +## Unreleased - 2021-xx-xx + + +## 0.1.1 - 2021-03-29 +* Move `LocalWaker` to it's own crate. diff --git a/local-waker/Cargo.toml b/local-waker/Cargo.toml new file mode 100644 index 00000000..df1f9ab8 --- /dev/null +++ b/local-waker/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "local-waker" +version = "0.1.1" +description = "A synchronization primitive for thread-local task wakeup" +authors = [ + "Nikolay Kim ", + "Rob Ede ", +] +keywords = ["waker", "local", "futures", "no-std"] +repository = "https://github.com/actix/actix-net.git" +documentation = "https://docs.rs/local-waker" +categories = ["asynchronous", "no-std"] +license = "MIT OR Apache-2.0" +edition = "2018" + +[dependencies] diff --git a/actix-utils/src/task.rs b/local-waker/src/lib.rs similarity index 95% rename from actix-utils/src/task.rs rename to local-waker/src/lib.rs index 507bfc14..c76badee 100644 --- a/actix-utils/src/task.rs +++ b/local-waker/src/lib.rs @@ -1,3 +1,9 @@ +//! A synchronization primitive for thread-local task wakeup. +//! +//! See docs for [`LocalWaker`]. + +#![no_std] + use core::{cell::Cell, fmt, marker::PhantomData, task::Waker}; /// A synchronization primitive for task wakeup. From f21eaa954fe756f22ae0b9483281f27a5aacf179 Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Wed, 31 Mar 2021 22:55:33 -0700 Subject: [PATCH 31/72] Reduce size of Conn by removing unused addr field (#304) --- actix-server/src/accept.rs | 3 +-- actix-server/src/socket.rs | 10 +++------- actix-server/src/worker.rs | 3 +-- 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/actix-server/src/accept.rs b/actix-server/src/accept.rs index f5484434..2b9c7206 100644 --- a/actix-server/src/accept.rs +++ b/actix-server/src/accept.rs @@ -391,11 +391,10 @@ impl Accept { .expect("ServerSocketInfo is removed from Slab"); match info.lst.accept() { - Ok((io, addr)) => { + Ok(io) => { let msg = Conn { io, token: info.token, - peer: Some(addr), }; self.accept_one(sockets, msg); } diff --git a/actix-server/src/socket.rs b/actix-server/src/socket.rs index baf02cbe..0625cfda 100644 --- a/actix-server/src/socket.rs +++ b/actix-server/src/socket.rs @@ -40,15 +40,11 @@ impl MioListener { } } - pub(crate) fn accept(&self) -> io::Result<(MioStream, SocketAddr)> { + pub(crate) fn accept(&self) -> io::Result { match *self { - MioListener::Tcp(ref lst) => lst - .accept() - .map(|(stream, addr)| (MioStream::Tcp(stream), SocketAddr::Tcp(addr))), + MioListener::Tcp(ref lst) => lst.accept().map(|(stream, _)| MioStream::Tcp(stream)), #[cfg(unix)] - MioListener::Uds(ref lst) => lst - .accept() - .map(|(stream, addr)| (MioStream::Uds(stream), SocketAddr::Uds(addr))), + MioListener::Uds(ref lst) => lst.accept().map(|(stream, _)| MioStream::Uds(stream)), } } } diff --git a/actix-server/src/worker.rs b/actix-server/src/worker.rs index aa6d31fc..63c45757 100644 --- a/actix-server/src/worker.rs +++ b/actix-server/src/worker.rs @@ -14,7 +14,7 @@ use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}; use tokio::sync::oneshot; use crate::service::{BoxedServerService, InternalServiceFactory}; -use crate::socket::{MioStream, SocketAddr}; +use crate::socket::MioStream; use crate::waker_queue::{WakerInterest, WakerQueue}; use crate::{join_all, Token}; @@ -31,7 +31,6 @@ pub(crate) struct StopCommand { pub(crate) struct Conn { pub io: MioStream, pub token: Token, - pub peer: Option, } static MAX_CONNS: AtomicUsize = AtomicUsize::new(25600); From ee3a548a8568e939021cc8c73e77638678d8c4ef Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Wed, 31 Mar 2021 23:45:49 -0700 Subject: [PATCH 32/72] Refactor Accept::accept_one (#303) --- actix-server/src/accept.rs | 94 +++++++++++++++++++------------------- 1 file changed, 48 insertions(+), 46 deletions(-) diff --git a/actix-server/src/accept.rs b/actix-server/src/accept.rs index 2b9c7206..34bb029d 100644 --- a/actix-server/src/accept.rs +++ b/actix-server/src/accept.rs @@ -318,72 +318,74 @@ impl Accept { } } - fn accept_one(&mut self, sockets: &mut Slab, mut msg: Conn) { + fn accept_one(&mut self, sockets: &mut Slab, mut conn: Conn) { if self.backpressure { + // send_connection would remove fault worker from handles. + // worst case here is conn get dropped after all handles are gone. while !self.handles.is_empty() { - match self.handles[self.next].send(msg) { - Ok(_) => { - self.set_next(); - break; - } - Err(tmp) => { - // 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.handles.swap_remove(self.next); - if self.handles.is_empty() { - error!("No workers"); - return; - } else if self.handles.len() <= self.next { - self.next = 0; - } - continue; - } + match self.send_connection(sockets, conn) { + Ok(_) => return, + Err(c) => conn = c, } } } else { + // Do one round and try to send conn to all workers until it succeed. + // Start from self.next. let mut idx = 0; while idx < self.handles.len() { idx += 1; if self.handles[self.next].available() { - match self.handles[self.next].send(msg) { - Ok(_) => { - 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.handles[self.next].idx); - msg = tmp; - self.handles.swap_remove(self.next); - if self.handles.is_empty() { - error!("No workers"); - self.maybe_backpressure(sockets, true); - return; - } else if self.handles.len() <= self.next { - self.next = 0; - } - continue; - } + match self.send_connection(sockets, conn) { + Ok(_) => return, + Err(c) => conn = c, } + } else { + self.set_next(); } - self.set_next(); } - // enable backpressure + // Sending Conn failed due to either all workers are in error or not available. + // Enter backpressure state and try again. self.maybe_backpressure(sockets, true); - self.accept_one(sockets, msg); + self.accept_one(sockets, conn); } } - // set next worker handle that would accept work. + // Set next worker handle that would accept work. fn set_next(&mut self) { self.next = (self.next + 1) % self.handles.len(); } + // Send connection to worker and handle error. + fn send_connection( + &mut self, + sockets: &mut Slab, + conn: Conn, + ) -> Result<(), Conn> { + match self.handles[self.next].send(conn) { + Ok(_) => { + self.set_next(); + Ok(()) + } + Err(conn) => { + // 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. + self.srv.worker_faulted(self.handles[self.next].idx); + self.handles.swap_remove(self.next); + if self.handles.is_empty() { + error!("No workers"); + self.maybe_backpressure(sockets, true); + // All workers are gone and Conn is nowhere to be sent. + // Treat this situation as Ok and drop Conn. + return Ok(()); + } else if self.handles.len() <= self.next { + self.next = 0; + } + Err(conn) + } + } + } + fn accept(&mut self, sockets: &mut Slab, token: usize) { loop { let info = sockets From 2c5c9167a58ee81bcdfb4e7ad91c555c2bffce37 Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Thu, 1 Apr 2021 00:25:24 -0700 Subject: [PATCH 33/72] =?UTF-8?q?Fix=20bug=20where=20timed=20out=20socket?= =?UTF-8?q?=20would=20register=20itself=20when=20server=20in=20b=E2=80=A6?= =?UTF-8?q?=20(#302)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Rob Ede --- actix-server/src/accept.rs | 56 +++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 25 deletions(-) diff --git a/actix-server/src/accept.rs b/actix-server/src/accept.rs index 34bb029d..2750d1c5 100644 --- a/actix-server/src/accept.rs +++ b/actix-server/src/accept.rs @@ -187,21 +187,19 @@ impl Accept { let mut guard = self.waker.guard(); match guard.pop_front() { // worker notify it becomes available. we may want to recover - // from backpressure. + // 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 + // 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. + // got timer interest and it's time to try register socket(s) again Some(WakerInterest::Timer) => { drop(guard); self.process_timer(&mut sockets) @@ -238,16 +236,23 @@ impl Accept { fn process_timer(&self, sockets: &mut Slab) { let now = Instant::now(); - 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 { - self.register_logged(token, info); - } else { + sockets + .iter_mut() + // Only sockets that had an associated timeout were deregistered. + .filter(|(_, info)| info.timeout.is_some()) + .for_each(|(token, info)| { + let inst = info.timeout.take().unwrap(); + + if now < inst { info.timeout = Some(inst); + } else if !self.backpressure { + self.register_logged(token, info); } - } - }); + + // Drop the timeout if server is in backpressure and socket timeout is expired. + // When server recovers from backpressure it will register all sockets without + // a timeout value so this socket register will be delayed till then. + }); } #[cfg(not(target_os = "windows"))] @@ -301,20 +306,21 @@ impl Accept { } fn maybe_backpressure(&mut self, sockets: &mut Slab, on: bool) { - if self.backpressure { - if !on { + // Only operate when server is in a different backpressure than the given flag. + if self.backpressure != on { + if on { + self.backpressure = true; + // TODO: figure out if timing out sockets can be safely de-registered twice. + self.deregister_all(sockets); + } else { self.backpressure = false; - for (token, info) in sockets.iter_mut() { - if info.timeout.is_some() { - // socket will attempt to re-register itself when its timeout completes - continue; - } - self.register_logged(token, info); - } + sockets + .iter_mut() + // Only operate on sockets without associated timeout. + // Sockets with it will attempt to re-register when their timeout expires. + .filter(|(_, info)| info.timeout.is_none()) + .for_each(|(token, info)| self.register_logged(token, info)); } - } else if on { - self.backpressure = true; - self.deregister_all(sockets); } } From b09e7cd417b3b453fd2fb31ebe26cfe9c2191e75 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Thu, 1 Apr 2021 09:01:56 +0100 Subject: [PATCH 34/72] fix local waker metadata --- .cargo/config.toml | 2 +- local-channel/Cargo.toml | 7 +++---- local-waker/CHANGES.md | 4 ++++ local-waker/Cargo.toml | 1 - 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index 77788410..40fe3e57 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,3 +1,3 @@ [alias] +chk = "hack check --workspace --all-features --tests --examples" lint = "hack --clean-per-run clippy --workspace --tests --examples" -chk = "hack check --workspace --tests --examples" diff --git a/local-channel/Cargo.toml b/local-channel/Cargo.toml index a9d3691e..0ffd3597 100644 --- a/local-channel/Cargo.toml +++ b/local-channel/Cargo.toml @@ -1,16 +1,15 @@ [package] name = "local-channel" -version = "0.1.1" +version = "0.1.2" description = "A non-threadsafe multi-producer, single-consumer, futures-aware, FIFO queue" authors = [ "Nikolay Kim ", "Rob Ede ", ] -edition = "2018" -license = "MIT OR Apache-2.0" repository = "https://github.com/actix/actix-net.git" -documentation = "https://docs.rs/actix-server" keywords = ["channel", "local", "futures"] +license = "MIT OR Apache-2.0" +edition = "2018" [dependencies] futures-core = { version = "0.3.7", default-features = false } diff --git a/local-waker/CHANGES.md b/local-waker/CHANGES.md index edb5aa3e..5caf69ca 100644 --- a/local-waker/CHANGES.md +++ b/local-waker/CHANGES.md @@ -3,5 +3,9 @@ ## Unreleased - 2021-xx-xx +## 0.1.2 - 2021-04-01 +* Fix crate metadata. + + ## 0.1.1 - 2021-03-29 * Move `LocalWaker` to it's own crate. diff --git a/local-waker/Cargo.toml b/local-waker/Cargo.toml index df1f9ab8..af512966 100644 --- a/local-waker/Cargo.toml +++ b/local-waker/Cargo.toml @@ -8,7 +8,6 @@ authors = [ ] keywords = ["waker", "local", "futures", "no-std"] repository = "https://github.com/actix/actix-net.git" -documentation = "https://docs.rs/local-waker" categories = ["asynchronous", "no-std"] license = "MIT OR Apache-2.0" edition = "2018" From 4eebdf4070d44709768263db3d10e0f6b3013992 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Thu, 1 Apr 2021 09:31:42 +0100 Subject: [PATCH 35/72] prepare actix-utils release 3.0.0-beta.3 --- actix-utils/CHANGES.md | 3 ++ actix-utils/Cargo.toml | 11 ++++--- actix-utils/src/counter.rs | 67 ++++++++++++++++++++++---------------- actix-utils/src/lib.rs | 4 +-- 4 files changed, 50 insertions(+), 35 deletions(-) diff --git a/actix-utils/CHANGES.md b/actix-utils/CHANGES.md index c911a211..57ab7add 100644 --- a/actix-utils/CHANGES.md +++ b/actix-utils/CHANGES.md @@ -1,6 +1,9 @@ # Changes ## Unreleased - 2021-xx-xx + + +## 3.0.0-beta.3 - 2021-04-01 * Moved `mpsc` to own crate `local-channel`. [#301] * Moved `task::LocalWaker` to own crate `local-waker`. [#301] * Remove `timeout` module. [#301] diff --git a/actix-utils/Cargo.toml b/actix-utils/Cargo.toml index 02bc3114..019d6d98 100644 --- a/actix-utils/Cargo.toml +++ b/actix-utils/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "actix-utils" -version = "3.0.0-beta.2" -authors = ["Nikolay Kim "] -description = "Various network related services and utilities for the Actix ecosystem" +version = "3.0.0-beta.3" +authors = [ + "Nikolay Kim ", + "Rob Ede ", +] +description = "Utilities 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-utils" categories = ["network-programming", "asynchronous"] license = "MIT OR Apache-2.0" edition = "2018" diff --git a/actix-utils/src/counter.rs b/actix-utils/src/counter.rs index c0926b73..7a87fa3d 100644 --- a/actix-utils/src/counter.rs +++ b/actix-utils/src/counter.rs @@ -1,24 +1,18 @@ //! Task-notifying counter. -use core::{cell::Cell, task}; +use core::{cell::Cell, fmt, task}; use std::rc::Rc; use local_waker::LocalWaker; -#[derive(Clone)] /// Simple counter with ability to notify task on reaching specific number /// /// Counter could be cloned, total n-count is shared across all clones. +#[derive(Debug, Clone)] pub struct Counter(Rc); -struct CounterInner { - count: Cell, - capacity: usize, - task: LocalWaker, -} - impl Counter { - /// Create `Counter` instance and set max value. + /// Create `Counter` instance with max value. pub fn new(capacity: usize) -> Self { Counter(Rc::new(CounterInner { capacity, @@ -27,38 +21,26 @@ impl Counter { })) } - /// Get counter guard. + /// Create new counter guard, incrementing the counter. pub fn get(&self) -> CounterGuard { CounterGuard::new(self.0.clone()) } - /// Check if counter is not at capacity. If counter at capacity - /// it registers notification for current task. + /// Notify current task and return true if counter is at capacity. pub fn available(&self, cx: &mut task::Context<'_>) -> bool { self.0.available(cx) } - /// Get total number of acquired counts + /// Get total number of acquired guards. pub fn total(&self) -> usize { self.0.count.get() } } -pub struct CounterGuard(Rc); - -impl CounterGuard { - fn new(inner: Rc) -> Self { - inner.inc(); - CounterGuard(inner) - } -} - -impl Unpin for CounterGuard {} - -impl Drop for CounterGuard { - fn drop(&mut self) { - self.0.dec(); - } +struct CounterInner { + count: Cell, + capacity: usize, + task: LocalWaker, } impl CounterInner { @@ -83,3 +65,32 @@ impl CounterInner { } } } + +impl fmt::Debug for CounterInner { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Counter") + .field("count", &self.count.get()) + .field("capacity", &self.capacity) + .field("task", &self.task) + .finish() + } +} + +/// An RAII structure that keeps the underlying counter incremented until this guard is dropped. +#[derive(Debug)] +pub struct CounterGuard(Rc); + +impl CounterGuard { + fn new(inner: Rc) -> Self { + inner.inc(); + CounterGuard(inner) + } +} + +impl Unpin for CounterGuard {} + +impl Drop for CounterGuard { + fn drop(&mut self) { + self.0.dec(); + } +} diff --git a/actix-utils/src/lib.rs b/actix-utils/src/lib.rs index d0e057ff..f94147ec 100644 --- a/actix-utils/src/lib.rs +++ b/actix-utils/src/lib.rs @@ -1,7 +1,7 @@ -//! Various network related services and utilities for the Actix ecosystem. +//! Various utilities for the Actix ecosystem. #![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")] From b068ea16f89afecf6becf56178098742342e67ab Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Thu, 1 Apr 2021 09:36:07 +0100 Subject: [PATCH 36/72] prepare server release 2.0.0-beta.4 --- actix-server/CHANGES.md | 5 ++++- actix-server/Cargo.toml | 3 +-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/actix-server/CHANGES.md b/actix-server/CHANGES.md index aaa38911..60d7ce37 100644 --- a/actix-server/CHANGES.md +++ b/actix-server/CHANGES.md @@ -1,7 +1,10 @@ # Changes ## Unreleased - 2021-xx-xx -* Prevent panic when shutdown_timeout is very large. [f9262db] + + +## 2.0.0-beta.4 - 2021-04-01 +* Prevent panic when `shutdown_timeout` is very large. [f9262db] [f9262db]: https://github.com/actix/actix-net/commit/f9262db diff --git a/actix-server/Cargo.toml b/actix-server/Cargo.toml index 620cbf51..e557dbd2 100755 --- a/actix-server/Cargo.toml +++ b/actix-server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-server" -version = "2.0.0-beta.3" +version = "2.0.0-beta.4" authors = [ "Nikolay Kim ", "fakeshadow <24548779@qq.com>", @@ -9,7 +9,6 @@ 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" categories = ["network-programming", "asynchronous"] license = "MIT OR Apache-2.0" edition = "2018" From fb27ffc525ec32af82cf71749a5486f643ac705f Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Thu, 1 Apr 2021 13:53:44 +0100 Subject: [PATCH 37/72] add future::Either type to utils (#305) --- actix-tracing/Cargo.toml | 2 +- actix-tracing/src/lib.rs | 6 +- actix-utils/CHANGES.md | 3 + actix-utils/Cargo.toml | 1 + actix-utils/src/future/either.rs | 91 +++++++++++++++++++++++++++++++ actix-utils/src/future/mod.rs | 2 + actix-utils/src/future/poll_fn.rs | 2 +- actix-utils/src/future/ready.rs | 4 +- 8 files changed, 104 insertions(+), 7 deletions(-) create mode 100644 actix-utils/src/future/either.rs diff --git a/actix-tracing/Cargo.toml b/actix-tracing/Cargo.toml index 992edbf4..4bbe7396 100644 --- a/actix-tracing/Cargo.toml +++ b/actix-tracing/Cargo.toml @@ -17,8 +17,8 @@ path = "src/lib.rs" [dependencies] actix-service = "2.0.0-beta.5" +actix-utils = "3.0.0-beta.3" -futures-util = { version = "0.3.7", default-features = false } tracing = "0.1" tracing-futures = "0.2" diff --git a/actix-tracing/src/lib.rs b/actix-tracing/src/lib.rs index 89e93be1..fcc1488d 100644 --- a/actix-tracing/src/lib.rs +++ b/actix-tracing/src/lib.rs @@ -9,7 +9,7 @@ use core::marker::PhantomData; use actix_service::{ apply, ApplyTransform, IntoServiceFactory, Service, ServiceFactory, Transform, }; -use futures_util::future::{ok, Either, Ready}; +use actix_utils::future::{ok, Either, Ready}; use tracing_futures::{Instrument, Instrumented}; /// A `Service` implementation that automatically enters/exits tracing spans @@ -48,9 +48,9 @@ where .clone() .map(|span| tracing::span!(parent: &span, tracing::Level::INFO, "future")) { - Either::Right(fut.instrument(span)) + Either::right(fut.instrument(span)) } else { - Either::Left(fut) + Either::left(fut) } } } diff --git a/actix-utils/CHANGES.md b/actix-utils/CHANGES.md index 57ab7add..5aa36128 100644 --- a/actix-utils/CHANGES.md +++ b/actix-utils/CHANGES.md @@ -1,6 +1,9 @@ # Changes ## Unreleased - 2021-xx-xx +* Add `future::Either` type. [#305] + +[#305]: https://github.com/actix/actix-net/pull/305 ## 3.0.0-beta.3 - 2021-04-01 diff --git a/actix-utils/Cargo.toml b/actix-utils/Cargo.toml index 019d6d98..c3961a6a 100644 --- a/actix-utils/Cargo.toml +++ b/actix-utils/Cargo.toml @@ -17,6 +17,7 @@ name = "actix_utils" path = "src/lib.rs" [dependencies] +pin-project-lite = "0.2" local-waker = "0.1" [dev-dependencies] diff --git a/actix-utils/src/future/either.rs b/actix-utils/src/future/either.rs new file mode 100644 index 00000000..77b2118d --- /dev/null +++ b/actix-utils/src/future/either.rs @@ -0,0 +1,91 @@ +//! A symmetric either future. + +use core::{ + future::Future, + pin::Pin, + task::{Context, Poll}, +}; + +use pin_project_lite::pin_project; + +pin_project! { + /// Combines two different futures that have the same output type. + /// + /// Construct variants with [`Either::left`] and [`Either::right`]. + /// + /// # Examples + /// ``` + /// use actix_utils::future::{ready, Ready, Either}; + /// + /// # async fn run() { + /// let res = Either::<_, Ready>::left(ready(42)); + /// assert_eq!(res.await, 42); + /// + /// let res = Either::, _>::right(ready(43)); + /// assert_eq!(res.await, 43); + /// # } + /// ``` + #[project = EitherProj] + #[derive(Debug, Clone)] + pub enum Either { + /// A value of type `L`. + #[allow(missing_docs)] + Left { #[pin] value: L }, + + /// A value of type `R`. + #[allow(missing_docs)] + Right { #[pin] value: R }, + } +} + +impl Either { + /// Creates new `Either` using left variant. + pub fn left(value: L) -> Either { + Either::Left { value } + } + + /// Creates new `Either` using right variant. + pub fn right(value: R) -> Either { + Either::Right { value } + } +} + +impl Either { + /// Unwraps into inner value when left and right have a common type. + pub fn into_inner(self) -> T { + match self { + Either::Left { value } => value, + Either::Right { value } => value, + } + } +} + +impl Future for Either +where + L: Future, + R: Future, +{ + type Output = L::Output; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + match self.project() { + EitherProj::Left { value } => value.poll(cx), + EitherProj::Right { value } => value.poll(cx), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::future::{ready, Ready}; + + #[actix_rt::test] + async fn test_either() { + let res = Either::<_, Ready>::left(ready(42)); + assert_eq!(res.await, 42); + + let res = Either::, _>::right(ready(43)); + assert_eq!(res.await, 43); + } +} diff --git a/actix-utils/src/future/mod.rs b/actix-utils/src/future/mod.rs index 0ad84ec7..be3807bf 100644 --- a/actix-utils/src/future/mod.rs +++ b/actix-utils/src/future/mod.rs @@ -1,7 +1,9 @@ //! Asynchronous values. +mod either; mod poll_fn; mod ready; +pub use self::either::Either; pub use self::poll_fn::{poll_fn, PollFn}; pub use self::ready::{err, ok, ready, Ready}; diff --git a/actix-utils/src/future/poll_fn.rs b/actix-utils/src/future/poll_fn.rs index 2e5285d8..5e911bf5 100644 --- a/actix-utils/src/future/poll_fn.rs +++ b/actix-utils/src/future/poll_fn.rs @@ -7,7 +7,7 @@ use core::{ task::{Context, Poll}, }; -/// Create a future driven by the provided function that receives a task context. +/// Creates a future driven by the provided function that receives a task context. pub fn poll_fn(f: F) -> PollFn where F: FnMut(&mut Context<'_>) -> Poll, diff --git a/actix-utils/src/future/ready.rs b/actix-utils/src/future/ready.rs index be2ee146..4a01ada3 100644 --- a/actix-utils/src/future/ready.rs +++ b/actix-utils/src/future/ready.rs @@ -69,7 +69,7 @@ pub fn ready(val: T) -> Ready { Ready { val: Some(val) } } -/// Create a future that is immediately ready with a success value. +/// Creates a future that is immediately ready with a success value. /// /// # Examples /// ```no_run @@ -84,7 +84,7 @@ pub fn ok(val: T) -> Ready> { Ready { val: Some(Ok(val)) } } -/// Create a future that is immediately ready with an error value. +/// Creates a future that is immediately ready with an error value. /// /// # Examples /// ```no_run From 6d66cfb06a6541674e5dc19897bf0e2e7b6bc3cf Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Thu, 1 Apr 2021 13:57:08 +0100 Subject: [PATCH 38/72] prepare utils release 3.0.0-beta.4 --- actix-server/Cargo.toml | 2 +- actix-tls/Cargo.toml | 2 +- actix-tracing/Cargo.toml | 2 +- actix-utils/CHANGES.md | 3 +++ actix-utils/Cargo.toml | 2 +- 5 files changed, 7 insertions(+), 4 deletions(-) diff --git a/actix-server/Cargo.toml b/actix-server/Cargo.toml index e557dbd2..80b44c6d 100755 --- a/actix-server/Cargo.toml +++ b/actix-server/Cargo.toml @@ -23,7 +23,7 @@ default = [] [dependencies] actix-rt = { version = "2.0.0", default-features = false } actix-service = "2.0.0-beta.5" -actix-utils = "3.0.0-beta.2" +actix-utils = "3.0.0-beta.4" futures-core = { version = "0.3.7", default-features = false, features = ["alloc"] } log = "0.4" diff --git a/actix-tls/Cargo.toml b/actix-tls/Cargo.toml index 3f767a28..7fbd94b0 100755 --- a/actix-tls/Cargo.toml +++ b/actix-tls/Cargo.toml @@ -43,7 +43,7 @@ uri = ["http"] actix-codec = "0.4.0-beta.1" actix-rt = { version = "2.2.0", default-features = false } actix-service = "2.0.0-beta.5" -actix-utils = "3.0.0-beta.2" +actix-utils = "3.0.0-beta.4" derive_more = "0.99.5" futures-core = { version = "0.3.7", default-features = false, features = ["alloc"] } diff --git a/actix-tracing/Cargo.toml b/actix-tracing/Cargo.toml index 4bbe7396..ec2e4a7c 100644 --- a/actix-tracing/Cargo.toml +++ b/actix-tracing/Cargo.toml @@ -17,7 +17,7 @@ path = "src/lib.rs" [dependencies] actix-service = "2.0.0-beta.5" -actix-utils = "3.0.0-beta.3" +actix-utils = "3.0.0-beta.4" tracing = "0.1" tracing-futures = "0.2" diff --git a/actix-utils/CHANGES.md b/actix-utils/CHANGES.md index 5aa36128..d14446de 100644 --- a/actix-utils/CHANGES.md +++ b/actix-utils/CHANGES.md @@ -1,6 +1,9 @@ # Changes ## Unreleased - 2021-xx-xx + + +## 3.0.0-beta.4 - 2021-04-01 * Add `future::Either` type. [#305] [#305]: https://github.com/actix/actix-net/pull/305 diff --git a/actix-utils/Cargo.toml b/actix-utils/Cargo.toml index c3961a6a..8b593697 100644 --- a/actix-utils/Cargo.toml +++ b/actix-utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-utils" -version = "3.0.0-beta.3" +version = "3.0.0-beta.4" authors = [ "Nikolay Kim ", "Rob Ede ", From fdac52aa1134b805cb8932f6a3462020f7e945d5 Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Fri, 2 Apr 2021 04:22:05 -0700 Subject: [PATCH 39/72] Refactor Worker::shutdown mehtod (#308) --- actix-server/src/worker.rs | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/actix-server/src/worker.rs b/actix-server/src/worker.rs index 63c45757..e9b609f4 100644 --- a/actix-server/src/worker.rs +++ b/actix-server/src/worker.rs @@ -8,7 +8,7 @@ use std::time::Duration; use actix_rt::time::{sleep, Sleep}; use actix_rt::{spawn, Arbiter}; use actix_utils::counter::Counter; -use futures_core::future::LocalBoxFuture; +use futures_core::{future::LocalBoxFuture, ready}; use log::{error, info, trace}; use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}; use tokio::sync::oneshot; @@ -263,19 +263,16 @@ impl ServerWorker { } fn shutdown(&mut self, force: bool) { - if force { - self.services.iter_mut().for_each(|srv| { - if srv.status == WorkerServiceStatus::Available { - srv.status = WorkerServiceStatus::Stopped; - } + self.services + .iter_mut() + .filter(|srv| srv.status == WorkerServiceStatus::Available) + .for_each(|srv| { + srv.status = if force { + WorkerServiceStatus::Stopped + } else { + WorkerServiceStatus::Stopping + }; }); - } else { - self.services.iter_mut().for_each(move |srv| { - if srv.status == WorkerServiceStatus::Available { - srv.status = WorkerServiceStatus::Stopping; - } - }); - } } fn check_readiness(&mut self, cx: &mut Context<'_>) -> Result { @@ -466,16 +463,15 @@ impl Future for ServerWorker { } } - match Pin::new(&mut self.rx).poll_recv(cx) { + match ready!(Pin::new(&mut self.rx).poll_recv(cx)) { // handle incoming io stream - Poll::Ready(Some(WorkerCommand(msg))) => { + 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(()), + None => return Poll::Ready(()), }; }, } From d8889c63ef13b5be584cb99f557ff6072de57875 Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Fri, 2 Apr 2021 04:49:12 -0700 Subject: [PATCH 40/72] Do not do double check on connection num when entering graceful shutdown (#309) --- actix-server/src/worker.rs | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/actix-server/src/worker.rs b/actix-server/src/worker.rs index e9b609f4..5e843983 100644 --- a/actix-server/src/worker.rs +++ b/actix-server/src/worker.rs @@ -353,18 +353,12 @@ impl Future for ServerWorker { return Poll::Ready(()); } else if graceful { self.shutdown(false); - let num = num_connections(); - if num != 0 { - info!("Graceful worker shutdown, {} connections", num); - self.state = WorkerState::Shutdown( - Box::pin(sleep(Duration::from_secs(1))), - Box::pin(sleep(self.config.shutdown_timeout)), - Some(result), - ); - } else { - let _ = result.send(true); - return Poll::Ready(()); - } + info!("Graceful worker shutdown, {} connections", num); + self.state = WorkerState::Shutdown( + Box::pin(sleep(Duration::from_secs(1))), + Box::pin(sleep(self.config.shutdown_timeout)), + Some(result), + ); } else { info!("Force shutdown worker, {} connections", num); self.shutdown(true); From 39d1f282f77d4b5abce06a18de6999a87f458678 Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Sat, 3 Apr 2021 11:01:00 -0700 Subject: [PATCH 41/72] add test for max concurrent connections (#311) --- actix-server/tests/test_server.rs | 79 +++++++++++++++++++++++++++++-- 1 file changed, 76 insertions(+), 3 deletions(-) diff --git a/actix-server/tests/test_server.rs b/actix-server/tests/test_server.rs index 6d413eea..cd61df9f 100644 --- a/actix-server/tests/test_server.rs +++ b/actix-server/tests/test_server.rs @@ -1,4 +1,4 @@ -use std::sync::atomic::{AtomicUsize, Ordering::Relaxed}; +use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{mpsc, Arc}; use std::{net, thread, time}; @@ -169,7 +169,7 @@ fn test_configure() { rt.service("addr1", fn_service(|_| ok::<_, ()>(()))); rt.service("addr3", fn_service(|_| ok::<_, ()>(()))); rt.on_start(lazy(move |_| { - let _ = num.fetch_add(1, Relaxed); + let _ = num.fetch_add(1, Ordering::Relaxed); })) }) }) @@ -187,7 +187,80 @@ fn test_configure() { assert!(net::TcpStream::connect(addr1).is_ok()); assert!(net::TcpStream::connect(addr2).is_ok()); assert!(net::TcpStream::connect(addr3).is_ok()); - assert_eq!(num.load(Relaxed), 1); + assert_eq!(num.load(Ordering::Relaxed), 1); sys.stop(); let _ = h.join(); } + +#[actix_rt::test] +async fn test_max_concurrent_connections() { + // Note: + // A tcp listener would accept connects based on it's backlog setting. + // + // The limit test on the other hand is only for concurrent tcp stream limiting a work + // thread accept. + + use actix_rt::net::TcpStream; + use tokio::io::AsyncWriteExt; + + let addr = unused_addr(); + let (tx, rx) = mpsc::channel(); + + let counter = Arc::new(AtomicUsize::new(0)); + let counter_clone = counter.clone(); + + let max_conn = 3; + + let h = thread::spawn(move || { + actix_rt::System::new().block_on(async { + let server = Server::build() + // Set a relative higher backlog. + .backlog(12) + // max connection for a worker is 3. + .maxconn(max_conn) + .workers(1) + .disable_signals() + .bind("test", addr, move || { + let counter = counter.clone(); + fn_service(move |_io: TcpStream| { + let counter = counter.clone(); + async move { + counter.fetch_add(1, Ordering::SeqCst); + actix_rt::time::sleep(time::Duration::from_secs(20)).await; + counter.fetch_sub(1, Ordering::SeqCst); + Ok::<(), ()>(()) + } + }) + })? + .run(); + + let _ = tx.send((server.clone(), actix_rt::System::current())); + + server.await + }) + }); + + let (srv, sys) = rx.recv().unwrap(); + + let mut conns = vec![]; + + for _ in 0..12 { + let conn = tokio::net::TcpStream::connect(addr).await.unwrap(); + conns.push(conn); + } + + actix_rt::time::sleep(time::Duration::from_secs(5)).await; + + // counter would remain at 3 even with 12 successful connection. + // and 9 of them remain in backlog. + assert_eq!(max_conn, counter_clone.load(Ordering::SeqCst)); + + for mut conn in conns { + conn.shutdown().await.unwrap(); + } + + srv.stop(false).await; + + sys.stop(); + let _ = h.join().unwrap(); +} From fd3e5fba027c83fe4cbc05ddaa1eb9cb5d6eba0b Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Sat, 3 Apr 2021 11:40:12 -0700 Subject: [PATCH 42/72] Refactor actix_server WorkerState::Restarting enum variant. (#306) Co-authored-by: Rob Ede --- actix-server/src/config.rs | 4 ++-- actix-server/src/lib.rs | 20 ++---------------- actix-server/src/service.rs | 7 +++++-- actix-server/src/worker.rs | 42 ++++++++++++++++++------------------- 4 files changed, 30 insertions(+), 43 deletions(-) diff --git a/actix-server/src/config.rs b/actix-server/src/config.rs index 20270a2f..28e4fdeb 100644 --- a/actix-server/src/config.rs +++ b/actix-server/src/config.rs @@ -7,14 +7,14 @@ use actix_service::{ fn_service, IntoServiceFactory as IntoBaseServiceFactory, ServiceFactory as BaseServiceFactory, }; -use actix_utils::counter::CounterGuard; +use actix_utils::{counter::CounterGuard, future::ready}; use futures_core::future::LocalBoxFuture; use log::error; use crate::builder::bind_addr; use crate::service::{BoxedServerService, InternalServiceFactory, StreamService}; use crate::socket::{MioStream, MioTcpListener, StdSocketAddr, StdTcpListener, ToSocketAddrs}; -use crate::{ready, Token}; +use crate::Token; pub struct ServiceConfig { pub(crate) services: Vec<(String, MioTcpListener)>, diff --git a/actix-server/src/lib.rs b/actix-server/src/lib.rs index 24129b5a..af9ab0b0 100644 --- a/actix-server/src/lib.rs +++ b/actix-server/src/lib.rs @@ -55,24 +55,6 @@ 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 { @@ -132,6 +114,8 @@ impl Future for JoinAll { mod test { use super::*; + use actix_utils::future::ready; + #[actix_rt::test] async fn test_join_all() { let futs = vec![ready(Ok(1)), ready(Err(3)), ready(Ok(9))]; diff --git a/actix-server/src/service.rs b/actix-server/src/service.rs index 63d2c1f5..835ee10b 100644 --- a/actix-server/src/service.rs +++ b/actix-server/src/service.rs @@ -3,12 +3,15 @@ use std::net::SocketAddr; use std::task::{Context, Poll}; use actix_service::{Service, ServiceFactory as BaseServiceFactory}; -use actix_utils::counter::CounterGuard; +use actix_utils::{ + counter::CounterGuard, + future::{ready, Ready}, +}; use futures_core::future::LocalBoxFuture; use log::error; use crate::socket::{FromStream, MioStream}; -use crate::{ready, Ready, Token}; +use crate::Token; pub trait ServiceFactory: Send + Clone + 'static { type Factory: BaseServiceFactory; diff --git a/actix-server/src/worker.rs b/actix-server/src/worker.rs index 5e843983..366dab0b 100644 --- a/actix-server/src/worker.rs +++ b/actix-server/src/worker.rs @@ -387,27 +387,27 @@ impl Future for ServerWorker { } }, WorkerState::Restarting(idx, token, ref mut fut) => { - match fut.as_mut().poll(cx) { - Poll::Ready(Ok(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; - return self.poll(cx); - } - } - Poll::Ready(Err(_)) => { - panic!( - "Can not restart {:?} service", - self.factories[idx].name(token) - ); - } - Poll::Pending => return Poll::Pending, - } + let item = ready!(fut.as_mut().poll(cx)).unwrap_or_else(|_| { + panic!( + "Can not restart {:?} service", + self.factories[idx].name(token) + ) + }); + + // Only interest in the first item? + let (token, service) = item + .into_iter() + .next() + .expect("No BoxedServerService. Restarting can not progress"); + + trace!( + "Service {:?} has been restarted", + self.factories[idx].name(token) + ); + + self.services[token.0].created(service); + self.state = WorkerState::Unavailable; + self.poll(cx) } WorkerState::Shutdown(ref mut t1, ref mut t2, ref mut tx) => { From 05689b86d9c7679862c9470ed9008fd61a2f19e4 Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Sun, 4 Apr 2021 02:53:06 -0700 Subject: [PATCH 43/72] Remove Option wrapper for CounterGuard (#313) --- actix-server/src/config.rs | 4 ++-- actix-server/src/service.rs | 6 +++--- actix-server/src/worker.rs | 4 +--- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/actix-server/src/config.rs b/actix-server/src/config.rs index 28e4fdeb..c5e63630 100644 --- a/actix-server/src/config.rs +++ b/actix-server/src/config.rs @@ -243,7 +243,7 @@ impl ServiceRuntime { type BoxedNewService = Box< dyn BaseServiceFactory< - (Option, MioStream), + (CounterGuard, MioStream), Response = (), Error = (), InitError = (), @@ -257,7 +257,7 @@ struct ServiceFactory { inner: T, } -impl BaseServiceFactory<(Option, MioStream)> for ServiceFactory +impl BaseServiceFactory<(CounterGuard, MioStream)> for ServiceFactory where T: BaseServiceFactory, T::Future: 'static, diff --git a/actix-server/src/service.rs b/actix-server/src/service.rs index 835ee10b..da57af67 100644 --- a/actix-server/src/service.rs +++ b/actix-server/src/service.rs @@ -29,7 +29,7 @@ pub(crate) trait InternalServiceFactory: Send { pub(crate) type BoxedServerService = Box< dyn Service< - (Option, MioStream), + (CounterGuard, MioStream), Response = (), Error = (), Future = Ready>, @@ -50,7 +50,7 @@ impl StreamService { } } -impl Service<(Option, MioStream)> for StreamService +impl Service<(CounterGuard, MioStream)> for StreamService where S: Service, S::Future: 'static, @@ -65,7 +65,7 @@ where self.service.poll_ready(ctx).map_err(|_| ()) } - fn call(&self, (guard, req): (Option, MioStream)) -> Self::Future { + fn call(&self, (guard, req): (CounterGuard, MioStream)) -> Self::Future { ready(match FromStream::from_mio(req) { Ok(stream) => { let f = self.service.call(stream); diff --git a/actix-server/src/worker.rs b/actix-server/src/worker.rs index 366dab0b..f289f2d2 100644 --- a/actix-server/src/worker.rs +++ b/actix-server/src/worker.rs @@ -461,9 +461,7 @@ impl Future for ServerWorker { // handle incoming io stream Some(WorkerCommand(msg)) => { let guard = self.conns.get(); - let _ = self.services[msg.token.0] - .service - .call((Some(guard), msg.io)); + let _ = self.services[msg.token.0].service.call((guard, msg.io)); } None => return Poll::Ready(()), }; From 8079c50ddb3d2cc9fccd56a1c53105cacf55b821 Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Sun, 4 Apr 2021 05:22:34 -0700 Subject: [PATCH 44/72] Add ServerWorker::restart_service method (#314) Co-authored-by: Rob Ede --- actix-server/src/worker.rs | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/actix-server/src/worker.rs b/actix-server/src/worker.rs index f289f2d2..bd28ccda 100644 --- a/actix-server/src/worker.rs +++ b/actix-server/src/worker.rs @@ -262,6 +262,13 @@ impl ServerWorker { WorkerHandle::new(idx, tx1, tx2, avail) } + fn restart_service(&mut self, token: Token, idx: usize) { + let factory = &self.factories[idx]; + trace!("Service {:?} failed, restarting", factory.name(token)); + self.services[token.0].status = WorkerServiceStatus::Restarting; + self.state = WorkerState::Restarting(idx, token, factory.create()); + } + fn shutdown(&mut self, force: bool) { self.services .iter_mut() @@ -376,13 +383,7 @@ impl Future for ServerWorker { } 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.restart_service(token, idx); self.poll(cx) } }, @@ -437,7 +438,7 @@ impl Future for ServerWorker { // actively poll stream and handle worker command WorkerState::Available => loop { match self.check_readiness(cx) { - Ok(true) => (), + Ok(true) => {} Ok(false) => { trace!("Worker is unavailable"); self.availability.set(false); @@ -445,14 +446,8 @@ impl Future for ServerWorker { return self.poll(cx); } Err((token, idx)) => { - trace!( - "Service {:?} failed, restarting", - self.factories[idx].name(token) - ); + self.restart_service(token, idx); 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); } } From 8aade720ede221ec0962f52692fe4a1f00c182ee Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Sun, 4 Apr 2021 12:34:52 -0700 Subject: [PATCH 45/72] Refactor WorkerState::Shutdown (#310) --- actix-server/src/worker.rs | 149 +++++++++++++++++++++---------------- 1 file changed, 84 insertions(+), 65 deletions(-) diff --git a/actix-server/src/worker.rs b/actix-server/src/worker.rs index bd28ccda..9409dfb4 100644 --- a/actix-server/src/worker.rs +++ b/actix-server/src/worker.rs @@ -1,17 +1,27 @@ -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::Duration; +use std::{ + future::Future, + mem, + pin::Pin, + sync::{ + atomic::{AtomicBool, AtomicUsize, Ordering}, + Arc, + }, + task::{Context, Poll}, + time::Duration, +}; -use actix_rt::time::{sleep, Sleep}; -use actix_rt::{spawn, Arbiter}; +use actix_rt::{ + spawn, + time::{sleep, Instant, Sleep}, + Arbiter, +}; use actix_utils::counter::Counter; use futures_core::{future::LocalBoxFuture, ready}; use log::{error, info, trace}; -use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}; -use tokio::sync::oneshot; +use tokio::sync::{ + mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}, + oneshot, +}; use crate::service::{BoxedServerService, InternalServiceFactory}; use crate::socket::MioStream; @@ -132,7 +142,7 @@ pub(crate) struct ServerWorker { conns: Counter, factories: Vec>, state: WorkerState, - config: ServerWorkerConfig, + shutdown_timeout: Duration, } struct WorkerService { @@ -211,12 +221,12 @@ impl ServerWorker { let mut wrk = MAX_CONNS_COUNTER.with(move |conns| ServerWorker { rx, rx2, + services: Default::default(), availability, factories, - config, - services: Vec::new(), + state: Default::default(), + shutdown_timeout: config.shutdown_timeout, conns: conns.clone(), - state: WorkerState::Unavailable, }); let fut = wrk @@ -337,53 +347,61 @@ enum WorkerState { Token, LocalBoxFuture<'static, Result, ()>>, ), - Shutdown( - Pin>, - Pin>, - Option>, - ), + // Shutdown keep states necessary for server shutdown: + // Sleep for interval check the shutdown progress. + // Instant for the start time of shutdown. + // Sender for send back the shutdown outcome(force/grace) to StopCommand caller. + Shutdown(Pin>, Instant, oneshot::Sender), +} + +impl Default for WorkerState { + fn default() -> Self { + Self::Unavailable + } } impl Future for ServerWorker { type Output = (); fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.as_mut().get_mut(); + // `StopWorker` message handler if let Poll::Ready(Some(StopCommand { graceful, result })) = - Pin::new(&mut self.rx2).poll_recv(cx) + Pin::new(&mut this.rx2).poll_recv(cx) { - self.availability.set(false); + this.availability.set(false); let num = num_connections(); if num == 0 { info!("Shutting down worker, 0 connections"); let _ = result.send(true); return Poll::Ready(()); } else if graceful { - self.shutdown(false); info!("Graceful worker shutdown, {} connections", num); - self.state = WorkerState::Shutdown( - Box::pin(sleep(Duration::from_secs(1))), - Box::pin(sleep(self.config.shutdown_timeout)), - Some(result), - ); + this.shutdown(false); + + let timer = Box::pin(sleep(Duration::from_secs(1))); + let start_from = Instant::now(); + this.state = WorkerState::Shutdown(timer, start_from, result); } else { info!("Force shutdown worker, {} connections", num); - self.shutdown(true); + this.shutdown(true); + let _ = result.send(false); return Poll::Ready(()); } } - match self.state { - WorkerState::Unavailable => match self.check_readiness(cx) { + match this.state { + WorkerState::Unavailable => match this.check_readiness(cx) { Ok(true) => { - self.state = WorkerState::Available; - self.availability.set(true); + this.state = WorkerState::Available; + this.availability.set(true); self.poll(cx) } Ok(false) => Poll::Pending, Err((token, idx)) => { - self.restart_service(token, idx); + this.restart_service(token, idx); self.poll(cx) } }, @@ -391,7 +409,7 @@ impl Future for ServerWorker { let item = ready!(fut.as_mut().poll(cx)).unwrap_or_else(|_| { panic!( "Can not restart {:?} service", - self.factories[idx].name(token) + this.factories[idx].name(token) ) }); @@ -403,60 +421,61 @@ impl Future for ServerWorker { trace!( "Service {:?} has been restarted", - self.factories[idx].name(token) + this.factories[idx].name(token) ); - self.services[token.0].created(service); - self.state = WorkerState::Unavailable; + this.services[token.0].created(service); + this.state = WorkerState::Unavailable; self.poll(cx) } - WorkerState::Shutdown(ref mut t1, ref mut t2, ref mut tx) => { - let num = num_connections(); - if num == 0 { - let _ = tx.take().unwrap().send(true); + WorkerState::Shutdown(ref mut timer, ref start_from, _) => { + // Wait for 1 second. + ready!(timer.as_mut().poll(cx)); + + if num_connections() == 0 { + // Graceful shutdown. + if let WorkerState::Shutdown(_, _, sender) = mem::take(&mut this.state) { + let _ = sender.send(true); + } Arbiter::current().stop(); - return Poll::Ready(()); - } - - // check graceful timeout - if Pin::new(t2).poll(cx).is_ready() { - let _ = tx.take().unwrap().send(false); - self.shutdown(true); + Poll::Ready(()) + } else if start_from.elapsed() >= this.shutdown_timeout { + // Timeout forceful shutdown. + if let WorkerState::Shutdown(_, _, sender) = mem::take(&mut this.state) { + let _ = sender.send(false); + } Arbiter::current().stop(); - return Poll::Ready(()); + Poll::Ready(()) + } else { + // Reset timer and wait for 1 second. + let time = Instant::now() + Duration::from_secs(1); + timer.as_mut().reset(time); + timer.as_mut().poll(cx) } - - // sleep for 1 second and then check again - if t1.as_mut().poll(cx).is_ready() { - *t1 = Box::pin(sleep(Duration::from_secs(1))); - let _ = t1.as_mut().poll(cx); - } - - Poll::Pending } // actively poll stream and handle worker command WorkerState::Available => loop { - match self.check_readiness(cx) { + match this.check_readiness(cx) { Ok(true) => {} Ok(false) => { trace!("Worker is unavailable"); - self.availability.set(false); - self.state = WorkerState::Unavailable; + this.availability.set(false); + this.state = WorkerState::Unavailable; return self.poll(cx); } Err((token, idx)) => { - self.restart_service(token, idx); - self.availability.set(false); + this.restart_service(token, idx); + this.availability.set(false); return self.poll(cx); } } - match ready!(Pin::new(&mut self.rx).poll_recv(cx)) { + match ready!(Pin::new(&mut this.rx).poll_recv(cx)) { // handle incoming io stream Some(WorkerCommand(msg)) => { - let guard = self.conns.get(); - let _ = self.services[msg.token.0].service.call((guard, msg.io)); + let guard = this.conns.get(); + let _ = this.services[msg.token.0].service.call((guard, msg.io)); } None => return Poll::Ready(()), }; From 3859e91799aae4a7fdaf667091ab1416a7e66f1e Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Sun, 4 Apr 2021 13:53:19 -0700 Subject: [PATCH 46/72] Use named type for WorkerState::Restarting and Shutdown (#317) --- actix-server/src/worker.rs | 89 +++++++++++++++++++++++--------------- 1 file changed, 53 insertions(+), 36 deletions(-) diff --git a/actix-server/src/worker.rs b/actix-server/src/worker.rs index 9409dfb4..6417dd0b 100644 --- a/actix-server/src/worker.rs +++ b/actix-server/src/worker.rs @@ -34,7 +34,7 @@ pub(crate) struct WorkerCommand(Conn); /// and `false` if some connections still alive. pub(crate) struct StopCommand { graceful: bool, - result: oneshot::Sender, + tx: oneshot::Sender, } #[derive(Debug)] @@ -98,8 +98,8 @@ impl WorkerHandle { } pub fn stop(&self, graceful: bool) -> oneshot::Receiver { - let (result, rx) = oneshot::channel(); - let _ = self.tx2.send(StopCommand { graceful, result }); + let (tx, rx) = oneshot::channel(); + let _ = self.tx2.send(StopCommand { graceful, tx }); rx } } @@ -221,7 +221,7 @@ impl ServerWorker { let mut wrk = MAX_CONNS_COUNTER.with(move |conns| ServerWorker { rx, rx2, - services: Default::default(), + services: Vec::new(), availability, factories, state: Default::default(), @@ -272,11 +272,15 @@ impl ServerWorker { WorkerHandle::new(idx, tx1, tx2, avail) } - fn restart_service(&mut self, token: Token, idx: usize) { - let factory = &self.factories[idx]; + fn restart_service(&mut self, token: Token, factory_id: usize) { + let factory = &self.factories[factory_id]; trace!("Service {:?} failed, restarting", factory.name(token)); self.services[token.0].status = WorkerServiceStatus::Restarting; - self.state = WorkerState::Restarting(idx, token, factory.create()); + self.state = WorkerState::Restarting(Restart { + factory_id, + token, + fut: factory.create(), + }); } fn shutdown(&mut self, force: bool) { @@ -342,16 +346,24 @@ impl ServerWorker { enum WorkerState { Available, Unavailable, - Restarting( - usize, - Token, - LocalBoxFuture<'static, Result, ()>>, - ), - // Shutdown keep states necessary for server shutdown: - // Sleep for interval check the shutdown progress. - // Instant for the start time of shutdown. - // Sender for send back the shutdown outcome(force/grace) to StopCommand caller. - Shutdown(Pin>, Instant, oneshot::Sender), + Restarting(Restart), + Shutdown(Shutdown), +} + +struct Restart { + factory_id: usize, + token: Token, + fut: LocalBoxFuture<'static, Result, ()>>, +} + +// Shutdown keep states necessary for server shutdown: +// Sleep for interval check the shutdown progress. +// Instant for the start time of shutdown. +// Sender for send back the shutdown outcome(force/grace) to StopCommand caller. +struct Shutdown { + timer: Pin>, + start_from: Instant, + tx: oneshot::Sender, } impl Default for WorkerState { @@ -367,27 +379,29 @@ impl Future for ServerWorker { let this = self.as_mut().get_mut(); // `StopWorker` message handler - if let Poll::Ready(Some(StopCommand { graceful, result })) = + if let Poll::Ready(Some(StopCommand { graceful, tx })) = Pin::new(&mut this.rx2).poll_recv(cx) { this.availability.set(false); let num = num_connections(); if num == 0 { info!("Shutting down worker, 0 connections"); - let _ = result.send(true); + let _ = tx.send(true); return Poll::Ready(()); } else if graceful { info!("Graceful worker shutdown, {} connections", num); this.shutdown(false); - let timer = Box::pin(sleep(Duration::from_secs(1))); - let start_from = Instant::now(); - this.state = WorkerState::Shutdown(timer, start_from, result); + this.state = WorkerState::Shutdown(Shutdown { + timer: Box::pin(sleep(Duration::from_secs(1))), + start_from: Instant::now(), + tx, + }); } else { info!("Force shutdown worker, {} connections", num); this.shutdown(true); - let _ = result.send(false); + let _ = tx.send(false); return Poll::Ready(()); } } @@ -405,11 +419,14 @@ impl Future for ServerWorker { self.poll(cx) } }, - WorkerState::Restarting(idx, token, ref mut fut) => { - let item = ready!(fut.as_mut().poll(cx)).unwrap_or_else(|_| { + WorkerState::Restarting(ref mut restart) => { + let factory_id = restart.factory_id; + let token = restart.token; + + let item = ready!(restart.fut.as_mut().poll(cx)).unwrap_or_else(|_| { panic!( "Can not restart {:?} service", - this.factories[idx].name(token) + this.factories[factory_id].name(token) ) }); @@ -421,7 +438,7 @@ impl Future for ServerWorker { trace!( "Service {:?} has been restarted", - this.factories[idx].name(token) + this.factories[factory_id].name(token) ); this.services[token.0].created(service); @@ -429,29 +446,29 @@ impl Future for ServerWorker { self.poll(cx) } - WorkerState::Shutdown(ref mut timer, ref start_from, _) => { + WorkerState::Shutdown(ref mut shutdown) => { // Wait for 1 second. - ready!(timer.as_mut().poll(cx)); + ready!(shutdown.timer.as_mut().poll(cx)); if num_connections() == 0 { // Graceful shutdown. - if let WorkerState::Shutdown(_, _, sender) = mem::take(&mut this.state) { - let _ = sender.send(true); + if let WorkerState::Shutdown(shutdown) = mem::take(&mut this.state) { + let _ = shutdown.tx.send(true); } Arbiter::current().stop(); Poll::Ready(()) - } else if start_from.elapsed() >= this.shutdown_timeout { + } else if shutdown.start_from.elapsed() >= this.shutdown_timeout { // Timeout forceful shutdown. - if let WorkerState::Shutdown(_, _, sender) = mem::take(&mut this.state) { - let _ = sender.send(false); + if let WorkerState::Shutdown(shutdown) = mem::take(&mut this.state) { + let _ = shutdown.tx.send(false); } Arbiter::current().stop(); Poll::Ready(()) } else { // Reset timer and wait for 1 second. let time = Instant::now() + Duration::from_secs(1); - timer.as_mut().reset(time); - timer.as_mut().poll(cx) + shutdown.timer.as_mut().reset(time); + shutdown.timer.as_mut().poll(cx) } } // actively poll stream and handle worker command From f1573931dd1d1cf79ba24c9413958e020189241d Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Sun, 4 Apr 2021 15:00:12 -0700 Subject: [PATCH 47/72] Remove MAX_CONN (#316) --- actix-server/src/builder.rs | 6 +++--- actix-server/src/worker.rs | 39 ++++++++++++------------------------- 2 files changed, 15 insertions(+), 30 deletions(-) diff --git a/actix-server/src/builder.rs b/actix-server/src/builder.rs index c20bb4f5..fdb02205 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, ServerWorkerConfig, WorkerAvailability, WorkerHandle}; +use crate::worker::{ServerWorker, ServerWorkerConfig, WorkerAvailability, WorkerHandle}; use crate::{join_all, Token}; /// Server builder @@ -117,8 +117,8 @@ impl ServerBuilder { /// reached for each worker. /// /// By default max connections is set to a 25k per worker. - pub fn maxconn(self, num: usize) -> Self { - worker::max_concurrent_connections(num); + pub fn maxconn(mut self, num: usize) -> Self { + self.worker_config.max_concurrent_connections(num); self } diff --git a/actix-server/src/worker.rs b/actix-server/src/worker.rs index 6417dd0b..fac9202e 100644 --- a/actix-server/src/worker.rs +++ b/actix-server/src/worker.rs @@ -3,7 +3,7 @@ use std::{ mem, pin::Pin, sync::{ - atomic::{AtomicBool, AtomicUsize, Ordering}, + atomic::{AtomicBool, Ordering}, Arc, }, task::{Context, Poll}, @@ -43,27 +43,6 @@ pub(crate) struct Conn { pub token: Token, } -static MAX_CONNS: AtomicUsize = AtomicUsize::new(25600); - -/// Sets the maximum per-worker number of concurrent connections. -/// -/// All socket listeners will stop accepting connections when this limit is -/// reached for each worker. -/// -/// By default max connections is set to a 25k per worker. -pub fn max_concurrent_connections(num: usize) { - MAX_CONNS.store(num, Ordering::Relaxed); -} - -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)] @@ -173,6 +152,7 @@ enum WorkerServiceStatus { pub(crate) struct ServerWorkerConfig { shutdown_timeout: Duration, max_blocking_threads: usize, + max_concurrent_connections: usize, } impl Default for ServerWorkerConfig { @@ -182,6 +162,7 @@ impl Default for ServerWorkerConfig { Self { shutdown_timeout: Duration::from_secs(30), max_blocking_threads, + max_concurrent_connections: 25600, } } } @@ -191,6 +172,10 @@ impl ServerWorkerConfig { self.max_blocking_threads = num; } + pub(crate) fn max_concurrent_connections(&mut self, num: usize) { + self.max_concurrent_connections = num; + } + pub(crate) fn shutdown_timeout(&mut self, dur: Duration) { self.shutdown_timeout = dur; } @@ -218,16 +203,16 @@ impl ServerWorker { }) .spawn(async move { availability.set(false); - let mut wrk = MAX_CONNS_COUNTER.with(move |conns| ServerWorker { + let mut wrk = ServerWorker { rx, rx2, services: Vec::new(), availability, + conns: Counter::new(config.max_concurrent_connections), factories, state: Default::default(), shutdown_timeout: config.shutdown_timeout, - conns: conns.clone(), - }); + }; let fut = wrk .factories @@ -383,7 +368,7 @@ impl Future for ServerWorker { Pin::new(&mut this.rx2).poll_recv(cx) { this.availability.set(false); - let num = num_connections(); + let num = this.conns.total(); if num == 0 { info!("Shutting down worker, 0 connections"); let _ = tx.send(true); @@ -450,7 +435,7 @@ impl Future for ServerWorker { // Wait for 1 second. ready!(shutdown.timer.as_mut().poll(cx)); - if num_connections() == 0 { + if this.conns.total() == 0 { // Graceful shutdown. if let WorkerState::Shutdown(shutdown) = mem::take(&mut this.state) { let _ = shutdown.tx.send(true); From 995efcf427b9a8ad378bab5c616f91c8f292c2a6 Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Mon, 5 Apr 2021 05:38:41 -0700 Subject: [PATCH 48/72] Fix bug where paused Accept would register timed out sockets (#312) --- actix-server/src/accept.rs | 46 +++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/actix-server/src/accept.rs b/actix-server/src/accept.rs index 2750d1c5..5b6345cc 100644 --- a/actix-server/src/accept.rs +++ b/actix-server/src/accept.rs @@ -300,27 +300,41 @@ impl Accept { } fn deregister_all(&self, sockets: &mut Slab) { - sockets.iter_mut().for_each(|(_, info)| { - self.deregister_logged(info); - }); + // This is a best effort implementation with following limitation: + // + // Every ServerSocketInfo with associate timeout will be skipped and it's timeout + // is removed in the process. + // + // Therefore WakerInterest::Pause followed by WakerInterest::Resume in a very short + // gap (less than 500ms) would cause all timing out ServerSocketInfos be reregistered + // before expected timing. + sockets + .iter_mut() + // Take all timeout. + // This is to prevent Accept::process_timer method re-register a socket afterwards. + .map(|(_, info)| (info.timeout.take(), info)) + // Socket info with a timeout is already deregistered so skip them. + .filter(|(timeout, _)| timeout.is_none()) + .for_each(|(_, info)| self.deregister_logged(info)); } fn maybe_backpressure(&mut self, sockets: &mut Slab, on: bool) { // Only operate when server is in a different backpressure than the given flag. if self.backpressure != on { - if on { - self.backpressure = true; - // TODO: figure out if timing out sockets can be safely de-registered twice. - self.deregister_all(sockets); - } else { - self.backpressure = false; - sockets - .iter_mut() - // Only operate on sockets without associated timeout. - // Sockets with it will attempt to re-register when their timeout expires. - .filter(|(_, info)| info.timeout.is_none()) - .for_each(|(token, info)| self.register_logged(token, info)); - } + self.backpressure = on; + sockets + .iter_mut() + // Only operate on sockets without associated timeout. + // Sockets with it should be handled by `accept` and `process_timer` methods. + // They are already deregistered or need to be reregister in the future. + .filter(|(_, info)| info.timeout.is_none()) + .for_each(|(token, info)| { + if on { + self.deregister_logged(info); + } else { + self.register_logged(token, info); + } + }); } } From 5961eb892e894db036168bb50ef9f3235d2780b1 Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Mon, 5 Apr 2021 12:39:05 -0700 Subject: [PATCH 49/72] Fix bug where worker service restart could skip failing services and not being able to restart multiple services (#318) --- actix-server/src/worker.rs | 33 +++--- actix-server/tests/test_server.rs | 174 ++++++++++++++++++++++++++++++ 2 files changed, 189 insertions(+), 18 deletions(-) diff --git a/actix-server/src/worker.rs b/actix-server/src/worker.rs index fac9202e..c3074646 100644 --- a/actix-server/src/worker.rs +++ b/actix-server/src/worker.rs @@ -283,7 +283,6 @@ impl ServerWorker { fn check_readiness(&mut self, cx: &mut Context<'_>) -> Result { let mut ready = self.conns.available(cx); - let mut failed = None; for (idx, srv) in self.services.iter_mut().enumerate() { if srv.status == WorkerServiceStatus::Available || srv.status == WorkerServiceStatus::Unavailable @@ -314,17 +313,14 @@ impl ServerWorker { "Service {:?} readiness check returned error, restarting", self.factories[srv.factory].name(Token(idx)) ); - failed = Some((Token(idx), srv.factory)); srv.status = WorkerServiceStatus::Failed; + return Err((Token(idx), srv.factory)); } } } } - if let Some(idx) = failed { - Err(idx) - } else { - Ok(ready) - } + + Ok(ready) } } @@ -408,18 +404,19 @@ impl Future for ServerWorker { let factory_id = restart.factory_id; let token = restart.token; - let item = ready!(restart.fut.as_mut().poll(cx)).unwrap_or_else(|_| { - panic!( - "Can not restart {:?} service", - this.factories[factory_id].name(token) - ) - }); - - // Only interest in the first item? - let (token, service) = item + let service = ready!(restart.fut.as_mut().poll(cx)) + .unwrap_or_else(|_| { + panic!( + "Can not restart {:?} service", + this.factories[factory_id].name(token) + ) + }) .into_iter() - .next() - .expect("No BoxedServerService. Restarting can not progress"); + // Find the same token from vector. There should be only one + // So the first match would be enough. + .find(|(t, _)| *t == token) + .map(|(_, service)| service) + .expect("No BoxedServerService found"); trace!( "Service {:?} has been restarted", diff --git a/actix-server/tests/test_server.rs b/actix-server/tests/test_server.rs index cd61df9f..40b07e1c 100644 --- a/actix-server/tests/test_server.rs +++ b/actix-server/tests/test_server.rs @@ -264,3 +264,177 @@ async fn test_max_concurrent_connections() { sys.stop(); let _ = h.join().unwrap(); } + +#[actix_rt::test] +async fn test_service_restart() { + use std::task::{Context, Poll}; + use std::time::Duration; + + use actix_rt::{net::TcpStream, time::sleep}; + use actix_service::{fn_factory, Service}; + use futures_core::future::LocalBoxFuture; + use tokio::io::AsyncWriteExt; + + struct TestService(Arc); + + impl Service for TestService { + type Response = (); + type Error = (); + type Future = LocalBoxFuture<'static, Result>; + + fn poll_ready(&self, _: &mut Context<'_>) -> Poll> { + let TestService(ref counter) = self; + let c = counter.fetch_add(1, Ordering::SeqCst); + // Force the service to restart on first readiness check. + if c > 0 { + Poll::Ready(Ok(())) + } else { + Poll::Ready(Err(())) + } + } + + fn call(&self, _: TcpStream) -> Self::Future { + Box::pin(async { Ok(()) }) + } + } + + let addr1 = unused_addr(); + let addr2 = unused_addr(); + let (tx, rx) = mpsc::channel(); + let num = Arc::new(AtomicUsize::new(0)); + let num2 = Arc::new(AtomicUsize::new(0)); + + let num_clone = num.clone(); + let num2_clone = num2.clone(); + + let h = thread::spawn(move || { + actix_rt::System::new().block_on(async { + let server = Server::build() + .backlog(1) + .disable_signals() + .configure(move |cfg| { + let num = num.clone(); + let num2 = num2.clone(); + cfg.bind("addr1", addr1) + .unwrap() + .bind("addr2", addr2) + .unwrap() + .apply(move |rt| { + let num = num.clone(); + let num2 = num2.clone(); + rt.service( + "addr1", + fn_factory(move || { + let num = num.clone(); + async move { Ok::<_, ()>(TestService(num)) } + }), + ); + rt.service( + "addr2", + fn_factory(move || { + let num2 = num2.clone(); + async move { Ok::<_, ()>(TestService(num2)) } + }), + ); + }) + }) + .unwrap() + .workers(1) + .run(); + + let _ = tx.send((server.clone(), actix_rt::System::current())); + server.await + }) + }); + + let (server, sys) = rx.recv().unwrap(); + + for _ in 0..5 { + TcpStream::connect(addr1) + .await + .unwrap() + .shutdown() + .await + .unwrap(); + TcpStream::connect(addr2) + .await + .unwrap() + .shutdown() + .await + .unwrap(); + } + + sleep(Duration::from_secs(3)).await; + + assert!(num_clone.load(Ordering::SeqCst) > 5); + assert!(num2_clone.load(Ordering::SeqCst) > 5); + + sys.stop(); + let _ = server.stop(false); + let _ = h.join().unwrap(); + + let addr1 = unused_addr(); + let addr2 = unused_addr(); + let (tx, rx) = mpsc::channel(); + let num = Arc::new(AtomicUsize::new(0)); + let num2 = Arc::new(AtomicUsize::new(0)); + + let num_clone = num.clone(); + let num2_clone = num2.clone(); + + let h = thread::spawn(move || { + let num = num.clone(); + actix_rt::System::new().block_on(async { + let server = Server::build() + .backlog(1) + .disable_signals() + .bind("addr1", addr1, move || { + let num = num.clone(); + fn_factory(move || { + let num = num.clone(); + async move { Ok::<_, ()>(TestService(num)) } + }) + }) + .unwrap() + .bind("addr2", addr2, move || { + let num2 = num2.clone(); + fn_factory(move || { + let num2 = num2.clone(); + async move { Ok::<_, ()>(TestService(num2)) } + }) + }) + .unwrap() + .workers(1) + .run(); + + let _ = tx.send((server.clone(), actix_rt::System::current())); + server.await + }) + }); + + let (server, sys) = rx.recv().unwrap(); + + for _ in 0..5 { + TcpStream::connect(addr1) + .await + .unwrap() + .shutdown() + .await + .unwrap(); + TcpStream::connect(addr2) + .await + .unwrap() + .shutdown() + .await + .unwrap(); + } + + sleep(Duration::from_secs(3)).await; + + assert!(num_clone.load(Ordering::SeqCst) > 5); + assert!(num2_clone.load(Ordering::SeqCst) > 5); + + sys.stop(); + let _ = server.stop(false); + let _ = h.join().unwrap(); +} From d4829b046defc6466045aeeff3c18f5b495b579a Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Thu, 8 Apr 2021 15:15:10 -0700 Subject: [PATCH 50/72] do no drain backlog on backpressure (#322) --- actix-server/src/accept.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actix-server/src/accept.rs b/actix-server/src/accept.rs index 5b6345cc..2463ce47 100644 --- a/actix-server/src/accept.rs +++ b/actix-server/src/accept.rs @@ -407,7 +407,7 @@ impl Accept { } fn accept(&mut self, sockets: &mut Slab, token: usize) { - loop { + while !self.backpressure { let info = sockets .get_mut(token) .expect("ServerSocketInfo is removed from Slab"); From 859f45868d8551381d8877a89f394e3bfcb7ba43 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Fri, 9 Apr 2021 21:04:41 +0100 Subject: [PATCH 51/72] Revert "do no drain backlog on backpressure" (#324) This reverts commit d4829b046defc6466045aeeff3c18f5b495b579a. --- actix-server/src/accept.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actix-server/src/accept.rs b/actix-server/src/accept.rs index 2463ce47..5b6345cc 100644 --- a/actix-server/src/accept.rs +++ b/actix-server/src/accept.rs @@ -407,7 +407,7 @@ impl Accept { } fn accept(&mut self, sockets: &mut Slab, token: usize) { - while !self.backpressure { + loop { let info = sockets .get_mut(token) .expect("ServerSocketInfo is removed from Slab"); From 0a11cf5cba713d56b449658117a41b9c68e714ce Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Fri, 9 Apr 2021 17:03:28 -0700 Subject: [PATCH 52/72] Separate WorkerHandle to two parts (#323) --- actix-server/src/accept.rs | 10 ++-- actix-server/src/builder.rs | 27 +++++++---- actix-server/src/waker_queue.rs | 6 +-- actix-server/src/worker.rs | 86 ++++++++++++++++++--------------- 4 files changed, 74 insertions(+), 55 deletions(-) diff --git a/actix-server/src/accept.rs b/actix-server/src/accept.rs index 5b6345cc..5b9f99c7 100644 --- a/actix-server/src/accept.rs +++ b/actix-server/src/accept.rs @@ -12,7 +12,7 @@ use slab::Slab; use crate::server::Server; use crate::socket::{MioListener, SocketAddr}; use crate::waker_queue::{WakerInterest, WakerQueue, WAKER_TOKEN}; -use crate::worker::{Conn, WorkerHandle}; +use crate::worker::{Conn, WorkerHandleAccept}; use crate::Token; struct ServerSocketInfo { @@ -66,7 +66,7 @@ impl AcceptLoop { pub(crate) fn start( &mut self, socks: Vec<(Token, MioListener)>, - handles: Vec, + handles: Vec, ) { let srv = self.srv.take().expect("Can not re-use AcceptInfo"); let poll = self.poll.take().unwrap(); @@ -80,7 +80,7 @@ impl AcceptLoop { struct Accept { poll: Poll, waker: WakerQueue, - handles: Vec, + handles: Vec, srv: Server, next: usize, backpressure: bool, @@ -105,7 +105,7 @@ impl Accept { waker: WakerQueue, socks: Vec<(Token, MioListener)>, srv: Server, - handles: Vec, + handles: Vec, ) { // Accept runs in its own thread and would want to spawn additional futures to current // actix system. @@ -125,7 +125,7 @@ impl Accept { poll: Poll, waker: WakerQueue, socks: Vec<(Token, MioListener)>, - handles: Vec, + handles: Vec, srv: Server, ) -> (Accept, Slab) { let mut sockets = Slab::new(); diff --git a/actix-server/src/builder.rs b/actix-server/src/builder.rs index fdb02205..6019ff16 100644 --- a/actix-server/src/builder.rs +++ b/actix-server/src/builder.rs @@ -19,7 +19,10 @@ 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::{ServerWorker, ServerWorkerConfig, WorkerAvailability, WorkerHandle}; +use crate::worker::{ + ServerWorker, ServerWorkerConfig, WorkerAvailability, WorkerHandleAccept, + WorkerHandleServer, +}; use crate::{join_all, Token}; /// Server builder @@ -27,7 +30,7 @@ pub struct ServerBuilder { threads: usize, token: Token, backlog: u32, - handles: Vec<(usize, WorkerHandle)>, + handles: Vec<(usize, WorkerHandleServer)>, services: Vec>, sockets: Vec<(Token, String, MioListener)>, accept: AcceptLoop, @@ -280,10 +283,11 @@ impl ServerBuilder { // start workers let handles = (0..self.threads) .map(|idx| { - let handle = self.start_worker(idx, self.accept.waker_owned()); - self.handles.push((idx, handle.clone())); + let (handle_accept, handle_server) = + self.start_worker(idx, self.accept.waker_owned()); + self.handles.push((idx, handle_server)); - handle + handle_accept }) .collect(); @@ -311,7 +315,11 @@ impl ServerBuilder { } } - fn start_worker(&self, idx: usize, waker: WakerQueue) -> WorkerHandle { + fn start_worker( + &self, + idx: usize, + waker: WakerQueue, + ) -> (WorkerHandleAccept, WorkerHandleServer) { let avail = WorkerAvailability::new(waker); let services = self.services.iter().map(|v| v.clone_factory()).collect(); @@ -437,9 +445,10 @@ impl ServerBuilder { break; } - let handle = self.start_worker(new_idx, self.accept.waker_owned()); - self.handles.push((new_idx, handle.clone())); - self.accept.wake(WakerInterest::Worker(handle)); + let (handle_accept, handle_server) = + self.start_worker(new_idx, self.accept.waker_owned()); + self.handles.push((new_idx, handle_server)); + self.accept.wake(WakerInterest::Worker(handle_accept)); } } } diff --git a/actix-server/src/waker_queue.rs b/actix-server/src/waker_queue.rs index e38a9782..8aa493aa 100644 --- a/actix-server/src/waker_queue.rs +++ b/actix-server/src/waker_queue.rs @@ -6,7 +6,7 @@ use std::{ use mio::{Registry, Token as MioToken, Waker}; -use crate::worker::WorkerHandle; +use crate::worker::WorkerHandleAccept; /// Waker token for `mio::Poll` instance. pub(crate) const WAKER_TOKEN: MioToken = MioToken(usize::MAX); @@ -84,6 +84,6 @@ pub(crate) enum WakerInterest { 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), + /// `WorkerHandleAccept`. + Worker(WorkerHandleAccept), } diff --git a/actix-server/src/worker.rs b/actix-server/src/worker.rs index c3074646..8e122623 100644 --- a/actix-server/src/worker.rs +++ b/actix-server/src/worker.rs @@ -28,11 +28,9 @@ use crate::socket::MioStream; use crate::waker_queue::{WakerInterest, WakerQueue}; use crate::{join_all, Token}; -pub(crate) struct WorkerCommand(Conn); - -/// Stop worker message. Returns `true` on successful shutdown -/// and `false` if some connections still alive. -pub(crate) struct StopCommand { +/// Stop worker message. Returns `true` on successful graceful shutdown. +/// and `false` if some connections still alive when shutdown execute. +pub(crate) struct Stop { graceful: bool, tx: oneshot::Sender, } @@ -43,42 +41,55 @@ pub(crate) struct Conn { pub token: Token, } -// a handle to worker that can send message to worker and share the availability of worker to other -// thread. -#[derive(Clone)] -pub(crate) struct WorkerHandle { +fn handle_pair( + idx: usize, + tx1: UnboundedSender, + tx2: UnboundedSender, + avail: WorkerAvailability, +) -> (WorkerHandleAccept, WorkerHandleServer) { + let accept = WorkerHandleAccept { + idx, + tx: tx1, + avail, + }; + + let server = WorkerHandleServer { idx, tx: tx2 }; + + (accept, server) +} + +/// Handle to worker that can send connection message to worker and share the +/// availability of worker to other thread. +/// +/// Held by [Accept](crate::accept::Accept). +pub(crate) struct WorkerHandleAccept { pub idx: usize, - tx1: UnboundedSender, - tx2: UnboundedSender, + tx: UnboundedSender, avail: WorkerAvailability, } -impl WorkerHandle { - pub fn new( - idx: usize, - tx1: UnboundedSender, - tx2: UnboundedSender, - avail: WorkerAvailability, - ) -> Self { - WorkerHandle { - idx, - tx1, - tx2, - avail, - } +impl WorkerHandleAccept { + pub(crate) fn send(&self, msg: Conn) -> Result<(), Conn> { + self.tx.send(msg).map_err(|msg| msg.0) } - pub fn send(&self, msg: Conn) -> Result<(), Conn> { - self.tx1.send(WorkerCommand(msg)).map_err(|msg| msg.0 .0) - } - - pub fn available(&self) -> bool { + pub(crate) fn available(&self) -> bool { self.avail.available() } +} - pub fn stop(&self, graceful: bool) -> oneshot::Receiver { +/// Handle to worker than can send stop message to worker. +/// +/// Held by [ServerBuilder](crate::builder::ServerBuilder). +pub(crate) struct WorkerHandleServer { + pub idx: usize, + tx: UnboundedSender, +} + +impl WorkerHandleServer { + pub(crate) fn stop(&self, graceful: bool) -> oneshot::Receiver { let (tx, rx) = oneshot::channel(); - let _ = self.tx2.send(StopCommand { graceful, tx }); + let _ = self.tx.send(Stop { graceful, tx }); rx } } @@ -114,8 +125,8 @@ impl WorkerAvailability { /// /// Worker accepts Socket objects via unbounded channel and starts stream processing. pub(crate) struct ServerWorker { - rx: UnboundedReceiver, - rx2: UnboundedReceiver, + rx: UnboundedReceiver, + rx2: UnboundedReceiver, services: Vec, availability: WorkerAvailability, conns: Counter, @@ -187,7 +198,7 @@ impl ServerWorker { factories: Vec>, availability: WorkerAvailability, config: ServerWorkerConfig, - ) -> WorkerHandle { + ) -> (WorkerHandleAccept, WorkerHandleServer) { let (tx1, rx) = unbounded_channel(); let (tx2, rx2) = unbounded_channel(); let avail = availability.clone(); @@ -254,7 +265,7 @@ impl ServerWorker { }); }); - WorkerHandle::new(idx, tx1, tx2, avail) + handle_pair(idx, tx1, tx2, avail) } fn restart_service(&mut self, token: Token, factory_id: usize) { @@ -360,8 +371,7 @@ impl Future for ServerWorker { let this = self.as_mut().get_mut(); // `StopWorker` message handler - if let Poll::Ready(Some(StopCommand { graceful, tx })) = - Pin::new(&mut this.rx2).poll_recv(cx) + if let Poll::Ready(Some(Stop { graceful, tx })) = Pin::new(&mut this.rx2).poll_recv(cx) { this.availability.set(false); let num = this.conns.total(); @@ -472,7 +482,7 @@ impl Future for ServerWorker { match ready!(Pin::new(&mut this.rx).poll_recv(cx)) { // handle incoming io stream - Some(WorkerCommand(msg)) => { + Some(msg) => { let guard = this.conns.get(); let _ = this.services[msg.token.0].service.call((guard, msg.io)); } From ddce2d6d12bb98a0fbf585a242089fa416fb78b3 Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Sat, 10 Apr 2021 08:05:50 -0700 Subject: [PATCH 53/72] Reduce cfg flags in actix_server::socket (#325) --- actix-server/src/socket.rs | 100 +++++++++++++++++++------------------ 1 file changed, 51 insertions(+), 49 deletions(-) diff --git a/actix-server/src/socket.rs b/actix-server/src/socket.rs index 0625cfda..948b5f1f 100644 --- a/actix-server/src/socket.rs +++ b/actix-server/src/socket.rs @@ -12,18 +12,7 @@ pub(crate) use { use std::{fmt, io}; use actix_rt::net::TcpStream; -use mio::event::Source; -use mio::net::TcpStream as MioTcpStream; -use mio::{Interest, Registry, Token}; - -#[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}, -}; +use mio::{event::Source, Interest, Registry, Token}; pub(crate) enum MioListener { Tcp(MioTcpListener), @@ -131,7 +120,7 @@ impl fmt::Display for MioListener { pub(crate) enum SocketAddr { Tcp(StdSocketAddr), #[cfg(unix)] - Uds(MioSocketAddr), + Uds(mio::net::SocketAddr), } impl fmt::Display for SocketAddr { @@ -156,9 +145,9 @@ impl fmt::Debug for SocketAddr { #[derive(Debug)] pub enum MioStream { - Tcp(MioTcpStream), + Tcp(mio::net::TcpStream), #[cfg(unix)] - Uds(MioUnixStream), + Uds(mio::net::UnixStream), } /// helper trait for converting mio stream to tokio stream. @@ -166,47 +155,60 @@ 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"); - } - } - } -} - -// 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 { - 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) }) +mod win_impl { + use super::*; + + use std::os::windows::io::{FromRawSocket, IntoRawSocket}; + + // FIXME: This is a workaround and we need an efficient way to convert between mio and tokio stream + impl FromStream for TcpStream { + fn from_mio(sock: MioStream) -> io::Result { + match sock { + 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) }) +mod unix_impl { + use super::*; + + use std::os::unix::io::{FromRawFd, IntoRawFd}; + + use actix_rt::net::UnixStream; + + // FIXME: This is a workaround and we need an efficient way to convert between mio and tokio stream + 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"); + } + } + } + } + + // FIXME: This is a workaround and we need an efficient way to convert between mio and tokio stream + 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) }) + } } } } From e0fb67f646e1a0534ea8d3a6b7536feb6ea1e38e Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Mon, 12 Apr 2021 17:12:59 -0700 Subject: [PATCH 54/72] Reduce ServerWorker size (#321) --- actix-server/src/worker.rs | 72 ++++++++++++++++++++------------------ 1 file changed, 38 insertions(+), 34 deletions(-) diff --git a/actix-server/src/worker.rs b/actix-server/src/worker.rs index 8e122623..cd7491d7 100644 --- a/actix-server/src/worker.rs +++ b/actix-server/src/worker.rs @@ -127,10 +127,10 @@ impl WorkerAvailability { pub(crate) struct ServerWorker { rx: UnboundedReceiver, rx2: UnboundedReceiver, - services: Vec, + services: Box<[WorkerService]>, availability: WorkerAvailability, conns: Counter, - factories: Vec>, + factories: Box<[Box]>, state: WorkerState, shutdown_timeout: Duration, } @@ -199,6 +199,8 @@ impl ServerWorker { availability: WorkerAvailability, config: ServerWorkerConfig, ) -> (WorkerHandleAccept, WorkerHandleServer) { + assert!(!availability.available()); + let (tx1, rx) = unbounded_channel(); let (tx2, rx2) = unbounded_channel(); let avail = availability.clone(); @@ -213,20 +215,7 @@ impl ServerWorker { .unwrap() }) .spawn(async move { - availability.set(false); - let mut wrk = ServerWorker { - rx, - rx2, - services: Vec::new(), - availability, - conns: Counter::new(config.max_concurrent_connections), - factories, - state: Default::default(), - shutdown_timeout: config.shutdown_timeout, - }; - - let fut = wrk - .factories + let fut = factories .iter() .enumerate() .map(|(idx, factory)| { @@ -239,29 +228,44 @@ 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. + // a second spawn to run !Send future tasks. 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, - }); - } - } - } + let res = join_all(fut) + .await + .into_iter() + .collect::, _>>(); + let services = match res { + Ok(res) => res + .into_iter() + .flatten() + .fold(Vec::new(), |mut services, (factory, token, service)| { + assert_eq!(token.0, services.len()); + services.push(WorkerService { + factory, + service, + status: WorkerServiceStatus::Unavailable, + }); + services + }) + .into_boxed_slice(), Err(e) => { error!("Can not start worker: {:?}", e); Arbiter::current().stop(); + return; } - } - wrk.await + }; + + // a third spawn to make sure ServerWorker runs as non boxed future. + spawn(ServerWorker { + rx, + rx2, + services, + availability, + conns: Counter::new(config.max_concurrent_connections), + factories: factories.into_boxed_slice(), + state: Default::default(), + shutdown_timeout: config.shutdown_timeout, + }); }); }); From d49ecf72036a42da406a8fcf1009b78bf42415ee Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Wed, 14 Apr 2021 06:48:05 -0700 Subject: [PATCH 55/72] Fix bug where backpressure happen too early (#332) --- actix-server/src/accept.rs | 193 +++++++++++++++++++++++++++++++------ 1 file changed, 165 insertions(+), 28 deletions(-) diff --git a/actix-server/src/accept.rs b/actix-server/src/accept.rs index 5b9f99c7..026bcc37 100644 --- a/actix-server/src/accept.rs +++ b/actix-server/src/accept.rs @@ -83,9 +83,59 @@ struct Accept { handles: Vec, srv: Server, next: usize, + avail: Availability, backpressure: bool, } +/// Array of u128 with every bit as marker for a worker handle's availability. +struct Availability([u128; 4]); + +impl Default for Availability { + fn default() -> Self { + Self([0; 4]) + } +} + +impl Availability { + /// Check if any worker handle is available + fn available(&self) -> bool { + self.0.iter().any(|a| *a != 0) + } + + /// Set worker handle available state by index. + fn set_available(&mut self, idx: usize, avail: bool) { + let (offset, idx) = if idx < 128 { + (0, idx) + } else if idx < 128 * 2 { + (1, idx - 128) + } else if idx < 128 * 3 { + (2, idx - 128 * 2) + } else if idx < 128 * 4 { + (3, idx - 128 * 3) + } else { + panic!("Max WorkerHandle count is 512") + }; + + if avail { + self.0[offset] |= 1 << idx as u128; + } else { + let shift = 1 << idx as u128; + + debug_assert_ne!(self.0[offset] & shift, 0); + + self.0[offset] ^= shift; + } + } + + /// Set all worker handle to available state. + /// This would result in a re-check on all workers' availability. + fn set_available_all(&mut self, handles: &[WorkerHandleAccept]) { + handles.iter().for_each(|handle| { + self.set_available(handle.idx, true); + }) + } +} + /// 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. @@ -116,6 +166,7 @@ impl Accept { System::set_current(sys); let (mut accept, sockets) = Accept::new_with_sockets(poll, waker, socks, handles, srv); + accept.poll_with(sockets); }) .unwrap(); @@ -148,12 +199,18 @@ impl Accept { }); } + let mut avail = Availability::default(); + + // Assume all handles are avail at construct time. + avail.set_available_all(&handles); + let accept = Accept { poll, waker, handles, srv, next: 0, + avail, backpressure: false, }; @@ -166,12 +223,8 @@ impl Accept { loop { if let Err(e) = self.poll.poll(&mut events, None) { match e.kind() { - std::io::ErrorKind::Interrupted => { - continue; - } - _ => { - panic!("Poll error: {}", e); - } + std::io::ErrorKind::Interrupted => continue, + _ => panic!("Poll error: {}", e), } } @@ -190,6 +243,8 @@ impl Accept { // from backpressure. Some(WakerInterest::WorkerAvailable) => { drop(guard); + // Assume all worker are avail as no worker index returned. + self.avail.set_available_all(&self.handles); self.maybe_backpressure(&mut sockets, false); } // a new worker thread is made and it's handle would be added to Accept @@ -197,6 +252,7 @@ impl Accept { drop(guard); // maybe we want to recover from a backpressure. self.maybe_backpressure(&mut sockets, false); + self.avail.set_available(handle.idx, true); self.handles.push(handle); } // got timer interest and it's time to try register socket(s) again @@ -342,27 +398,25 @@ impl Accept { if self.backpressure { // send_connection would remove fault worker from handles. // worst case here is conn get dropped after all handles are gone. - while !self.handles.is_empty() { - match self.send_connection(sockets, conn) { - Ok(_) => return, - Err(c) => conn = c, - } + while let Err(c) = self.send_connection(sockets, conn) { + conn = c } } else { - // Do one round and try to send conn to all workers until it succeed. - // Start from self.next. - let mut idx = 0; - while idx < self.handles.len() { - idx += 1; - if self.handles[self.next].available() { + while self.avail.available() { + let next = self.next(); + let idx = next.idx; + if next.available() { + self.avail.set_available(idx, true); match self.send_connection(sockets, conn) { Ok(_) => return, Err(c) => conn = c, } } else { + self.avail.set_available(idx, false); self.set_next(); } } + // Sending Conn failed due to either all workers are in error or not available. // Enter backpressure state and try again. self.maybe_backpressure(sockets, true); @@ -370,28 +424,22 @@ impl Accept { } } - // Set next worker handle that would accept work. - fn set_next(&mut self) { - self.next = (self.next + 1) % self.handles.len(); - } - // Send connection to worker and handle error. fn send_connection( &mut self, sockets: &mut Slab, conn: Conn, ) -> Result<(), Conn> { - match self.handles[self.next].send(conn) { + match self.next().send(conn) { Ok(_) => { self.set_next(); Ok(()) } Err(conn) => { - // 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. - self.srv.worker_faulted(self.handles[self.next].idx); - self.handles.swap_remove(self.next); + // Worker thread is error and could be gone. + // Remove worker handle and notify `ServerBuilder`. + self.remove_next(); + if self.handles.is_empty() { error!("No workers"); self.maybe_backpressure(sockets, true); @@ -401,6 +449,7 @@ impl Accept { } else if self.handles.len() <= self.next { self.next = 0; } + Err(conn) } } @@ -445,4 +494,92 @@ impl Accept { }; } } + + fn next(&self) -> &WorkerHandleAccept { + &self.handles[self.next] + } + + /// Set next worker handle that would accept connection. + fn set_next(&mut self) { + self.next = (self.next + 1) % self.handles.len(); + } + + /// Remove next worker handle that fail to accept connection. + fn remove_next(&mut self) { + let handle = self.handles.swap_remove(self.next); + let idx = handle.idx; + // A message is sent to `ServerBuilder` future to notify it a new worker + // should be made. + self.srv.worker_faulted(idx); + self.avail.set_available(idx, false); + } +} + +#[cfg(test)] +mod test { + use super::Availability; + + fn single(aval: &mut Availability, idx: usize) { + aval.set_available(idx, true); + assert!(aval.available()); + + aval.set_available(idx, true); + + aval.set_available(idx, false); + assert!(!aval.available()); + } + + fn multi(aval: &mut Availability, mut idx: Vec) { + idx.iter().for_each(|idx| aval.set_available(*idx, true)); + + assert!(aval.available()); + + while let Some(idx) = idx.pop() { + assert!(aval.available()); + aval.set_available(idx, false); + } + + assert!(!aval.available()); + } + + #[test] + fn availability() { + let mut aval = Availability::default(); + + single(&mut aval, 1); + single(&mut aval, 128); + single(&mut aval, 256); + single(&mut aval, 511); + + let idx = (0..511).filter(|i| i % 3 == 0 && i % 5 == 0).collect(); + + multi(&mut aval, idx); + + multi(&mut aval, (0..511).collect()) + } + + #[test] + #[should_panic] + fn overflow() { + let mut aval = Availability::default(); + single(&mut aval, 512); + } + + #[test] + #[should_panic] + fn double_set_unavailable() { + let mut aval = Availability::default(); + aval.set_available(233, false); + } + + #[test] + fn pin_point() { + let mut aval = Availability::default(); + + aval.set_available(438, true); + + aval.set_available(479, true); + + assert_eq!(aval.0[3], 1 << (438 - 384) | 1 << (479 - 384)); + } } From 3c1f57706ace5ea222ab7414bdebe67a862c9229 Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Thu, 15 Apr 2021 05:31:03 -0700 Subject: [PATCH 56/72] Make ServerWorker drop stop Arbiter it runs on (#334) --- actix-server/src/worker.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/actix-server/src/worker.rs b/actix-server/src/worker.rs index cd7491d7..801467f8 100644 --- a/actix-server/src/worker.rs +++ b/actix-server/src/worker.rs @@ -368,6 +368,12 @@ impl Default for WorkerState { } } +impl Drop for ServerWorker { + fn drop(&mut self) { + Arbiter::current().stop(); + } +} + impl Future for ServerWorker { type Output = (); @@ -451,14 +457,12 @@ impl Future for ServerWorker { if let WorkerState::Shutdown(shutdown) = mem::take(&mut this.state) { let _ = shutdown.tx.send(true); } - Arbiter::current().stop(); Poll::Ready(()) } else if shutdown.start_from.elapsed() >= this.shutdown_timeout { // Timeout forceful shutdown. if let WorkerState::Shutdown(shutdown) = mem::take(&mut this.state) { let _ = shutdown.tx.send(false); } - Arbiter::current().stop(); Poll::Ready(()) } else { // Reset timer and wait for 1 second. From 8e98d9168c8db1201e4fe8d4f04c15dac4febeda Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Thu, 15 Apr 2021 10:49:43 -0700 Subject: [PATCH 57/72] add test for restart worker thread (#328) --- actix-server/src/worker.rs | 3 + actix-server/tests/test_server.rs | 170 +++++++++++++++++++++++++++--- 2 files changed, 157 insertions(+), 16 deletions(-) diff --git a/actix-server/src/worker.rs b/actix-server/src/worker.rs index 801467f8..65951345 100644 --- a/actix-server/src/worker.rs +++ b/actix-server/src/worker.rs @@ -125,6 +125,8 @@ impl WorkerAvailability { /// /// Worker accepts Socket objects via unbounded channel and starts stream processing. pub(crate) struct ServerWorker { + // UnboundedReceiver should always be the first field. + // It must be dropped as soon as ServerWorker dropping. rx: UnboundedReceiver, rx2: UnboundedReceiver, services: Box<[WorkerService]>, @@ -370,6 +372,7 @@ impl Default for WorkerState { impl Drop for ServerWorker { fn drop(&mut self) { + // Stop the Arbiter ServerWorker runs on on drop. Arbiter::current().stop(); } } diff --git a/actix-server/tests/test_server.rs b/actix-server/tests/test_server.rs index 40b07e1c..3af072bb 100644 --- a/actix-server/tests/test_server.rs +++ b/actix-server/tests/test_server.rs @@ -1,7 +1,8 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{mpsc, Arc}; -use std::{net, thread, time}; +use std::{net, thread, time::Duration}; +use actix_rt::{net::TcpStream, time::sleep}; use actix_server::Server; use actix_service::fn_service; use actix_utils::future::ok; @@ -37,7 +38,7 @@ fn test_bind() { }); let (_, sys) = rx.recv().unwrap(); - thread::sleep(time::Duration::from_millis(500)); + thread::sleep(Duration::from_millis(500)); assert!(net::TcpStream::connect(addr).is_ok()); sys.stop(); let _ = h.join(); @@ -64,7 +65,7 @@ fn test_listen() { }); let sys = rx.recv().unwrap(); - thread::sleep(time::Duration::from_millis(500)); + thread::sleep(Duration::from_millis(500)); assert!(net::TcpStream::connect(addr).is_ok()); sys.stop(); let _ = h.join(); @@ -73,11 +74,11 @@ fn test_listen() { #[test] #[cfg(unix)] fn test_start() { + use std::io::Read; + use actix_codec::{BytesCodec, Framed}; - use actix_rt::net::TcpStream; use bytes::Bytes; use futures_util::sink::SinkExt; - use std::io::Read; let addr = unused_addr(); let (tx, rx) = mpsc::channel(); @@ -112,16 +113,16 @@ fn test_start() { // pause let _ = srv.pause(); - thread::sleep(time::Duration::from_millis(200)); + thread::sleep(Duration::from_millis(200)); let mut conn = net::TcpStream::connect(addr).unwrap(); - conn.set_read_timeout(Some(time::Duration::from_millis(100))) + conn.set_read_timeout(Some(Duration::from_millis(100))) .unwrap(); let res = conn.read_exact(&mut buf); assert!(res.is_err()); // resume let _ = srv.resume(); - thread::sleep(time::Duration::from_millis(100)); + thread::sleep(Duration::from_millis(100)); assert!(net::TcpStream::connect(addr).is_ok()); assert!(net::TcpStream::connect(addr).is_ok()); assert!(net::TcpStream::connect(addr).is_ok()); @@ -133,10 +134,10 @@ fn test_start() { // stop let _ = srv.stop(false); - thread::sleep(time::Duration::from_millis(100)); + thread::sleep(Duration::from_millis(100)); assert!(net::TcpStream::connect(addr).is_err()); - thread::sleep(time::Duration::from_millis(100)); + thread::sleep(Duration::from_millis(100)); sys.stop(); let _ = h.join(); } @@ -182,7 +183,7 @@ fn test_configure() { let _ = sys.run(); }); let (_, sys) = rx.recv().unwrap(); - thread::sleep(time::Duration::from_millis(500)); + thread::sleep(Duration::from_millis(500)); assert!(net::TcpStream::connect(addr1).is_ok()); assert!(net::TcpStream::connect(addr2).is_ok()); @@ -200,7 +201,6 @@ async fn test_max_concurrent_connections() { // The limit test on the other hand is only for concurrent tcp stream limiting a work // thread accept. - use actix_rt::net::TcpStream; use tokio::io::AsyncWriteExt; let addr = unused_addr(); @@ -226,7 +226,7 @@ async fn test_max_concurrent_connections() { let counter = counter.clone(); async move { counter.fetch_add(1, Ordering::SeqCst); - actix_rt::time::sleep(time::Duration::from_secs(20)).await; + sleep(Duration::from_secs(20)).await; counter.fetch_sub(1, Ordering::SeqCst); Ok::<(), ()>(()) } @@ -249,7 +249,7 @@ async fn test_max_concurrent_connections() { conns.push(conn); } - actix_rt::time::sleep(time::Duration::from_secs(5)).await; + sleep(Duration::from_secs(5)).await; // counter would remain at 3 even with 12 successful connection. // and 9 of them remain in backlog. @@ -268,9 +268,7 @@ async fn test_max_concurrent_connections() { #[actix_rt::test] async fn test_service_restart() { use std::task::{Context, Poll}; - use std::time::Duration; - use actix_rt::{net::TcpStream, time::sleep}; use actix_service::{fn_factory, Service}; use futures_core::future::LocalBoxFuture; use tokio::io::AsyncWriteExt; @@ -438,3 +436,143 @@ async fn test_service_restart() { let _ = server.stop(false); let _ = h.join().unwrap(); } + +#[actix_rt::test] +async fn worker_restart() { + use actix_service::{Service, ServiceFactory}; + use futures_core::future::LocalBoxFuture; + use tokio::io::{AsyncReadExt, AsyncWriteExt}; + + struct TestServiceFactory(Arc); + + impl ServiceFactory for TestServiceFactory { + type Response = (); + type Error = (); + type Config = (); + type Service = TestService; + type InitError = (); + type Future = LocalBoxFuture<'static, Result>; + + fn new_service(&self, _: Self::Config) -> Self::Future { + let counter = self.0.fetch_add(1, Ordering::Relaxed); + + Box::pin(async move { Ok(TestService(counter)) }) + } + } + + struct TestService(usize); + + impl Service for TestService { + type Response = (); + type Error = (); + type Future = LocalBoxFuture<'static, Result>; + + actix_service::always_ready!(); + + fn call(&self, stream: TcpStream) -> Self::Future { + let counter = self.0; + + let mut stream = stream.into_std().unwrap(); + use std::io::Write; + let str = counter.to_string(); + let buf = str.as_bytes(); + + let mut written = 0; + + while written < buf.len() { + if let Ok(n) = stream.write(&buf[written..]) { + written += n; + } + } + stream.flush().unwrap(); + stream.shutdown(net::Shutdown::Write).unwrap(); + + // force worker 2 to restart service once. + if counter == 2 { + panic!("panic on purpose") + } else { + Box::pin(async { Ok(()) }) + } + } + } + + let addr = unused_addr(); + let (tx, rx) = mpsc::channel(); + + let counter = Arc::new(AtomicUsize::new(1)); + let h = thread::spawn(move || { + let counter = counter.clone(); + actix_rt::System::new().block_on(async { + let server = Server::build() + .disable_signals() + .bind("addr", addr, move || TestServiceFactory(counter.clone())) + .unwrap() + .workers(2) + .run(); + + let _ = tx.send((server.clone(), actix_rt::System::current())); + server.await + }) + }); + + let (server, sys) = rx.recv().unwrap(); + + sleep(Duration::from_secs(3)).await; + + let mut buf = [0; 8]; + + // worker 1 would not restart and return it's id consistently. + let mut stream = TcpStream::connect(addr).await.unwrap(); + let n = stream.read(&mut buf).await.unwrap(); + let id = String::from_utf8_lossy(&buf[0..n]); + assert_eq!("1", id); + stream.shutdown().await.unwrap(); + + // worker 2 dead after return response. + let mut stream = TcpStream::connect(addr).await.unwrap(); + let n = stream.read(&mut buf).await.unwrap(); + let id = String::from_utf8_lossy(&buf[0..n]); + assert_eq!("2", id); + stream.shutdown().await.unwrap(); + + // request to worker 1 + let mut stream = TcpStream::connect(addr).await.unwrap(); + let n = stream.read(&mut buf).await.unwrap(); + let id = String::from_utf8_lossy(&buf[0..n]); + assert_eq!("1", id); + stream.shutdown().await.unwrap(); + + // TODO: Remove sleep if it can pass CI. + sleep(Duration::from_secs(3)).await; + + // worker 2 restarting and work goes to worker 1. + let mut stream = TcpStream::connect(addr).await.unwrap(); + let n = stream.read(&mut buf).await.unwrap(); + let id = String::from_utf8_lossy(&buf[0..n]); + assert_eq!("1", id); + stream.shutdown().await.unwrap(); + + // TODO: Remove sleep if it can pass CI. + sleep(Duration::from_secs(3)).await; + + // worker 2 restarted but worker 1 was still the next to accept connection. + let mut stream = TcpStream::connect(addr).await.unwrap(); + let n = stream.read(&mut buf).await.unwrap(); + let id = String::from_utf8_lossy(&buf[0..n]); + assert_eq!("1", id); + stream.shutdown().await.unwrap(); + + // TODO: Remove sleep if it can pass CI. + sleep(Duration::from_secs(3)).await; + + // worker 2 accept connection again but it's id is 3. + let mut stream = TcpStream::connect(addr).await.unwrap(); + let n = stream.read(&mut buf).await.unwrap(); + let id = String::from_utf8_lossy(&buf[0..n]); + assert_eq!("3", id); + stream.shutdown().await.unwrap(); + + sys.stop(); + let _ = server.stop(false); + let _ = h.join().unwrap(); +} From ef206f40fb95ccb1d1d5f34a8f0e2950c0b8ff4e Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Thu, 15 Apr 2021 20:13:27 +0100 Subject: [PATCH 58/72] update ignored service docs to new traits --- actix-service/src/lib.rs | 18 +++++--- actix-service/src/transform.rs | 75 ++++++++++++++++++++++++++-------- 2 files changed, 72 insertions(+), 21 deletions(-) diff --git a/actix-service/src/lib.rs b/actix-service/src/lib.rs index cc82bfa6..3db2bcc5 100644 --- a/actix-service/src/lib.rs +++ b/actix-service/src/lib.rs @@ -53,8 +53,14 @@ use self::ready::{err, ok, ready, Ready}; /// async fn(Request) -> Result /// ``` /// -/// The `Service` trait just generalizes this form where each parameter is described as an -/// associated type on the trait. Services can also have mutable state that influence computation. +/// The `Service` trait just generalizes this form. Requests are defined as a generic type parameter +/// and responses and other details are defined as associated types on the trait impl. Notice that +/// this design means that services can receive many request types and converge them to a single +/// response type. +/// +/// Services can also have mutable state that influence computation by using a `Cell`, `RefCell` +/// or `Mutex`. Services intentionally do not take `&mut self` to reduce over-head in the +/// common cases. /// /// `Service` provides a symmetric and uniform API; the same abstractions can be used to represent /// both clients and servers. Services describe only _transformation_ operations which encourage @@ -64,8 +70,7 @@ use self::ready::{err, ok, ready, Ready}; /// ```ignore /// struct MyService; /// -/// impl Service for MyService { -/// type Request = u8; +/// impl Service for MyService { /// type Response = u64; /// type Error = MyError; /// type Future = Pin>>>; @@ -81,6 +86,9 @@ use self::ready::{err, ok, ready, Ready}; /// /// ```ignore /// async fn my_service(req: u8) -> Result; +/// +/// let svc = fn_service(my_service) +/// svc.call(123) /// ``` pub trait Service { /// Responses given by the service. @@ -144,7 +152,7 @@ pub trait ServiceFactory { /// Errors potentially raised while building a service. type InitError; - /// The future of the `Service` instance. + /// The future of the `Service` instance.g type Future: Future>; /// Create and return a new service asynchronously. diff --git a/actix-service/src/transform.rs b/actix-service/src/transform.rs index b0abe72b..8fdff66f 100644 --- a/actix-service/src/transform.rs +++ b/actix-service/src/transform.rs @@ -27,7 +27,7 @@ where /// Transform(middleware) wraps inner service and runs during inbound and/or outbound processing in /// the request/response lifecycle. It may modify request and/or response. /// -/// For example, timeout transform: +/// For example, a timeout service wrapper: /// /// ```ignore /// pub struct Timeout { @@ -35,11 +35,7 @@ where /// timeout: Duration, /// } /// -/// impl Service for Timeout -/// where -/// S: Service, -/// { -/// type Request = S::Request; +/// impl, Req> Service for Timeout { /// type Response = S::Response; /// type Error = TimeoutError; /// type Future = TimeoutServiceResponse; @@ -55,26 +51,22 @@ where /// } /// ``` /// -/// Timeout service in above example is decoupled from underlying service implementation and could -/// be applied to any service. +/// This wrapper service is decoupled from the underlying service implementation and could be +/// applied to any service. /// -/// The `Transform` trait defines the interface of a Service factory. `Transform` is often +/// The `Transform` trait defines the interface of a service wrapper. `Transform` is often /// implemented for middleware, defining how to construct a middleware Service. A Service that is /// constructed by the factory takes the Service that follows it during execution as a parameter, /// assuming ownership of the next Service. /// -/// Factory for `Timeout` middleware from the above example could look like this: +/// A transform for the `Timeout` middleware could look like this: /// /// ```ignore /// pub struct TimeoutTransform { /// timeout: Duration, /// } /// -/// impl Transform for TimeoutTransform -/// where -/// S: Service, -/// { -/// type Request = S::Request; +/// impl, Req> Transform for TimeoutTransform { /// type Response = S::Response; /// type Error = TimeoutError; /// type InitError = S::Error; @@ -82,7 +74,7 @@ where /// type Future = Ready>; /// /// fn new_transform(&self, service: S) -> Self::Future { -/// ready(Ok(TimeoutService { +/// ready(Ok(Timeout { /// service, /// timeout: self.timeout, /// })) @@ -227,3 +219,54 @@ where } } } + +#[cfg(test)] +mod tests { + use core::{ + future::{ready, Ready}, + time::Duration, + }; + + use super::*; + use crate::Service; + + // pseudo-doctest for Transform trait + pub struct TimeoutTransform { + timeout: Duration, + } + + // pseudo-doctest for Transform trait + impl, Req> Transform for TimeoutTransform { + type Response = S::Response; + type Error = S::Error; + type InitError = S::Error; + type Transform = Timeout; + type Future = Ready>; + + fn new_transform(&self, service: S) -> Self::Future { + ready(Ok(Timeout { + service, + _timeout: self.timeout, + })) + } + } + + // pseudo-doctest for Transform trait + pub struct Timeout { + service: S, + _timeout: Duration, + } + + // pseudo-doctest for Transform trait + impl, Req> Service for Timeout { + type Response = S::Response; + type Error = S::Error; + type Future = S::Future; + + crate::forward_ready!(service); + + fn call(&self, req: Req) -> Self::Future { + self.service.call(req) + } + } +} From 4e6d88d1438c05d0ed90cce214a6970a1ed3e2c5 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Thu, 15 Apr 2021 20:43:02 +0100 Subject: [PATCH 59/72] improve boxed service docs --- actix-service/Cargo.toml | 2 ++ actix-service/src/boxed.rs | 34 ++++++++++++++++-------------- actix-service/src/lib.rs | 38 +++++++++++++++++----------------- actix-service/src/transform.rs | 7 +++---- 4 files changed, 43 insertions(+), 38 deletions(-) diff --git a/actix-service/Cargo.toml b/actix-service/Cargo.toml index 84a0c172..1c82f703 100644 --- a/actix-service/Cargo.toml +++ b/actix-service/Cargo.toml @@ -22,8 +22,10 @@ path = "src/lib.rs" [dependencies] futures-core = { version = "0.3.7", default-features = false } +paste = "1" pin-project-lite = "0.2" [dev-dependencies] actix-rt = "2.0.0" +actix-utils = "3.0.0-beta.4" futures-util = { version = "0.3.7", default-features = false } diff --git a/actix-service/src/boxed.rs b/actix-service/src/boxed.rs index a872ca9f..3141c5e4 100644 --- a/actix-service/src/boxed.rs +++ b/actix-service/src/boxed.rs @@ -3,26 +3,30 @@ use alloc::{boxed::Box, rc::Rc}; use core::{future::Future, pin::Pin}; +use paste::paste; + use crate::{Service, ServiceFactory}; -/// A boxed future without a Send bound or lifetime parameters. +/// A boxed future with no send bound or lifetime parameters. pub type BoxFuture = Pin>>; macro_rules! service_object { ($name: ident, $type: tt, $fn_name: ident) => { - /// Type alias for service trait object. - pub type $name = $type< - dyn Service>>, - >; + paste! { + #[doc = "Type alias for service trait object using `" $type "`."] + pub type $name = $type< + dyn Service>>, + >; - /// Create service trait object. - pub fn $fn_name(service: S) -> $name - where - S: Service + 'static, - Req: 'static, - S::Future: 'static, - { - $type::new(ServiceWrapper::new(service)) + #[doc = "Wraps service as a trait object using [`" $name "`]."] + pub fn $fn_name(service: S) -> $name + where + S: Service + 'static, + Req: 'static, + S::Future: 'static, + { + $type::new(ServiceWrapper::new(service)) + } } }; } @@ -56,10 +60,10 @@ where } } -/// Wrapper for a service factory trait object that will produce a boxed trait object service. +/// Wrapper for a service factory that will map it's services to boxed trait object services. pub struct BoxServiceFactory(Inner); -/// Create service factory trait object. +/// Wraps a service factory that returns service trait objects. pub fn factory( factory: SF, ) -> BoxServiceFactory diff --git a/actix-service/src/lib.rs b/actix-service/src/lib.rs index 3db2bcc5..e83323a3 100644 --- a/actix-service/src/lib.rs +++ b/actix-service/src/lib.rs @@ -73,7 +73,7 @@ use self::ready::{err, ok, ready, Ready}; /// impl Service for MyService { /// type Response = u64; /// type Error = MyError; -/// type Future = Pin>>>; +/// type Future = Pin>>>; /// /// fn poll_ready(&self, cx: &mut Context<'_>) -> Poll> { ... } /// @@ -82,7 +82,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()). +/// could be rewritten as a simple function and passed to [`fn_service`](fn_service()). /// /// ```ignore /// async fn my_service(req: u8) -> Result; @@ -102,13 +102,12 @@ pub trait Service { /// Returns `Ready` when the service is able to process requests. /// - /// If the service is at capacity, then `Pending` is returned and the task - /// is notified when the service becomes ready again. This function is - /// expected to be called while on a task. + /// If the service is at capacity, then `Pending` is returned and the task is notified when the + /// service becomes ready again. This function is expected to be called while on a task. /// - /// This is a **best effort** implementation. False positives are permitted. - /// It is permitted for the service to return `Ready` from a `poll_ready` - /// call and the next invocation of `call` results in an error. + /// This is a best effort implementation. False positives are permitted. It is permitted for + /// the service to return `Ready` from a `poll_ready` call and the next invocation of `call` + /// results in an error. /// /// # Notes /// 1. `poll_ready` might be called on a different task to `call`. @@ -117,25 +116,26 @@ pub trait Service { /// Process the request and return the response asynchronously. /// - /// This function is expected to be callable off task. As such, - /// implementations should take care to not call `poll_ready`. If the - /// service is at capacity and the request is unable to be handled, the - /// returned `Future` should resolve to an error. + /// This function is expected to be callable off-task. As such, implementations of `call` should + /// take care to not call `poll_ready`. If the service is at capacity and the request is unable + /// to be handled, the returned `Future` should resolve to an error. /// - /// Calling `call` without calling `poll_ready` is permitted. The - /// implementation must be resilient to this fact. + /// Invoking `call` without first invoking `poll_ready` is permitted. Implementations must be + /// resilient to this fact. fn call(&self, req: Req) -> Self::Future; } /// Factory for creating `Service`s. /// -/// Acts as a service factory. This is useful for cases where new `Service`s -/// must be produced. One case is a TCP server listener. The listener -/// accepts new TCP streams, obtains a new `Service` using the -/// `ServiceFactory` trait, and uses the new `Service` to process inbound -/// requests on that new TCP stream. +/// This is useful for cases where new `Service`s must be produced. One case is a TCP +/// server listener: a listener accepts new connections, constructs a new `Service` for each using +/// the `ServiceFactory` trait, and uses the new `Service` to process inbound requests on that new +/// connection. /// /// `Config` is a service factory configuration type. +/// +/// Simple factories may be able to use [`fn_factory`] or [`fn_factory_with_config`] to +/// reduce boilerplate. pub trait ServiceFactory { /// Responses given by the created services. type Response; diff --git a/actix-service/src/transform.rs b/actix-service/src/transform.rs index 8fdff66f..b561a1e5 100644 --- a/actix-service/src/transform.rs +++ b/actix-service/src/transform.rs @@ -222,10 +222,9 @@ where #[cfg(test)] mod tests { - use core::{ - future::{ready, Ready}, - time::Duration, - }; + use core::time::Duration; + + use actix_utils::future::{ready, Ready}; use super::*; use crate::Service; From 7a82288066ea434349d9850eef63e4fae17a56d0 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Thu, 15 Apr 2021 21:58:18 +0100 Subject: [PATCH 60/72] docs tweak --- actix-service/src/lib.rs | 3 ++- actix-service/src/macros.rs | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/actix-service/src/lib.rs b/actix-service/src/lib.rs index e83323a3..3ae22679 100644 --- a/actix-service/src/lib.rs +++ b/actix-service/src/lib.rs @@ -2,6 +2,7 @@ #![no_std] #![deny(rust_2018_idioms, nonstandard_style)] +#![warn(missing_docs)] #![allow(clippy::type_complexity)] #![doc(html_logo_url = "https://actix.rs/img/logo.png")] #![doc(html_favicon_url = "https://actix.rs/favicon.ico")] @@ -59,7 +60,7 @@ use self::ready::{err, ok, ready, Ready}; /// response type. /// /// Services can also have mutable state that influence computation by using a `Cell`, `RefCell` -/// or `Mutex`. Services intentionally do not take `&mut self` to reduce over-head in the +/// or `Mutex`. Services intentionally do not take `&mut self` to reduce overhead in the /// common cases. /// /// `Service` provides a symmetric and uniform API; the same abstractions can be used to represent diff --git a/actix-service/src/macros.rs b/actix-service/src/macros.rs index d2ae9dbf..e23b2960 100644 --- a/actix-service/src/macros.rs +++ b/actix-service/src/macros.rs @@ -1,6 +1,6 @@ -/// A boilerplate implementation of [`Service::poll_ready`] that always signals readiness. +/// An implementation of [`poll_ready`]() that always signals readiness. /// -/// [`Service::poll_ready`]: crate::Service::poll_ready +/// [`poll_ready`]: crate::Service::poll_ready /// /// # Examples /// ```no_run @@ -34,12 +34,12 @@ macro_rules! always_ready { }; } -/// A boilerplate implementation of [`Service::poll_ready`] that forwards readiness checks to a +/// An implementation of [`poll_ready`] that forwards readiness checks to a /// named struct field. /// /// Tuple structs are not supported. /// -/// [`Service::poll_ready`]: crate::Service::poll_ready +/// [`poll_ready`]: crate::Service::poll_ready /// /// # Examples /// ```no_run From 47fba25d67752a8196c70086abf5f2c0adff1eef Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Fri, 16 Apr 2021 00:00:02 +0100 Subject: [PATCH 61/72] remove pipeline from public api (#335) --- actix-server/examples/tcp-echo.rs | 16 ++++++------ actix-service/CHANGES.md | 3 +++ actix-service/src/and_then.rs | 16 ++++++------ actix-service/src/apply.rs | 6 ++++- actix-service/src/ext.rs | 42 +++++++++++++++++++++++++++++-- actix-service/src/lib.rs | 1 - actix-service/src/pipeline.rs | 11 +++++--- actix-service/src/then.rs | 6 ++++- actix-service/src/transform.rs | 7 +++--- actix-tls/examples/tcp-rustls.rs | 12 +++------ 10 files changed, 85 insertions(+), 35 deletions(-) diff --git a/actix-server/examples/tcp-echo.rs b/actix-server/examples/tcp-echo.rs index 45e473a9..8b038da4 100644 --- a/actix-server/examples/tcp-echo.rs +++ b/actix-server/examples/tcp-echo.rs @@ -9,15 +9,17 @@ //! Start typing. When you press enter the typed line will be echoed back. The server will log //! the length of each line it echos and the total size of data sent when the connection is closed. -use std::sync::{ - atomic::{AtomicUsize, Ordering}, - Arc, +use std::{ + env, io, + sync::{ + atomic::{AtomicUsize, Ordering}, + Arc, + }, }; -use std::{env, io}; use actix_rt::net::TcpStream; use actix_server::Server; -use actix_service::pipeline_factory; +use actix_service::{fn_service, ServiceFactoryExt as _}; use bytes::BytesMut; use futures_util::future::ok; use log::{error, info}; @@ -25,7 +27,7 @@ use tokio::io::{AsyncReadExt, AsyncWriteExt}; #[actix_rt::main] async fn main() -> io::Result<()> { - env::set_var("RUST_LOG", "actix=trace,basic=trace"); + env::set_var("RUST_LOG", "info"); env_logger::init(); let count = Arc::new(AtomicUsize::new(0)); @@ -41,7 +43,7 @@ async fn main() -> io::Result<()> { let count = Arc::clone(&count); let num2 = Arc::clone(&count); - pipeline_factory(move |mut stream: TcpStream| { + fn_service(move |mut stream: TcpStream| { let count = Arc::clone(&count); async move { diff --git a/actix-service/CHANGES.md b/actix-service/CHANGES.md index 51749ecd..c99cc2eb 100644 --- a/actix-service/CHANGES.md +++ b/actix-service/CHANGES.md @@ -1,6 +1,9 @@ # Changes ## Unreleased - 2021-xx-xx +* Removed pipeline and related structs/functions. [#335] + +[#335]: https://github.com/actix/actix-net/pull/335 ## 2.0.0-beta.5 - 2021-03-15 diff --git a/actix-service/src/and_then.rs b/actix-service/src/and_then.rs index e3b293ea..38980079 100644 --- a/actix-service/src/and_then.rs +++ b/actix-service/src/and_then.rs @@ -11,11 +11,11 @@ use pin_project_lite::pin_project; use super::{Service, ServiceFactory}; -/// Service for the `and_then` combinator, chaining a computation onto the end -/// of another service which completes successfully. +/// Service for the `and_then` combinator, chaining a computation onto the end of another service +/// which completes successfully. /// /// This is created by the `Pipeline::and_then` method. -pub(crate) struct AndThenService(Rc<(A, B)>, PhantomData); +pub struct AndThenService(Rc<(A, B)>, PhantomData); impl AndThenService { /// Create new `AndThen` combinator @@ -64,7 +64,7 @@ where } pin_project! { - pub(crate) struct AndThenServiceResponse + pub struct AndThenServiceResponse where A: Service, B: Service, @@ -117,7 +117,7 @@ where } /// `.and_then()` service factory combinator -pub(crate) struct AndThenServiceFactory +pub struct AndThenServiceFactory where A: ServiceFactory, A::Config: Clone, @@ -200,7 +200,7 @@ where } pin_project! { - pub(crate) struct AndThenServiceFactoryResponse + pub struct AndThenServiceFactoryResponse where A: ServiceFactory, B: ServiceFactory, @@ -272,7 +272,9 @@ mod tests { use futures_util::future::lazy; use crate::{ - fn_factory, ok, pipeline, pipeline_factory, ready, Ready, Service, ServiceFactory, + fn_factory, ok, + pipeline::{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 9a7e27d2..2f798fd0 100644 --- a/actix-service/src/apply.rs +++ b/actix-service/src/apply.rs @@ -214,7 +214,11 @@ mod tests { use futures_util::future::lazy; use super::*; - use crate::{ok, pipeline, pipeline_factory, Ready, Service, ServiceFactory}; + use crate::{ + ok, + pipeline::{pipeline, pipeline_factory}, + Ready, Service, ServiceFactory, + }; #[derive(Clone)] struct Srv; diff --git a/actix-service/src/ext.rs b/actix-service/src/ext.rs index d931596b..f5fe6ed1 100644 --- a/actix-service/src/ext.rs +++ b/actix-service/src/ext.rs @@ -1,8 +1,12 @@ use crate::{ - map::Map, map_err::MapErr, transform_err::TransformMapInitErr, Service, ServiceFactory, - Transform, + and_then::{AndThenService, AndThenServiceFactory}, + map::Map, + map_err::MapErr, + transform_err::TransformMapInitErr, + IntoService, IntoServiceFactory, Service, ServiceFactory, Transform, }; +/// An extension trait for [`Service`]s that provides a variety of convenient adapters. pub trait ServiceExt: Service { /// Map this service's output to a different type, returning a new service /// of the resulting type. @@ -36,10 +40,27 @@ pub trait ServiceExt: Service { { MapErr::new(self, f) } + + /// Call another service after call to this one has resolved successfully. + /// + /// This function can be used to chain two services together and ensure that the second service + /// isn't called until call to the fist service have finished. Result of the call to the first + /// service is used as an input parameter for the second service's call. + /// + /// Note that this function consumes the receiving service and returns a wrapped version of it. + fn and_then(self, service: I) -> AndThenService + where + Self: Sized, + I: IntoService, + S1: Service, + { + AndThenService::new(self, service.into_service()) + } } impl ServiceExt for S where S: Service {} +/// An extension trait for [`ServiceFactory`]s that provides a variety of convenient adapters. pub trait ServiceFactoryExt: ServiceFactory { /// Map this service's output to a different type, returning a new service /// of the resulting type. @@ -68,10 +89,27 @@ pub trait ServiceFactoryExt: ServiceFactory { { crate::map_init_err::MapInitErr::new(self, f) } + + /// Call another service after call to this one has resolved successfully. + fn and_then(self, factory: I) -> AndThenServiceFactory + where + Self: Sized, + Self::Config: Clone, + I: IntoServiceFactory, + SF1: ServiceFactory< + Self::Response, + Config = Self::Config, + Error = Self::Error, + InitError = Self::InitError, + >, + { + AndThenServiceFactory::new(self, factory.into_factory()) + } } impl ServiceFactoryExt for SF where SF: ServiceFactory {} +/// An extension trait for [`Transform`]s that provides a variety of convenient adapters. pub trait TransformExt: Transform { /// Return a new `Transform` whose init error is mapped to to a different type. fn map_init_err(self, f: F) -> TransformMapInitErr diff --git a/actix-service/src/lib.rs b/actix-service/src/lib.rs index 3ae22679..8f839121 100644 --- a/actix-service/src/lib.rs +++ b/actix-service/src/lib.rs @@ -38,7 +38,6 @@ pub use self::apply_cfg::{apply_cfg, apply_cfg_factory}; pub use self::ext::{ServiceExt, ServiceFactoryExt, TransformExt}; 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, ApplyTransform, Transform}; #[allow(unused_imports)] diff --git a/actix-service/src/pipeline.rs b/actix-service/src/pipeline.rs index 0ec43f0d..2c71a74b 100644 --- a/actix-service/src/pipeline.rs +++ b/actix-service/src/pipeline.rs @@ -1,3 +1,6 @@ +// TODO: see if pipeline is necessary +#![allow(dead_code)] + use core::{ marker::PhantomData, task::{Context, Poll}, @@ -11,7 +14,7 @@ use crate::then::{ThenService, ThenServiceFactory}; use crate::{IntoService, IntoServiceFactory, Service, ServiceFactory}; /// Construct new pipeline with one service in pipeline chain. -pub fn pipeline(service: I) -> Pipeline +pub(crate) fn pipeline(service: I) -> Pipeline where I: IntoService, S: Service, @@ -23,7 +26,7 @@ where } /// Construct new pipeline factory with one service factory. -pub fn pipeline_factory(factory: I) -> PipelineFactory +pub(crate) fn pipeline_factory(factory: I) -> PipelineFactory where I: IntoServiceFactory, SF: ServiceFactory, @@ -35,7 +38,7 @@ where } /// Pipeline service - pipeline allows to compose multiple service into one service. -pub struct Pipeline { +pub(crate) struct Pipeline { service: S, _phantom: PhantomData, } @@ -157,7 +160,7 @@ impl, Req> Service for Pipeline { } /// Pipeline factory -pub struct PipelineFactory { +pub(crate) struct PipelineFactory { factory: SF, _phantom: PhantomData, } diff --git a/actix-service/src/then.rs b/actix-service/src/then.rs index c9428824..82b9dc94 100644 --- a/actix-service/src/then.rs +++ b/actix-service/src/then.rs @@ -246,7 +246,11 @@ mod tests { use futures_util::future::lazy; - use crate::{err, ok, pipeline, pipeline_factory, ready, Ready, Service, ServiceFactory}; + use crate::{ + err, ok, + pipeline::{pipeline, pipeline_factory}, + ready, Ready, Service, ServiceFactory, + }; #[derive(Clone)] struct Srv1(Rc>); diff --git a/actix-service/src/transform.rs b/actix-service/src/transform.rs index b561a1e5..00b686f9 100644 --- a/actix-service/src/transform.rs +++ b/actix-service/src/transform.rs @@ -21,11 +21,10 @@ where ApplyTransform::new(t, factory.into_factory()) } -/// The `Transform` trait defines the interface of a service factory that wraps inner service -/// during construction. +/// Defines the interface of a service factory that wraps inner service during construction. /// -/// Transform(middleware) wraps inner service and runs during inbound and/or outbound processing in -/// the request/response lifecycle. It may modify request and/or response. +/// Transformers wrap an inner service and runs during inbound and/or outbound processing in the +/// service lifecycle. It may modify request and/or response. /// /// For example, a timeout service wrapper: /// diff --git a/actix-tls/examples/tcp-rustls.rs b/actix-tls/examples/tcp-rustls.rs index d0c20428..687c1f86 100644 --- a/actix-tls/examples/tcp-rustls.rs +++ b/actix-tls/examples/tcp-rustls.rs @@ -31,7 +31,7 @@ use std::{ use actix_rt::net::TcpStream; use actix_server::Server; -use actix_service::pipeline_factory; +use actix_service::ServiceFactoryExt as _; use actix_tls::accept::rustls::{Acceptor as RustlsAcceptor, TlsStream}; use futures_util::future::ok; use log::info; @@ -39,14 +39,9 @@ use rustls::{ internal::pemfile::certs, internal::pemfile::rsa_private_keys, NoClientAuth, ServerConfig, }; -#[derive(Debug)] -struct ServiceState { - num: Arc, -} - #[actix_rt::main] async fn main() -> io::Result<()> { - env::set_var("RUST_LOG", "actix=trace,basic=trace"); + env::set_var("RUST_LOG", "info"); env_logger::init(); let mut tls_config = ServerConfig::new(NoClientAuth::new()); @@ -73,7 +68,8 @@ async fn main() -> io::Result<()> { let count = Arc::clone(&count); // Set up TLS service factory - pipeline_factory(tls_acceptor.clone()) + tls_acceptor + .clone() .map_err(|err| println!("Rustls error: {:?}", err)) .and_then(move |stream: TlsStream| { let num = count.fetch_add(1, Ordering::Relaxed); From aeb81ad3fd19b3e976c4ce8a83527d9192ce29dc Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Thu, 15 Apr 2021 16:54:15 -0700 Subject: [PATCH 62/72] Fix worker are notified to stop with non_graceful shutdown (#333) --- actix-server/CHANGES.md | 4 ++++ actix-server/src/builder.rs | 46 ++++++++++++------------------------- 2 files changed, 19 insertions(+), 31 deletions(-) diff --git a/actix-server/CHANGES.md b/actix-server/CHANGES.md index 60d7ce37..7ccada0e 100644 --- a/actix-server/CHANGES.md +++ b/actix-server/CHANGES.md @@ -1,6 +1,10 @@ # Changes ## Unreleased - 2021-xx-xx +* Server shutdown would notify all workers to exit regardless if shutdown is graceful. + This would make all worker shutdown immediately in force shutdown case. [#333] + +[#333]: https://github.com/actix/actix-net/pull/333 ## 2.0.0-beta.4 - 2021-04-01 diff --git a/actix-server/src/builder.rs b/actix-server/src/builder.rs index 6019ff16..aa18bb22 100644 --- a/actix-server/src/builder.rs +++ b/actix-server/src/builder.rs @@ -381,45 +381,29 @@ impl ServerBuilder { let notify = std::mem::take(&mut self.notify); // stop workers - if !self.handles.is_empty() && graceful { - let iter = self - .handles - .iter() - .map(move |worker| worker.1.stop(graceful)) - .collect(); + let stop = self + .handles + .iter() + .map(move |worker| worker.1.stop(graceful)) + .collect(); - let fut = join_all(iter); - - rt::spawn(async move { - let _ = fut.await; - if let Some(tx) = completion { - let _ = tx.send(()); - } - for tx in notify { - let _ = tx.send(()); - } - if exit { - rt::spawn(async { - sleep(Duration::from_millis(300)).await; - System::current().stop(); - }); - } - }); - } else { - // we need to stop system if server was spawned - if self.exit { - rt::spawn(async { - sleep(Duration::from_millis(300)).await; - System::current().stop(); - }); + rt::spawn(async move { + if graceful { + let _ = join_all(stop).await; } + if let Some(tx) = completion { let _ = tx.send(()); } for tx in notify { let _ = tx.send(()); } - } + + if exit { + sleep(Duration::from_millis(300)).await; + System::current().stop(); + } + }); } ServerCommand::WorkerFaulted(idx) => { let mut found = false; From 7749dfe46a5af18122e02a7d64bf74699d080c5b Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Fri, 16 Apr 2021 02:06:11 +0100 Subject: [PATCH 63/72] address msrv todo in router --- actix-codec/src/lib.rs | 2 +- actix-router/src/resource.rs | 5 +---- actix-service/src/lib.rs | 2 +- actix-tls/src/connect/resolve.rs | 2 +- 4 files changed, 4 insertions(+), 7 deletions(-) diff --git a/actix-codec/src/lib.rs b/actix-codec/src/lib.rs index dec30ba6..c7713bfe 100644 --- a/actix-codec/src/lib.rs +++ b/actix-codec/src/lib.rs @@ -7,7 +7,7 @@ //! [`Sink`]: futures_sink::Sink //! [`Stream`]: futures_core::Stream -#![deny(rust_2018_idioms, nonstandard_style)] +#![deny(rust_2018_idioms, nonstandard_style, future_incompatible)] #![warn(missing_docs)] #![doc(html_logo_url = "https://actix.rs/img/logo.png")] #![doc(html_favicon_url = "https://actix.rs/favicon.ico")] diff --git a/actix-router/src/resource.rs b/actix-router/src/resource.rs index 98b4a709..32162c53 100644 --- a/actix-router/src/resource.rs +++ b/actix-router/src/resource.rs @@ -581,10 +581,7 @@ impl ResourceDef { mut for_prefix: bool, ) -> (String, Vec, bool, usize) { if pattern.find('{').is_none() { - // TODO: MSRV: 1.45 - #[allow(clippy::manual_strip)] - return if pattern.ends_with('*') { - let path = &pattern[..pattern.len() - 1]; + return if let Some(path) = pattern.strip_suffix('*') { let re = String::from("^") + path + "(.*)"; (re, vec![PatternElement::Str(String::from(path))], true, 0) } else { diff --git a/actix-service/src/lib.rs b/actix-service/src/lib.rs index 8f839121..e26d5c62 100644 --- a/actix-service/src/lib.rs +++ b/actix-service/src/lib.rs @@ -1,7 +1,7 @@ //! See [`Service`] docs for information on this crate's foundational trait. #![no_std] -#![deny(rust_2018_idioms, nonstandard_style)] +#![deny(rust_2018_idioms, nonstandard_style, future_incompatible)] #![warn(missing_docs)] #![allow(clippy::type_complexity)] #![doc(html_logo_url = "https://actix.rs/img/logo.png")] diff --git a/actix-tls/src/connect/resolve.rs b/actix-tls/src/connect/resolve.rs index 32e442bf..0a92b9b1 100755 --- a/actix-tls/src/connect/resolve.rs +++ b/actix-tls/src/connect/resolve.rs @@ -56,7 +56,7 @@ pub enum Resolver { /// An interface for custom async DNS resolvers. /// /// # Usage -/// ```rust +/// ``` /// use std::net::SocketAddr; /// /// use actix_tls::connect::{Resolve, Resolver}; From fdafc1dd655c43b126b2e0eb61761098240e9f44 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Fri, 16 Apr 2021 02:08:44 +0100 Subject: [PATCH 64/72] amend licences --- LICENSE-APACHE | 2 +- LICENSE-MIT | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/LICENSE-APACHE b/LICENSE-APACHE index 6cdf2d16..8f5ba39b 100644 --- a/LICENSE-APACHE +++ b/LICENSE-APACHE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2017-NOW Nikolay Kim + Copyright 2017-NOW Actix Team Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/LICENSE-MIT b/LICENSE-MIT index 0f80296a..d559b1cd 100644 --- a/LICENSE-MIT +++ b/LICENSE-MIT @@ -1,4 +1,4 @@ -Copyright (c) 2017 Nikolay Kim +Copyright (c) 2017-NOW Actix Team Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated From 20c2da17ed9679d7719d605ce5bc029c4a6fd713 Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Thu, 15 Apr 2021 19:20:02 -0700 Subject: [PATCH 65/72] Fix worker_avail (#336) Co-authored-by: Rob Ede --- actix-server/src/accept.rs | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/actix-server/src/accept.rs b/actix-server/src/accept.rs index 026bcc37..5b152fb2 100644 --- a/actix-server/src/accept.rs +++ b/actix-server/src/accept.rs @@ -116,14 +116,11 @@ impl Availability { panic!("Max WorkerHandle count is 512") }; + let off = 1 << idx as u128; if avail { - self.0[offset] |= 1 << idx as u128; + self.0[offset] |= off; } else { - let shift = 1 << idx as u128; - - debug_assert_ne!(self.0[offset] & shift, 0); - - self.0[offset] ^= shift; + self.0[offset] &= !off } } @@ -527,6 +524,9 @@ mod test { aval.set_available(idx, false); assert!(!aval.available()); + + aval.set_available(idx, false); + assert!(!aval.available()); } fn multi(aval: &mut Availability, mut idx: Vec) { @@ -565,13 +565,6 @@ mod test { single(&mut aval, 512); } - #[test] - #[should_panic] - fn double_set_unavailable() { - let mut aval = Availability::default(); - aval.set_available(233, false); - } - #[test] fn pin_point() { let mut aval = Availability::default(); From bd48908792c57f8d55ca20d058803fcf7e7d234d Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Thu, 15 Apr 2021 21:59:10 -0700 Subject: [PATCH 66/72] Return worker index in WakerInterest::WorkerAvailable (#337) --- actix-server/src/accept.rs | 5 ++--- actix-server/src/builder.rs | 2 +- actix-server/src/waker_queue.rs | 2 +- actix-server/src/worker.rs | 6 ++++-- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/actix-server/src/accept.rs b/actix-server/src/accept.rs index 5b152fb2..dbf66b20 100644 --- a/actix-server/src/accept.rs +++ b/actix-server/src/accept.rs @@ -238,11 +238,10 @@ impl Accept { match guard.pop_front() { // worker notify it becomes available. we may want to recover // from backpressure. - Some(WakerInterest::WorkerAvailable) => { + Some(WakerInterest::WorkerAvailable(idx)) => { drop(guard); - // Assume all worker are avail as no worker index returned. - self.avail.set_available_all(&self.handles); self.maybe_backpressure(&mut sockets, false); + self.avail.set_available(idx, true); } // a new worker thread is made and it's handle would be added to Accept Some(WakerInterest::Worker(handle)) => { diff --git a/actix-server/src/builder.rs b/actix-server/src/builder.rs index aa18bb22..66aba10c 100644 --- a/actix-server/src/builder.rs +++ b/actix-server/src/builder.rs @@ -320,7 +320,7 @@ impl ServerBuilder { idx: usize, waker: WakerQueue, ) -> (WorkerHandleAccept, WorkerHandleServer) { - let avail = WorkerAvailability::new(waker); + let avail = WorkerAvailability::new(idx, waker); let services = self.services.iter().map(|v| v.clone_factory()).collect(); ServerWorker::start(idx, services, avail, self.worker_config) diff --git a/actix-server/src/waker_queue.rs b/actix-server/src/waker_queue.rs index 8aa493aa..3f8669d4 100644 --- a/actix-server/src/waker_queue.rs +++ b/actix-server/src/waker_queue.rs @@ -72,7 +72,7 @@ impl WakerQueue { pub(crate) enum WakerInterest { /// `WorkerAvailable` is an interest from `Worker` notifying `Accept` there is a worker /// available and can accept new tasks. - WorkerAvailable, + WorkerAvailable(usize), /// `Pause`, `Resume`, `Stop` Interest are from `ServerBuilder` future. It listens to /// `ServerCommand` and notify `Accept` to do exactly these tasks. Pause, diff --git a/actix-server/src/worker.rs b/actix-server/src/worker.rs index 65951345..3d499382 100644 --- a/actix-server/src/worker.rs +++ b/actix-server/src/worker.rs @@ -96,13 +96,15 @@ impl WorkerHandleServer { #[derive(Clone)] pub(crate) struct WorkerAvailability { + idx: usize, waker: WakerQueue, available: Arc, } impl WorkerAvailability { - pub fn new(waker: WakerQueue) -> Self { + pub fn new(idx: usize, waker: WakerQueue) -> Self { WorkerAvailability { + idx, waker, available: Arc::new(AtomicBool::new(false)), } @@ -116,7 +118,7 @@ impl WorkerAvailability { let old = self.available.swap(val, Ordering::Release); // notify the accept on switched to available. if !old && val { - self.waker.wake(WakerInterest::WorkerAvailable); + self.waker.wake(WakerInterest::WorkerAvailable(self.idx)); } } } From 19468feef8b0fb4be1b5e4bd0e799a4d7b55a966 Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Fri, 16 Apr 2021 03:20:08 -0700 Subject: [PATCH 67/72] Fix memory ordering of WorkerAvailability (#340) --- actix-server/src/accept.rs | 8 ++++---- actix-server/src/worker.rs | 27 +++++++++++++++++++-------- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/actix-server/src/accept.rs b/actix-server/src/accept.rs index dbf66b20..23ba616c 100644 --- a/actix-server/src/accept.rs +++ b/actix-server/src/accept.rs @@ -128,7 +128,7 @@ impl Availability { /// This would result in a re-check on all workers' availability. fn set_available_all(&mut self, handles: &[WorkerHandleAccept]) { handles.iter().for_each(|handle| { - self.set_available(handle.idx, true); + self.set_available(handle.idx(), true); }) } } @@ -248,7 +248,7 @@ impl Accept { drop(guard); // maybe we want to recover from a backpressure. self.maybe_backpressure(&mut sockets, false); - self.avail.set_available(handle.idx, true); + self.avail.set_available(handle.idx(), true); self.handles.push(handle); } // got timer interest and it's time to try register socket(s) again @@ -400,7 +400,7 @@ impl Accept { } else { while self.avail.available() { let next = self.next(); - let idx = next.idx; + let idx = next.idx(); if next.available() { self.avail.set_available(idx, true); match self.send_connection(sockets, conn) { @@ -503,7 +503,7 @@ impl Accept { /// Remove next worker handle that fail to accept connection. fn remove_next(&mut self) { let handle = self.handles.swap_remove(self.next); - let idx = handle.idx; + let idx = handle.idx(); // A message is sent to `ServerBuilder` future to notify it a new worker // should be made. self.srv.worker_faulted(idx); diff --git a/actix-server/src/worker.rs b/actix-server/src/worker.rs index 3d499382..7bc211b1 100644 --- a/actix-server/src/worker.rs +++ b/actix-server/src/worker.rs @@ -47,11 +47,7 @@ fn handle_pair( tx2: UnboundedSender, avail: WorkerAvailability, ) -> (WorkerHandleAccept, WorkerHandleServer) { - let accept = WorkerHandleAccept { - idx, - tx: tx1, - avail, - }; + let accept = WorkerHandleAccept { tx: tx1, avail }; let server = WorkerHandleServer { idx, tx: tx2 }; @@ -63,16 +59,22 @@ fn handle_pair( /// /// Held by [Accept](crate::accept::Accept). pub(crate) struct WorkerHandleAccept { - pub idx: usize, tx: UnboundedSender, avail: WorkerAvailability, } impl WorkerHandleAccept { + #[inline(always)] + pub(crate) fn idx(&self) -> usize { + self.avail.idx + } + + #[inline(always)] pub(crate) fn send(&self, msg: Conn) -> Result<(), Conn> { self.tx.send(msg).map_err(|msg| msg.0) } + #[inline(always)] pub(crate) fn available(&self) -> bool { self.avail.available() } @@ -110,13 +112,18 @@ impl WorkerAvailability { } } + #[inline(always)] pub fn available(&self) -> bool { self.available.load(Ordering::Acquire) } pub fn set(&self, val: bool) { - let old = self.available.swap(val, Ordering::Release); - // notify the accept on switched to available. + // Ordering: + // + // There could be multiple set calls happen in one ::poll. + // Order is important between them. + let old = self.available.swap(val, Ordering::AcqRel); + // Notify the accept on switched to available. if !old && val { self.waker.wake(WakerInterest::WorkerAvailable(self.idx)); } @@ -374,6 +381,10 @@ impl Default for WorkerState { impl Drop for ServerWorker { fn drop(&mut self) { + // Set availability to true so if accept try to send connection to this worker + // it would find worker is gone and remove it. + // This is helpful when worker is dropped unexpected. + self.availability.set(true); // Stop the Arbiter ServerWorker runs on on drop. Arbiter::current().stop(); } From 2435520e6713c197227d21be8828e5354aa956fb Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Fri, 16 Apr 2021 06:40:21 -0700 Subject: [PATCH 68/72] Remove/restart worker test (#341) --- actix-server/tests/test_server.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/actix-server/tests/test_server.rs b/actix-server/tests/test_server.rs index 3af072bb..cc9f8190 100644 --- a/actix-server/tests/test_server.rs +++ b/actix-server/tests/test_server.rs @@ -437,6 +437,7 @@ async fn test_service_restart() { let _ = h.join().unwrap(); } +#[ignore] #[actix_rt::test] async fn worker_restart() { use actix_service::{Service, ServiceFactory}; From 1c4e96536637cde59110e298b322c5332f4caeba Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Fri, 16 Apr 2021 15:18:53 +0100 Subject: [PATCH 69/72] prepare service release 2.0.0 (#339) --- actix-server/Cargo.toml | 2 +- actix-service/CHANGES.md | 3 +++ actix-service/Cargo.toml | 7 ++----- actix-service/README.md | 4 ++-- actix-tls/Cargo.toml | 2 +- actix-tracing/Cargo.toml | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/actix-server/Cargo.toml b/actix-server/Cargo.toml index 80b44c6d..a839dddb 100755 --- a/actix-server/Cargo.toml +++ b/actix-server/Cargo.toml @@ -22,7 +22,7 @@ default = [] [dependencies] actix-rt = { version = "2.0.0", default-features = false } -actix-service = "2.0.0-beta.5" +actix-service = "2.0.0" actix-utils = "3.0.0-beta.4" futures-core = { version = "0.3.7", default-features = false, features = ["alloc"] } diff --git a/actix-service/CHANGES.md b/actix-service/CHANGES.md index c99cc2eb..a0130dbc 100644 --- a/actix-service/CHANGES.md +++ b/actix-service/CHANGES.md @@ -1,6 +1,9 @@ # Changes ## Unreleased - 2021-xx-xx + + +## 2.0.0 - 2021-04-16 * Removed pipeline and related structs/functions. [#335] [#335]: https://github.com/actix/actix-net/pull/335 diff --git a/actix-service/Cargo.toml b/actix-service/Cargo.toml index 1c82f703..ad03242f 100644 --- a/actix-service/Cargo.toml +++ b/actix-service/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-service" -version = "2.0.0-beta.5" +version = "2.0.0" authors = [ "Nikolay Kim ", "Rob Ede ", @@ -8,10 +8,7 @@ authors = [ ] description = "Service trait and combinators for representing asynchronous request/response operations." keywords = ["network", "framework", "async", "futures", "service"] -homepage = "https://actix.rs" -repository = "https://github.com/actix/actix-net.git" -documentation = "https://docs.rs/actix-service" -readme = "README.md" +repository = "https://github.com/actix/actix-net" categories = ["network-programming", "asynchronous"] license = "MIT OR Apache-2.0" edition = "2018" diff --git a/actix-service/README.md b/actix-service/README.md index 54171274..913ac199 100644 --- a/actix-service/README.md +++ b/actix-service/README.md @@ -3,10 +3,10 @@ > 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.5)](https://docs.rs/actix-service/2.0.0-beta.5) +[![Documentation](https://docs.rs/actix-service/badge.svg?version=2.0.0)](https://docs.rs/actix-service/2.0.0) [![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.5/status.svg)](https://deps.rs/crate/actix-service/2.0.0-beta.5) +[![Dependency Status](https://deps.rs/crate/actix-service/2.0.0/status.svg)](https://deps.rs/crate/actix-service/2.0.0) ![Download](https://img.shields.io/crates/d/actix-service.svg) [![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x) diff --git a/actix-tls/Cargo.toml b/actix-tls/Cargo.toml index 7fbd94b0..4e047cf5 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.2.0", default-features = false } -actix-service = "2.0.0-beta.5" +actix-service = "2.0.0" actix-utils = "3.0.0-beta.4" derive_more = "0.99.5" diff --git a/actix-tracing/Cargo.toml b/actix-tracing/Cargo.toml index ec2e4a7c..46cdc278 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.5" +actix-service = "2.0.0" actix-utils = "3.0.0-beta.4" tracing = "0.1" From 978e4f25fb0078e02165c11784fa2694b72ed57a Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sat, 17 Apr 2021 02:00:36 +0100 Subject: [PATCH 70/72] prepare actix-utils release 3.0.0 (#342) --- actix-server/Cargo.toml | 2 +- actix-service/Cargo.toml | 4 ++-- actix-tls/Cargo.toml | 2 +- actix-tracing/Cargo.toml | 2 +- actix-utils/CHANGES.md | 4 ++++ actix-utils/Cargo.toml | 6 +++--- actix-utils/src/lib.rs | 2 +- 7 files changed, 13 insertions(+), 9 deletions(-) diff --git a/actix-server/Cargo.toml b/actix-server/Cargo.toml index a839dddb..a1cf520a 100755 --- a/actix-server/Cargo.toml +++ b/actix-server/Cargo.toml @@ -23,7 +23,7 @@ default = [] [dependencies] actix-rt = { version = "2.0.0", default-features = false } actix-service = "2.0.0" -actix-utils = "3.0.0-beta.4" +actix-utils = "3.0.0" futures-core = { version = "0.3.7", default-features = false, features = ["alloc"] } log = "0.4" diff --git a/actix-service/Cargo.toml b/actix-service/Cargo.toml index ad03242f..7865cd86 100644 --- a/actix-service/Cargo.toml +++ b/actix-service/Cargo.toml @@ -8,8 +8,8 @@ authors = [ ] description = "Service trait and combinators for representing asynchronous request/response operations." keywords = ["network", "framework", "async", "futures", "service"] -repository = "https://github.com/actix/actix-net" categories = ["network-programming", "asynchronous"] +repository = "https://github.com/actix/actix-net" license = "MIT OR Apache-2.0" edition = "2018" @@ -24,5 +24,5 @@ pin-project-lite = "0.2" [dev-dependencies] actix-rt = "2.0.0" -actix-utils = "3.0.0-beta.4" +actix-utils = "3.0.0" futures-util = { version = "0.3.7", default-features = false } diff --git a/actix-tls/Cargo.toml b/actix-tls/Cargo.toml index 4e047cf5..73395a14 100755 --- a/actix-tls/Cargo.toml +++ b/actix-tls/Cargo.toml @@ -43,7 +43,7 @@ uri = ["http"] actix-codec = "0.4.0-beta.1" actix-rt = { version = "2.2.0", default-features = false } actix-service = "2.0.0" -actix-utils = "3.0.0-beta.4" +actix-utils = "3.0.0" derive_more = "0.99.5" futures-core = { version = "0.3.7", default-features = false, features = ["alloc"] } diff --git a/actix-tracing/Cargo.toml b/actix-tracing/Cargo.toml index 46cdc278..2ed2c434 100644 --- a/actix-tracing/Cargo.toml +++ b/actix-tracing/Cargo.toml @@ -17,7 +17,7 @@ path = "src/lib.rs" [dependencies] actix-service = "2.0.0" -actix-utils = "3.0.0-beta.4" +actix-utils = "3.0.0" tracing = "0.1" tracing-futures = "0.2" diff --git a/actix-utils/CHANGES.md b/actix-utils/CHANGES.md index d14446de..79f171b4 100644 --- a/actix-utils/CHANGES.md +++ b/actix-utils/CHANGES.md @@ -3,6 +3,10 @@ ## Unreleased - 2021-xx-xx +## 3.0.0 - 2021-04-16 +* No significant changes from `3.0.0-beta.4`. + + ## 3.0.0-beta.4 - 2021-04-01 * Add `future::Either` type. [#305] diff --git a/actix-utils/Cargo.toml b/actix-utils/Cargo.toml index 8b593697..a94706a2 100644 --- a/actix-utils/Cargo.toml +++ b/actix-utils/Cargo.toml @@ -1,14 +1,14 @@ [package] name = "actix-utils" -version = "3.0.0-beta.4" +version = "3.0.0" authors = [ "Nikolay Kim ", "Rob Ede ", ] -description = "Utilities for the Actix ecosystem" +description = "Various utilities used in the Actix ecosystem" keywords = ["network", "framework", "async", "futures"] -repository = "https://github.com/actix/actix-net.git" categories = ["network-programming", "asynchronous"] +repository = "https://github.com/actix/actix-net" license = "MIT OR Apache-2.0" edition = "2018" diff --git a/actix-utils/src/lib.rs b/actix-utils/src/lib.rs index f94147ec..6d431d52 100644 --- a/actix-utils/src/lib.rs +++ b/actix-utils/src/lib.rs @@ -1,4 +1,4 @@ -//! Various utilities for the Actix ecosystem. +//! Various utilities used in the Actix ecosystem. #![deny(rust_2018_idioms, nonstandard_style)] #![warn(missing_docs)] From 76338a58223c2e7261047ceff3253e5f71ad8c6b Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Tue, 20 Apr 2021 05:16:32 +0100 Subject: [PATCH 71/72] prepare server release 2.0.0-beta.5 --- actix-server/CHANGES.md | 3 +++ actix-server/Cargo.toml | 2 +- actix-tls/Cargo.toml | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/actix-server/CHANGES.md b/actix-server/CHANGES.md index 7ccada0e..58b1bd38 100644 --- a/actix-server/CHANGES.md +++ b/actix-server/CHANGES.md @@ -1,6 +1,9 @@ # Changes ## Unreleased - 2021-xx-xx + + +## 2.0.0-beta.5 - 2021-04-20 * Server shutdown would notify all workers to exit regardless if shutdown is graceful. This would make all worker shutdown immediately in force shutdown case. [#333] diff --git a/actix-server/Cargo.toml b/actix-server/Cargo.toml index a1cf520a..cafca3e6 100755 --- a/actix-server/Cargo.toml +++ b/actix-server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-server" -version = "2.0.0-beta.4" +version = "2.0.0-beta.5" authors = [ "Nikolay Kim ", "fakeshadow <24548779@qq.com>", diff --git a/actix-tls/Cargo.toml b/actix-tls/Cargo.toml index 73395a14..9fa260c7 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.2.0" -actix-server = "2.0.0-beta.3" +actix-server = "2.0.0-beta.5" bytes = "1" env_logger = "0.8" futures-util = { version = "0.3.7", default-features = false, features = ["sink"] } From b2e96409525739c2a6675dfddeefb604977bac28 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Wed, 21 Apr 2021 11:08:43 +0100 Subject: [PATCH 72/72] prepare codec 0.4.0 release (#346) --- actix-codec/CHANGES.md | 20 ++++++++++++-------- actix-codec/Cargo.toml | 6 ++---- actix-server/Cargo.toml | 3 +-- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/actix-codec/CHANGES.md b/actix-codec/CHANGES.md index f6102cbf..fd893454 100644 --- a/actix-codec/CHANGES.md +++ b/actix-codec/CHANGES.md @@ -3,6 +3,10 @@ ## Unreleased - 2021-xx-xx +## 0.4.0 - 2021-04-20 +* No significant changes since v0.4.0-beta.1. + + ## 0.4.0-beta.1 - 2020-12-28 * Replace `pin-project` with `pin-project-lite`. [#237] * Upgrade `tokio` dependency to `1`. [#237] @@ -23,28 +27,28 @@ ## 0.3.0-beta.1 - 2020-08-19 * Use `.advance()` instead of `.split_to()`. * Upgrade `tokio-util` to `0.3`. -* Improve `BytesCodec` `.encode()` performance -* Simplify `BytesCodec` `.decode()` +* Improve `BytesCodec::encode()` performance. +* Simplify `BytesCodec::decode()`. * Rename methods on `Framed` to better describe their use. * 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 -* Use specific futures dependencies +* Use specific futures dependencies. ## 0.2.0-alpha.4 -* Fix buffer remaining capacity calculation +* Fix buffer remaining capacity calculation. ## 0.2.0-alpha.3 -* Use tokio 0.2 -* Fix low/high watermark for write/read buffers +* Use tokio 0.2. +* Fix low/high watermark for write/read buffers. ## 0.2.0-alpha.2 -* Migrated to `std::future` +* Migrated to `std::future`. ## 0.1.2 - 2019-03-27 @@ -56,4 +60,4 @@ ## 0.1.0 - 2018-12-09 -* Move codec to separate crate +* Move codec to separate crate. diff --git a/actix-codec/Cargo.toml b/actix-codec/Cargo.toml index 95a24764..815f1039 100644 --- a/actix-codec/Cargo.toml +++ b/actix-codec/Cargo.toml @@ -1,12 +1,10 @@ [package] name = "actix-codec" -version = "0.4.0-beta.1" +version = "0.4.0" authors = ["Nikolay Kim "] 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" +repository = "https://github.com/actix/actix-net" categories = ["network-programming", "asynchronous"] license = "MIT OR Apache-2.0" edition = "2018" diff --git a/actix-server/Cargo.toml b/actix-server/Cargo.toml index cafca3e6..333e7549 100755 --- a/actix-server/Cargo.toml +++ b/actix-server/Cargo.toml @@ -7,8 +7,7 @@ authors = [ ] 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" +repository = "https://github.com/actix/actix-net" categories = ["network-programming", "asynchronous"] license = "MIT OR Apache-2.0" edition = "2018"