mirror of
https://github.com/fafhrd91/actix-net
synced 2025-08-13 18:58:23 +02:00
Compare commits
19 Commits
macros-v0.
...
utils-v3.0
Author | SHA1 | Date | |
---|---|---|---|
|
a09f9abfcb | ||
|
e4a44b77e6 | ||
|
2ee8f45f5d | ||
|
f48e3f4cb0 | ||
|
3d3bd60368 | ||
|
d684128831 | ||
|
0c12930796 | ||
|
ba44ea7d0b | ||
|
8a58a341a4 | ||
|
33c9aa6988 | ||
|
3ab8c3eb69 | ||
|
518bf3f6a6 | ||
|
43ce25cda1 | ||
|
4e4122b702 | ||
|
b296d0f254 | ||
|
02a902068f | ||
|
049795662f | ||
|
4e43216b99 | ||
|
93889776c4 |
29
.github/workflows/bench.yml
vendored
29
.github/workflows/bench.yml
vendored
@@ -1,29 +0,0 @@
|
||||
name: Benchmark (Linux)
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- '1.0'
|
||||
|
||||
jobs:
|
||||
check_benchmark:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Install Rust
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: nightly
|
||||
profile: minimal
|
||||
override: true
|
||||
|
||||
- name: Check benchmark
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: bench
|
||||
args: --package=actix-service
|
2
.github/workflows/linux.yml
vendored
2
.github/workflows/linux.yml
vendored
@@ -14,7 +14,7 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
version:
|
||||
- 1.42.0
|
||||
- 1.46.0
|
||||
- stable
|
||||
- nightly
|
||||
|
||||
|
@@ -16,16 +16,16 @@ members = [
|
||||
]
|
||||
|
||||
[patch.crates-io]
|
||||
actix-codec = { path = "actix-codec" }
|
||||
actix-codec = { git = "https://github.com/actix/actix-net.git", rev = "ba44ea7d0bafaf5fccb9a34003d503e1910943ee" }
|
||||
actix-connect = { path = "actix-connect" }
|
||||
actix-rt = { path = "actix-rt" }
|
||||
actix-rt = { git = "https://github.com/actix/actix-net.git", rev = "ba44ea7d0bafaf5fccb9a34003d503e1910943ee" }
|
||||
actix-macros = { path = "actix-macros" }
|
||||
actix-server = { path = "actix-server" }
|
||||
actix-service = { path = "actix-service" }
|
||||
actix-service = { git = "https://github.com/actix/actix-net.git", rev = "ba44ea7d0bafaf5fccb9a34003d503e1910943ee" }
|
||||
actix-testing = { path = "actix-testing" }
|
||||
actix-threadpool = { path = "actix-threadpool" }
|
||||
actix-tls = { path = "actix-tls" }
|
||||
actix-tracing = { path = "actix-tracing" }
|
||||
actix-utils = { path = "actix-utils" }
|
||||
actix-utils = { git = "https://github.com/actix/actix-net.git", rev = "ba44ea7d0bafaf5fccb9a34003d503e1910943ee" }
|
||||
actix-router = { path = "router" }
|
||||
bytestring = { path = "string" }
|
||||
|
@@ -14,7 +14,7 @@ Actix net - framework for composable network services
|
||||
## Documentation & community resources
|
||||
|
||||
* [Chat on Gitter](https://gitter.im/actix/actix)
|
||||
* Minimum supported Rust version: 1.42 or later
|
||||
* Minimum supported Rust version: 1.46 or later
|
||||
|
||||
## Example
|
||||
|
||||
|
@@ -1,14 +1,25 @@
|
||||
# Changes
|
||||
|
||||
## Unreleased - 2020-xx-xx
|
||||
* Upgrade `pin-project` to `1.0`.
|
||||
|
||||
|
||||
## 0.4.0-beta.1 - 2020-12-28
|
||||
* Replace `pin-project` with `pin-project-lite`. [#237]
|
||||
* Upgrade `tokio` dependency to `1`. [#237]
|
||||
* Upgrade `tokio-util` dependency to `0.6`. [#237]
|
||||
* Upgrade `bytes` dependency to `1`. [#237]
|
||||
|
||||
[#237]: https://github.com/actix/actix-net/pull/237
|
||||
|
||||
|
||||
## 0.3.0 - 2020-08-23
|
||||
* No changes from beta 2.
|
||||
|
||||
|
||||
## 0.3.0-beta.2 - 2020-08-19
|
||||
* Remove unused type parameter from `Framed::replace_codec`.
|
||||
|
||||
|
||||
## 0.3.0-beta.1 - 2020-08-19
|
||||
* Use `.advance()` instead of `.split_to()`.
|
||||
* Upgrade `tokio-util` to `0.3`.
|
||||
@@ -18,32 +29,31 @@
|
||||
* Add method on `Framed` to get a pinned reference to the underlying I/O.
|
||||
* Add method on `Framed` check emptiness of read buffer.
|
||||
|
||||
## [0.2.0] - 2019-12-10
|
||||
|
||||
## 0.2.0 - 2019-12-10
|
||||
* Use specific futures dependencies
|
||||
|
||||
## [0.2.0-alpha.4]
|
||||
|
||||
## 0.2.0-alpha.4
|
||||
* Fix buffer remaining capacity calculation
|
||||
|
||||
## [0.2.0-alpha.3]
|
||||
|
||||
## 0.2.0-alpha.3
|
||||
* Use tokio 0.2
|
||||
|
||||
* Fix low/high watermark for write/read buffers
|
||||
|
||||
## [0.2.0-alpha.2]
|
||||
|
||||
## 0.2.0-alpha.2
|
||||
* Migrated to `std::future`
|
||||
|
||||
## [0.1.2] - 2019-03-27
|
||||
|
||||
## 0.1.2 - 2019-03-27
|
||||
* Added `Framed::map_io()` method.
|
||||
|
||||
## [0.1.1] - 2019-03-06
|
||||
|
||||
## 0.1.1 - 2019-03-06
|
||||
* Added `FramedParts::with_read_buffer()` method.
|
||||
|
||||
## [0.1.0] - 2018-12-09
|
||||
|
||||
## 0.1.0 - 2018-12-09
|
||||
* Move codec to separate crate
|
||||
|
@@ -1,8 +1,8 @@
|
||||
[package]
|
||||
name = "actix-codec"
|
||||
version = "0.3.0"
|
||||
version = "0.4.0-beta.1"
|
||||
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
||||
description = "Codec utilities for working with framed protocols."
|
||||
description = "Codec utilities for working with framed protocols"
|
||||
keywords = ["network", "framework", "async", "futures"]
|
||||
homepage = "https://actix.rs"
|
||||
repository = "https://github.com/actix/actix-net.git"
|
||||
@@ -17,10 +17,10 @@ path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
bitflags = "1.2.1"
|
||||
bytes = "0.5.2"
|
||||
futures-core = { version = "0.3.4", default-features = false }
|
||||
futures-sink = { version = "0.3.4", default-features = false }
|
||||
bytes = "1"
|
||||
futures-core = { version = "0.3.7", default-features = false }
|
||||
futures-sink = { version = "0.3.7", default-features = false }
|
||||
log = "0.4"
|
||||
pin-project = "1.0.0"
|
||||
tokio = { version = "0.2.5", default-features = false }
|
||||
tokio-util = { version = "0.3.1", default-features = false, features = ["codec"] }
|
||||
pin-project-lite = "0.2"
|
||||
tokio = "1"
|
||||
tokio-util = { version = "0.6", features = ["codec", "io"] }
|
||||
|
@@ -14,7 +14,7 @@ impl Encoder<Bytes> for BytesCodec {
|
||||
|
||||
#[inline]
|
||||
fn encode(&mut self, item: Bytes, dst: &mut BytesMut) -> Result<(), Self::Error> {
|
||||
dst.extend_from_slice(item.bytes());
|
||||
dst.extend_from_slice(item.chunk());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@@ -5,7 +5,6 @@ use std::{fmt, io};
|
||||
use bytes::{Buf, BytesMut};
|
||||
use futures_core::{ready, Stream};
|
||||
use futures_sink::Sink;
|
||||
use pin_project::pin_project;
|
||||
|
||||
use crate::{AsyncRead, AsyncWrite, Decoder, Encoder};
|
||||
|
||||
@@ -21,22 +20,23 @@ bitflags::bitflags! {
|
||||
}
|
||||
}
|
||||
|
||||
/// A unified `Stream` and `Sink` interface to an underlying I/O object, using
|
||||
/// the `Encoder` and `Decoder` traits to encode and decode frames.
|
||||
///
|
||||
/// Raw I/O objects work with byte sequences, but higher-level code usually
|
||||
/// wants to batch these into meaningful chunks, called "frames". This
|
||||
/// method layers framing on top of an I/O object, by using the `Encoder`/`Decoder`
|
||||
/// traits to handle encoding and decoding of message frames. Note that
|
||||
/// the incoming and outgoing frame types may be distinct.
|
||||
#[pin_project]
|
||||
pub struct Framed<T, U> {
|
||||
#[pin]
|
||||
io: T,
|
||||
codec: U,
|
||||
flags: Flags,
|
||||
read_buf: BytesMut,
|
||||
write_buf: BytesMut,
|
||||
pin_project_lite::pin_project! {
|
||||
/// A unified `Stream` and `Sink` interface to an underlying I/O object, using
|
||||
/// the `Encoder` and `Decoder` traits to encode and decode frames.
|
||||
///
|
||||
/// Raw I/O objects work with byte sequences, but higher-level code usually
|
||||
/// wants to batch these into meaningful chunks, called "frames". This
|
||||
/// method layers framing on top of an I/O object, by using the `Encoder`/`Decoder`
|
||||
/// traits to handle encoding and decoding of message frames. Note that
|
||||
/// the incoming and outgoing frame types may be distinct.
|
||||
pub struct Framed<T, U> {
|
||||
#[pin]
|
||||
io: T,
|
||||
codec: U,
|
||||
flags: Flags,
|
||||
read_buf: BytesMut,
|
||||
write_buf: BytesMut,
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Framed<T, U>
|
||||
@@ -220,7 +220,8 @@ impl<T, U> Framed<T, U> {
|
||||
if remaining < LW {
|
||||
this.read_buf.reserve(HW - remaining)
|
||||
}
|
||||
let cnt = match this.io.poll_read_buf(cx, &mut this.read_buf) {
|
||||
|
||||
let cnt = match tokio_util::io::poll_read_buf(this.io, cx, this.read_buf) {
|
||||
Poll::Pending => return Poll::Pending,
|
||||
Poll::Ready(Err(e)) => return Poll::Ready(Some(Err(e.into()))),
|
||||
Poll::Ready(Ok(cnt)) => cnt,
|
||||
|
@@ -1,16 +1,16 @@
|
||||
//! Utilities for encoding and decoding frames.
|
||||
//! Codec utilities for working with framed protocols.
|
||||
//!
|
||||
//! Contains adapters to go from streams of bytes, [`AsyncRead`] and
|
||||
//! [`AsyncWrite`], to framed streams implementing [`Sink`] and [`Stream`].
|
||||
//! Framed streams are also known as `transports`.
|
||||
//!
|
||||
//! [`AsyncRead`]: AsyncRead
|
||||
//! [`AsyncWrite`]: AsyncWrite
|
||||
//! [`Sink`]: futures_sink::Sink
|
||||
//! [`Stream`]: futures_core::Stream
|
||||
|
||||
#![deny(rust_2018_idioms)]
|
||||
#![deny(rust_2018_idioms, nonstandard_style)]
|
||||
#![warn(missing_docs)]
|
||||
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
||||
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
||||
|
||||
mod bcodec;
|
||||
mod framed;
|
||||
@@ -18,5 +18,6 @@ mod framed;
|
||||
pub use self::bcodec::BytesCodec;
|
||||
pub use self::framed::{Framed, FramedParts};
|
||||
|
||||
pub use tokio::io::{AsyncRead, AsyncWrite};
|
||||
pub use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
|
||||
pub use tokio_util::codec::{Decoder, Encoder};
|
||||
pub use tokio_util::io::poll_read_buf;
|
||||
|
@@ -138,7 +138,7 @@ impl<T: Address> fmt::Display for Connect<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterator over addresses in a [`Connect`](struct.Connect.html) request.
|
||||
/// Iterator over addresses in a [`Connect`] request.
|
||||
#[derive(Clone)]
|
||||
pub struct ConnectAddrsIter<'a> {
|
||||
inner: Either<Option<SocketAddr>, vec_deque::Iter<'a, SocketAddr>>,
|
||||
@@ -173,7 +173,7 @@ impl ExactSizeIterator for ConnectAddrsIter<'_> {}
|
||||
|
||||
impl FusedIterator for ConnectAddrsIter<'_> {}
|
||||
|
||||
/// Owned iterator over addresses in a [`Connect`](struct.Connect.html) request.
|
||||
/// Owned iterator over addresses in a [`Connect`] request.
|
||||
#[derive(Debug)]
|
||||
pub struct ConnectTakeAddrsIter {
|
||||
inner: Either<Option<SocketAddr>, vec_deque::IntoIter<SocketAddr>>,
|
||||
|
@@ -40,8 +40,7 @@ impl<T> Clone for TcpConnectorFactory<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Address> ServiceFactory for TcpConnectorFactory<T> {
|
||||
type Request = Connect<T>;
|
||||
impl<T: Address> ServiceFactory<Connect<T>> for TcpConnectorFactory<T> {
|
||||
type Response = Connection<T, TcpStream>;
|
||||
type Error = ConnectError;
|
||||
type Config = ();
|
||||
@@ -70,16 +69,13 @@ impl<T> Clone for TcpConnector<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Address> Service for TcpConnector<T> {
|
||||
type Request = Connect<T>;
|
||||
impl<T: Address> Service<Connect<T>> for TcpConnector<T> {
|
||||
type Response = Connection<T, TcpStream>;
|
||||
type Error = ConnectError;
|
||||
#[allow(clippy::type_complexity)]
|
||||
type Future = Either<TcpConnectorResponse<T>, Ready<Result<Self::Response, Self::Error>>>;
|
||||
|
||||
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
actix_service::always_ready!();
|
||||
|
||||
fn call(&mut self, req: Connect<T>) -> Self::Future {
|
||||
let port = req.port();
|
||||
|
@@ -5,8 +5,10 @@
|
||||
//! * `openssl` - enables TLS support via `openssl` crate
|
||||
//! * `rustls` - enables TLS support via `rustls` crate
|
||||
|
||||
#![deny(rust_2018_idioms)]
|
||||
#![deny(rust_2018_idioms, nonstandard_style)]
|
||||
#![recursion_limit = "128"]
|
||||
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
||||
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
@@ -74,8 +76,8 @@ pub async fn start_default_resolver() -> Result<AsyncResolver, ConnectError> {
|
||||
/// Create TCP connector service.
|
||||
pub fn new_connector<T: Address + 'static>(
|
||||
resolver: AsyncResolver,
|
||||
) -> impl Service<Request = Connect<T>, Response = Connection<T, TcpStream>, Error = ConnectError>
|
||||
+ Clone {
|
||||
) -> impl Service<Connect<T>, Response = Connection<T, TcpStream>, Error = ConnectError> + Clone
|
||||
{
|
||||
pipeline(Resolver::new(resolver)).and_then(TcpConnector::new())
|
||||
}
|
||||
|
||||
@@ -83,8 +85,8 @@ pub fn new_connector<T: Address + 'static>(
|
||||
pub fn new_connector_factory<T: Address + 'static>(
|
||||
resolver: AsyncResolver,
|
||||
) -> impl ServiceFactory<
|
||||
Connect<T>,
|
||||
Config = (),
|
||||
Request = Connect<T>,
|
||||
Response = Connection<T, TcpStream>,
|
||||
Error = ConnectError,
|
||||
InitError = (),
|
||||
@@ -94,15 +96,15 @@ pub fn new_connector_factory<T: Address + 'static>(
|
||||
|
||||
/// Create connector service with default parameters.
|
||||
pub fn default_connector<T: Address + 'static>(
|
||||
) -> impl Service<Request = Connect<T>, Response = Connection<T, TcpStream>, Error = ConnectError>
|
||||
+ Clone {
|
||||
) -> impl Service<Connect<T>, Response = Connection<T, TcpStream>, Error = ConnectError> + Clone
|
||||
{
|
||||
pipeline(Resolver::default()).and_then(TcpConnector::new())
|
||||
}
|
||||
|
||||
/// Create connector service factory with default parameters.
|
||||
pub fn default_connector_factory<T: Address + 'static>() -> impl ServiceFactory<
|
||||
Connect<T>,
|
||||
Config = (),
|
||||
Request = Connect<T>,
|
||||
Response = Connection<T, TcpStream>,
|
||||
Error = ConnectError,
|
||||
InitError = (),
|
||||
|
@@ -54,8 +54,7 @@ impl<T> Clone for ResolverFactory<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Address> ServiceFactory for ResolverFactory<T> {
|
||||
type Request = Connect<T>;
|
||||
impl<T: Address> ServiceFactory<Connect<T>> for ResolverFactory<T> {
|
||||
type Response = Connect<T>;
|
||||
type Error = ConnectError;
|
||||
type Config = ();
|
||||
@@ -102,8 +101,7 @@ impl<T> Clone for Resolver<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Address> Service for Resolver<T> {
|
||||
type Request = Connect<T>;
|
||||
impl<T: Address> Service<Connect<T>> for Resolver<T> {
|
||||
type Response = Connect<T>;
|
||||
type Error = ConnectError;
|
||||
#[allow(clippy::type_complexity)]
|
||||
@@ -112,9 +110,7 @@ impl<T: Address> Service for Resolver<T> {
|
||||
Ready<Result<Connect<T>, Self::Error>>,
|
||||
>;
|
||||
|
||||
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
actix_service::always_ready!();
|
||||
|
||||
fn call(&mut self, mut req: Connect<T>) -> Self::Future {
|
||||
if req.addr.is_some() {
|
||||
|
@@ -70,8 +70,7 @@ impl<T> Clone for ConnectServiceFactory<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Address> ServiceFactory for ConnectServiceFactory<T> {
|
||||
type Request = Connect<T>;
|
||||
impl<T: Address> ServiceFactory<Connect<T>> for ConnectServiceFactory<T> {
|
||||
type Response = Connection<T, TcpStream>;
|
||||
type Error = ConnectError;
|
||||
type Config = ();
|
||||
@@ -90,15 +89,12 @@ pub struct ConnectService<T> {
|
||||
resolver: Resolver<T>,
|
||||
}
|
||||
|
||||
impl<T: Address> Service for ConnectService<T> {
|
||||
type Request = Connect<T>;
|
||||
impl<T: Address> Service<Connect<T>> for ConnectService<T> {
|
||||
type Response = Connection<T, TcpStream>;
|
||||
type Error = ConnectError;
|
||||
type Future = ConnectServiceResponse<T>;
|
||||
|
||||
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
actix_service::always_ready!();
|
||||
|
||||
fn call(&mut self, req: Connect<T>) -> Self::Future {
|
||||
ConnectServiceResponse {
|
||||
@@ -109,8 +105,8 @@ impl<T: Address> Service for ConnectService<T> {
|
||||
}
|
||||
|
||||
enum ConnectState<T: Address> {
|
||||
Resolve(<Resolver<T> as Service>::Future),
|
||||
Connect(<TcpConnector<T> as Service>::Future),
|
||||
Resolve(<Resolver<T> as Service<Connect<T>>>::Future),
|
||||
Connect(<TcpConnector<T> as Service<Connect<T>>>::Future),
|
||||
}
|
||||
|
||||
impl<T: Address> ConnectState<T> {
|
||||
@@ -160,15 +156,12 @@ pub struct TcpConnectService<T> {
|
||||
resolver: Resolver<T>,
|
||||
}
|
||||
|
||||
impl<T: Address + 'static> Service for TcpConnectService<T> {
|
||||
type Request = Connect<T>;
|
||||
impl<T: Address + 'static> Service<Connect<T>> for TcpConnectService<T> {
|
||||
type Response = TcpStream;
|
||||
type Error = ConnectError;
|
||||
type Future = TcpConnectServiceResponse<T>;
|
||||
|
||||
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
actix_service::always_ready!();
|
||||
|
||||
fn call(&mut self, req: Connect<T>) -> Self::Future {
|
||||
TcpConnectServiceResponse {
|
||||
@@ -179,8 +172,8 @@ impl<T: Address + 'static> Service for TcpConnectService<T> {
|
||||
}
|
||||
|
||||
enum TcpConnectState<T: Address> {
|
||||
Resolve(<Resolver<T> as Service>::Future),
|
||||
Connect(<TcpConnector<T> as Service>::Future),
|
||||
Resolve(<Resolver<T> as Service<Connect<T>>>::Future),
|
||||
Connect(<TcpConnector<T> as Service<Connect<T>>>::Future),
|
||||
}
|
||||
|
||||
impl<T: Address> TcpConnectState<T> {
|
||||
|
@@ -100,9 +100,7 @@ where
|
||||
#[allow(clippy::type_complexity)]
|
||||
type Future = Either<ConnectAsyncExt<T, U>, Ready<Result<Self::Response, Self::Error>>>;
|
||||
|
||||
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
actix_service::always_ready!();
|
||||
|
||||
fn call(&mut self, stream: Connection<T, U>) -> Self::Future {
|
||||
trace!("SSL Handshake start for: {:?}", stream.host());
|
||||
@@ -220,9 +218,7 @@ impl<T: Address + 'static> Service for OpensslConnectService<T> {
|
||||
type Error = ConnectError;
|
||||
type Future = OpensslConnectServiceResponse<T>;
|
||||
|
||||
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
actix_service::always_ready!();
|
||||
|
||||
fn call(&mut self, req: Connect<T>) -> Self::Future {
|
||||
OpensslConnectServiceResponse {
|
||||
|
@@ -96,9 +96,7 @@ where
|
||||
type Error = std::io::Error;
|
||||
type Future = ConnectAsyncExt<T, U>;
|
||||
|
||||
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
actix_service::always_ready!();
|
||||
|
||||
fn call(&mut self, stream: Connection<T, U>) -> Self::Future {
|
||||
trace!("SSL Handshake start for: {:?}", stream.host());
|
||||
|
@@ -1,5 +1,8 @@
|
||||
//! Macros for use with Tokio
|
||||
extern crate proc_macro;
|
||||
|
||||
#![deny(rust_2018_idioms, nonstandard_style)]
|
||||
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
||||
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
|
@@ -2,10 +2,26 @@
|
||||
|
||||
## Unreleased - 2020-xx-xx
|
||||
|
||||
### Added
|
||||
|
||||
## 2.0.0-beta.1 - 2020-12-28
|
||||
### Added
|
||||
* Add `System::attach_to_tokio` method. [#173]
|
||||
|
||||
### Changed
|
||||
* Update `tokio` dependency to `1.0`. [#236]
|
||||
* Rename `time` module `delay_for` to `sleep`, `delay_until` to `sleep_until`, `Delay` to `Sleep`
|
||||
to stay aligned with Tokio's naming. [#236]
|
||||
* Remove `'static` lifetime requirement for `Runtime::block_on` and `SystemRunner::block_on`.
|
||||
* These methods 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,8 +1,8 @@
|
||||
[package]
|
||||
name = "actix-rt"
|
||||
version = "1.1.1"
|
||||
version = "2.0.0-beta.1"
|
||||
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
||||
description = "Actix runtime"
|
||||
description = "Tokio-based single-thread async runtime for the Actix ecosystem"
|
||||
keywords = ["network", "framework", "async", "futures"]
|
||||
homepage = "https://actix.rs"
|
||||
repository = "https://github.com/actix/actix-net.git"
|
||||
@@ -17,11 +17,5 @@ path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
actix-macros = "0.1.0"
|
||||
copyless = "0.1.4"
|
||||
futures-channel = "0.3.4"
|
||||
futures-util = { version = "0.3.4", default-features = false, features = ["alloc"] }
|
||||
smallvec = "1"
|
||||
tokio = { version = "0.2.6", default-features = false, features = ["rt-core", "rt-util", "io-driver", "tcp", "uds", "udp", "time", "signal", "stream"] }
|
||||
|
||||
[dev-dependencies]
|
||||
tokio = { version = "0.2.6", features = ["full"] }
|
||||
tokio = { version = "1", features = ["rt", "net", "signal", "sync", "time"] }
|
||||
|
@@ -1,31 +1,30 @@
|
||||
use std::any::{Any, TypeId};
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::task::{Context, Poll};
|
||||
use std::{fmt, thread};
|
||||
|
||||
use futures_channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender};
|
||||
use futures_channel::oneshot::{channel, Canceled, Sender};
|
||||
use futures_util::{
|
||||
future::{self, Future, FutureExt},
|
||||
stream::Stream,
|
||||
};
|
||||
use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender};
|
||||
use tokio::sync::oneshot::{channel, error::RecvError as Canceled, Sender};
|
||||
// use futures_util::stream::FuturesUnordered;
|
||||
// use tokio::task::JoinHandle;
|
||||
// use tokio::stream::StreamExt;
|
||||
use tokio::task::LocalSet;
|
||||
|
||||
use crate::runtime::Runtime;
|
||||
use crate::system::System;
|
||||
|
||||
use copyless::BoxHelper;
|
||||
|
||||
use smallvec::SmallVec;
|
||||
pub use tokio::task::JoinHandle;
|
||||
|
||||
thread_local!(
|
||||
static ADDR: RefCell<Option<Arbiter>> = RefCell::new(None);
|
||||
static RUNNING: Cell<bool> = Cell::new(false);
|
||||
static Q: RefCell<Vec<Pin<Box<dyn Future<Output = ()>>>>> = RefCell::new(Vec::new());
|
||||
static PENDING: RefCell<SmallVec<[JoinHandle<()>; 8]>> = RefCell::new(SmallVec::new());
|
||||
// TODO: Commented out code are for Arbiter::local_join function.
|
||||
// It can be safely removed if this function is not used in actix-*.
|
||||
//
|
||||
// /// stores join handle for spawned async tasks.
|
||||
// static HANDLE: RefCell<FuturesUnordered<JoinHandle<()>>> =
|
||||
// RefCell::new(FuturesUnordered::new());
|
||||
static STORAGE: RefCell<HashMap<TypeId, Box<dyn Any>>> = RefCell::new(HashMap::new());
|
||||
);
|
||||
|
||||
@@ -69,14 +68,14 @@ impl Default for Arbiter {
|
||||
}
|
||||
|
||||
impl Arbiter {
|
||||
pub(crate) fn new_system() -> Self {
|
||||
let (tx, rx) = unbounded();
|
||||
pub(crate) fn new_system(local: &LocalSet) -> Self {
|
||||
let (tx, rx) = unbounded_channel();
|
||||
|
||||
let arb = Arbiter::with_sender(tx);
|
||||
ADDR.with(|cell| *cell.borrow_mut() = Some(arb.clone()));
|
||||
RUNNING.with(|cell| cell.set(false));
|
||||
STORAGE.with(|cell| cell.borrow_mut().clear());
|
||||
Arbiter::spawn(ArbiterController { stop: None, rx });
|
||||
|
||||
local.spawn_local(ArbiterController { rx });
|
||||
|
||||
arb
|
||||
}
|
||||
@@ -91,13 +90,14 @@ impl Arbiter {
|
||||
}
|
||||
|
||||
/// Check if current arbiter is running.
|
||||
#[deprecated(note = "Thread local variables for running state of Arbiter is removed")]
|
||||
pub fn is_running() -> bool {
|
||||
RUNNING.with(|cell| cell.get())
|
||||
false
|
||||
}
|
||||
|
||||
/// Stop arbiter from continuing it's event loop.
|
||||
pub fn stop(&self) {
|
||||
let _ = self.sender.unbounded_send(ArbiterCommand::Stop);
|
||||
let _ = self.sender.send(ArbiterCommand::Stop);
|
||||
}
|
||||
|
||||
/// Spawn new thread and run event loop in spawned thread.
|
||||
@@ -106,69 +106,47 @@ impl Arbiter {
|
||||
let id = COUNT.fetch_add(1, Ordering::Relaxed);
|
||||
let name = format!("actix-rt:worker:{}", id);
|
||||
let sys = System::current();
|
||||
let (arb_tx, arb_rx) = unbounded();
|
||||
let arb_tx2 = arb_tx.clone();
|
||||
let (tx, rx) = unbounded_channel();
|
||||
|
||||
let handle = thread::Builder::new()
|
||||
.name(name.clone())
|
||||
.spawn(move || {
|
||||
let mut rt = Runtime::new().expect("Can not create Runtime");
|
||||
let arb = Arbiter::with_sender(arb_tx);
|
||||
.spawn({
|
||||
let tx = tx.clone();
|
||||
move || {
|
||||
let rt = Runtime::new().expect("Can not create Runtime");
|
||||
let arb = Arbiter::with_sender(tx);
|
||||
|
||||
let (stop, stop_rx) = channel();
|
||||
RUNNING.with(|cell| cell.set(true));
|
||||
STORAGE.with(|cell| cell.borrow_mut().clear());
|
||||
STORAGE.with(|cell| cell.borrow_mut().clear());
|
||||
|
||||
System::set_current(sys);
|
||||
System::set_current(sys);
|
||||
|
||||
// start arbiter controller
|
||||
rt.spawn(ArbiterController {
|
||||
stop: Some(stop),
|
||||
rx: arb_rx,
|
||||
});
|
||||
ADDR.with(|cell| *cell.borrow_mut() = Some(arb.clone()));
|
||||
ADDR.with(|cell| *cell.borrow_mut() = Some(arb.clone()));
|
||||
|
||||
// register arbiter
|
||||
let _ = System::current()
|
||||
.sys()
|
||||
.unbounded_send(SystemCommand::RegisterArbiter(id, arb));
|
||||
// register arbiter
|
||||
let _ = System::current()
|
||||
.sys()
|
||||
.send(SystemCommand::RegisterArbiter(id, arb));
|
||||
|
||||
// run loop
|
||||
let _ = rt.block_on(stop_rx).unwrap_or(1);
|
||||
// start arbiter controller
|
||||
// run loop
|
||||
rt.block_on(ArbiterController { rx });
|
||||
|
||||
// unregister arbiter
|
||||
let _ = System::current()
|
||||
.sys()
|
||||
.unbounded_send(SystemCommand::UnregisterArbiter(id));
|
||||
// unregister arbiter
|
||||
let _ = System::current()
|
||||
.sys()
|
||||
.send(SystemCommand::UnregisterArbiter(id));
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(|err| {
|
||||
panic!("Cannot spawn an arbiter's thread {:?}: {:?}", &name, err)
|
||||
});
|
||||
|
||||
Arbiter {
|
||||
sender: arb_tx2,
|
||||
sender: tx,
|
||||
thread_handle: Some(handle),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn run_system(rt: Option<&Runtime>) {
|
||||
RUNNING.with(|cell| cell.set(true));
|
||||
Q.with(|cell| {
|
||||
let mut v = cell.borrow_mut();
|
||||
for fut in v.drain(..) {
|
||||
if let Some(rt) = rt {
|
||||
rt.spawn(fut);
|
||||
} else {
|
||||
tokio::task::spawn_local(fut);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) fn stop_system() {
|
||||
RUNNING.with(|cell| cell.set(false));
|
||||
}
|
||||
|
||||
/// Spawn a future on the current thread. This does not create a new Arbiter
|
||||
/// or Arbiter address, it is simply a helper for spawning futures on the current
|
||||
/// thread.
|
||||
@@ -176,26 +154,12 @@ impl Arbiter {
|
||||
where
|
||||
F: Future<Output = ()> + 'static,
|
||||
{
|
||||
RUNNING.with(move |cell| {
|
||||
if cell.get() {
|
||||
// Spawn the future on running executor
|
||||
let len = PENDING.with(move |cell| {
|
||||
let mut p = cell.borrow_mut();
|
||||
p.push(tokio::task::spawn_local(future));
|
||||
p.len()
|
||||
});
|
||||
if len > 7 {
|
||||
// Before reaching the inline size
|
||||
tokio::task::spawn_local(CleanupPending);
|
||||
}
|
||||
} else {
|
||||
// Box the future and push it to the queue, this results in double boxing
|
||||
// because the executor boxes the future again, but works for now
|
||||
Q.with(move |cell| {
|
||||
cell.borrow_mut().push(Pin::from(Box::alloc().init(future)))
|
||||
});
|
||||
}
|
||||
});
|
||||
// HANDLE.with(|handle| {
|
||||
// let handle = handle.borrow();
|
||||
// handle.push(tokio::task::spawn_local(future));
|
||||
// });
|
||||
// let _ = tokio::task::spawn_local(CleanupPending);
|
||||
let _ = tokio::task::spawn_local(future);
|
||||
}
|
||||
|
||||
/// Executes a future on the current thread. This does not create a new Arbiter
|
||||
@@ -206,7 +170,9 @@ impl Arbiter {
|
||||
F: FnOnce() -> R + 'static,
|
||||
R: Future<Output = ()> + 'static,
|
||||
{
|
||||
Arbiter::spawn(future::lazy(|_| f()).flatten())
|
||||
Arbiter::spawn(async {
|
||||
f();
|
||||
})
|
||||
}
|
||||
|
||||
/// Send a future to the Arbiter's thread, and spawn it.
|
||||
@@ -214,9 +180,7 @@ impl Arbiter {
|
||||
where
|
||||
F: Future<Output = ()> + Send + Unpin + 'static,
|
||||
{
|
||||
let _ = self
|
||||
.sender
|
||||
.unbounded_send(ArbiterCommand::Execute(Box::new(future)));
|
||||
let _ = self.sender.send(ArbiterCommand::Execute(Box::new(future)));
|
||||
}
|
||||
|
||||
/// Send a function to the Arbiter's thread, and execute it. Any result from the function
|
||||
@@ -227,7 +191,7 @@ impl Arbiter {
|
||||
{
|
||||
let _ = self
|
||||
.sender
|
||||
.unbounded_send(ArbiterCommand::ExecuteFn(Box::new(move || {
|
||||
.send(ArbiterCommand::ExecuteFn(Box::new(move || {
|
||||
f();
|
||||
})));
|
||||
}
|
||||
@@ -243,8 +207,8 @@ impl Arbiter {
|
||||
let (tx, rx) = channel();
|
||||
let _ = self
|
||||
.sender
|
||||
.unbounded_send(ArbiterCommand::ExecuteFn(Box::new(move || {
|
||||
if !tx.is_canceled() {
|
||||
.send(ArbiterCommand::ExecuteFn(Box::new(move || {
|
||||
if !tx.is_closed() {
|
||||
let _ = tx.send(f());
|
||||
}
|
||||
})));
|
||||
@@ -313,40 +277,33 @@ impl Arbiter {
|
||||
|
||||
/// Returns a future that will be completed once all currently spawned futures
|
||||
/// have completed.
|
||||
pub fn local_join() -> impl Future<Output = ()> {
|
||||
PENDING.with(move |cell| {
|
||||
let current = cell.replace(SmallVec::new());
|
||||
future::join_all(current).map(|_| ())
|
||||
})
|
||||
#[deprecated(since = "1.2.0", note = "Arbiter::local_join function is removed.")]
|
||||
pub async fn local_join() {
|
||||
// let handle = HANDLE.with(|fut| std::mem::take(&mut *fut.borrow_mut()));
|
||||
// async move {
|
||||
// handle.collect::<Vec<_>>().await;
|
||||
// }
|
||||
unimplemented!("Arbiter::local_join function is removed.")
|
||||
}
|
||||
}
|
||||
|
||||
/// Future used for cleaning-up already finished `JoinHandle`s
|
||||
/// from the `PENDING` list so the vector doesn't grow indefinitely
|
||||
struct CleanupPending;
|
||||
|
||||
impl Future for CleanupPending {
|
||||
type Output = ();
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
PENDING.with(move |cell| {
|
||||
let mut pending = cell.borrow_mut();
|
||||
let mut i = 0;
|
||||
while i != pending.len() {
|
||||
if let Poll::Ready(_) = Pin::new(&mut pending[i]).poll(cx) {
|
||||
pending.remove(i);
|
||||
} else {
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Poll::Ready(())
|
||||
}
|
||||
}
|
||||
// /// Future used for cleaning-up already finished `JoinHandle`s
|
||||
// /// from the `PENDING` list so the vector doesn't grow indefinitely
|
||||
// struct CleanupPending;
|
||||
//
|
||||
// impl Future for CleanupPending {
|
||||
// type Output = ();
|
||||
//
|
||||
// fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
// HANDLE.with(move |handle| {
|
||||
// recycle_join_handle(&mut *handle.borrow_mut(), cx);
|
||||
// });
|
||||
//
|
||||
// Poll::Ready(())
|
||||
// }
|
||||
// }
|
||||
|
||||
struct ArbiterController {
|
||||
stop: Option<Sender<i32>>,
|
||||
rx: UnboundedReceiver<ArbiterCommand>,
|
||||
}
|
||||
|
||||
@@ -368,25 +325,17 @@ impl Future for ArbiterController {
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
loop {
|
||||
match Pin::new(&mut self.rx).poll_next(cx) {
|
||||
match Pin::new(&mut self.rx).poll_recv(cx) {
|
||||
Poll::Ready(None) => return Poll::Ready(()),
|
||||
Poll::Ready(Some(item)) => match item {
|
||||
ArbiterCommand::Stop => {
|
||||
if let Some(stop) = self.stop.take() {
|
||||
let _ = stop.send(0);
|
||||
};
|
||||
return Poll::Ready(());
|
||||
}
|
||||
ArbiterCommand::Stop => return Poll::Ready(()),
|
||||
ArbiterCommand::Execute(fut) => {
|
||||
let len = PENDING.with(move |cell| {
|
||||
let mut p = cell.borrow_mut();
|
||||
p.push(tokio::task::spawn_local(fut));
|
||||
p.len()
|
||||
});
|
||||
if len > 7 {
|
||||
// Before reaching the inline size
|
||||
tokio::task::spawn_local(CleanupPending);
|
||||
}
|
||||
// HANDLE.with(|handle| {
|
||||
// let mut handle = handle.borrow_mut();
|
||||
// handle.push(tokio::task::spawn_local(fut));
|
||||
// recycle_join_handle(&mut *handle, cx);
|
||||
// });
|
||||
tokio::task::spawn_local(fut);
|
||||
}
|
||||
ArbiterCommand::ExecuteFn(f) => {
|
||||
f.call_box();
|
||||
@@ -398,6 +347,20 @@ impl Future for ArbiterController {
|
||||
}
|
||||
}
|
||||
|
||||
// fn recycle_join_handle(handle: &mut FuturesUnordered<JoinHandle<()>>, cx: &mut Context<'_>) {
|
||||
// let _ = Pin::new(&mut *handle).poll_next(cx);
|
||||
//
|
||||
// // Try to recycle more join handles and free up memory.
|
||||
// //
|
||||
// // this is a guess. The yield limit for FuturesUnordered is 32.
|
||||
// // So poll an extra 3 times would make the total poll below 128.
|
||||
// if handle.len() > 64 {
|
||||
// (0..3).for_each(|_| {
|
||||
// let _ = Pin::new(&mut *handle).poll_next(cx);
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum SystemCommand {
|
||||
Exit(i32),
|
||||
@@ -427,7 +390,7 @@ impl Future for SystemArbiter {
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
loop {
|
||||
match Pin::new(&mut self.commands).poll_next(cx) {
|
||||
match Pin::new(&mut self.commands).poll_recv(cx) {
|
||||
Poll::Ready(None) => return Poll::Ready(()),
|
||||
Poll::Ready(Some(cmd)) => match cmd {
|
||||
SystemCommand::Exit(code) => {
|
||||
|
@@ -1,9 +1,9 @@
|
||||
use std::borrow::Cow;
|
||||
use std::future::Future;
|
||||
use std::io;
|
||||
|
||||
use futures_channel::mpsc::unbounded;
|
||||
use futures_channel::oneshot::{channel, Receiver};
|
||||
use futures_util::future::{lazy, Future, FutureExt};
|
||||
use tokio::sync::mpsc::unbounded_channel;
|
||||
use tokio::sync::oneshot::{channel, Receiver};
|
||||
use tokio::task::LocalSet;
|
||||
|
||||
use crate::arbiter::{Arbiter, SystemArbiter};
|
||||
@@ -65,16 +65,17 @@ impl Builder {
|
||||
/// Function `f` get called within tokio runtime context.
|
||||
pub fn run<F>(self, f: F) -> io::Result<()>
|
||||
where
|
||||
F: FnOnce() + 'static,
|
||||
F: FnOnce(),
|
||||
{
|
||||
self.create_runtime(f).run()
|
||||
}
|
||||
|
||||
fn create_async_runtime(self, local: &LocalSet) -> AsyncSystemRunner {
|
||||
let (stop_tx, stop) = channel();
|
||||
let (sys_sender, sys_receiver) = unbounded();
|
||||
let (sys_sender, sys_receiver) = unbounded_channel();
|
||||
|
||||
let system = System::construct(sys_sender, Arbiter::new_system(), self.stop_on_panic);
|
||||
let system =
|
||||
System::construct(sys_sender, Arbiter::new_system(local), self.stop_on_panic);
|
||||
|
||||
// system arbiter
|
||||
let arb = SystemArbiter::new(stop_tx, sys_receiver);
|
||||
@@ -87,21 +88,26 @@ impl Builder {
|
||||
|
||||
fn create_runtime<F>(self, f: F) -> SystemRunner
|
||||
where
|
||||
F: FnOnce() + 'static,
|
||||
F: FnOnce(),
|
||||
{
|
||||
let (stop_tx, stop) = channel();
|
||||
let (sys_sender, sys_receiver) = unbounded();
|
||||
let (sys_sender, sys_receiver) = unbounded_channel();
|
||||
|
||||
let system = System::construct(sys_sender, Arbiter::new_system(), self.stop_on_panic);
|
||||
let rt = Runtime::new().unwrap();
|
||||
|
||||
let system = System::construct(
|
||||
sys_sender,
|
||||
Arbiter::new_system(rt.local()),
|
||||
self.stop_on_panic,
|
||||
);
|
||||
|
||||
// system arbiter
|
||||
let arb = SystemArbiter::new(stop_tx, sys_receiver);
|
||||
|
||||
let mut rt = Runtime::new().unwrap();
|
||||
rt.spawn(arb);
|
||||
|
||||
// init system arbiter and run configuration method
|
||||
rt.block_on(lazy(move |_| f()));
|
||||
rt.block_on(async { f() });
|
||||
|
||||
SystemRunner { rt, stop, system }
|
||||
}
|
||||
@@ -120,27 +126,21 @@ impl AsyncSystemRunner {
|
||||
let AsyncSystemRunner { stop, .. } = self;
|
||||
|
||||
// run loop
|
||||
lazy(|_| {
|
||||
Arbiter::run_system(None);
|
||||
async {
|
||||
let res = match stop.await {
|
||||
Ok(code) => {
|
||||
if code != 0 {
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("Non-zero exit code: {}", code),
|
||||
))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
async {
|
||||
match stop.await {
|
||||
Ok(code) => {
|
||||
if code != 0 {
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("Non-zero exit code: {}", code),
|
||||
))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => Err(io::Error::new(io::ErrorKind::Other, e)),
|
||||
};
|
||||
Arbiter::stop_system();
|
||||
res
|
||||
}
|
||||
Err(e) => Err(io::Error::new(io::ErrorKind::Other, e)),
|
||||
}
|
||||
})
|
||||
.flatten()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,11 +157,10 @@ impl SystemRunner {
|
||||
/// This function will start event loop and will finish once the
|
||||
/// `System::stop()` function is called.
|
||||
pub fn run(self) -> io::Result<()> {
|
||||
let SystemRunner { mut rt, stop, .. } = self;
|
||||
let SystemRunner { rt, stop, .. } = self;
|
||||
|
||||
// run loop
|
||||
Arbiter::run_system(Some(&rt));
|
||||
let result = match rt.block_on(stop) {
|
||||
match rt.block_on(stop) {
|
||||
Ok(code) => {
|
||||
if code != 0 {
|
||||
Err(io::Error::new(
|
||||
@@ -173,19 +172,12 @@ impl SystemRunner {
|
||||
}
|
||||
}
|
||||
Err(e) => Err(io::Error::new(io::ErrorKind::Other, e)),
|
||||
};
|
||||
Arbiter::stop_system();
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
/// Execute a future and wait for result.
|
||||
pub fn block_on<F, O>(&mut self, fut: F) -> O
|
||||
where
|
||||
F: Future<Output = O> + 'static,
|
||||
{
|
||||
Arbiter::run_system(Some(&self.rt));
|
||||
let res = self.rt.block_on(fut);
|
||||
Arbiter::stop_system();
|
||||
res
|
||||
#[inline]
|
||||
pub fn block_on<F: Future>(&self, fut: F) -> F::Output {
|
||||
self.rt.block_on(fut)
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,11 @@
|
||||
//! A runtime implementation that runs everything on the current thread.
|
||||
#![deny(rust_2018_idioms, warnings)]
|
||||
//! Tokio-based single-thread async runtime for the Actix ecosystem.
|
||||
|
||||
#![deny(rust_2018_idioms, nonstandard_style)]
|
||||
#![allow(clippy::type_complexity)]
|
||||
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
||||
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
||||
|
||||
use std::future::Future;
|
||||
|
||||
#[cfg(not(test))] // Work around for rust-lang/rust#62127
|
||||
pub use actix_macros::{main, test};
|
||||
@@ -20,15 +25,12 @@ pub use self::system::System;
|
||||
/// # Panics
|
||||
///
|
||||
/// This function panics if actix system is not running.
|
||||
#[inline]
|
||||
pub fn spawn<F>(f: F)
|
||||
where
|
||||
F: futures_util::future::Future<Output = ()> + 'static,
|
||||
F: Future<Output = ()> + 'static,
|
||||
{
|
||||
if !System::is_set() {
|
||||
panic!("System is not running");
|
||||
}
|
||||
|
||||
Arbiter::spawn(f);
|
||||
Arbiter::spawn(f)
|
||||
}
|
||||
|
||||
/// Asynchronous signal handling
|
||||
@@ -57,7 +59,7 @@ pub mod net {
|
||||
/// Utilities for tracking time.
|
||||
pub mod time {
|
||||
pub use tokio::time::Instant;
|
||||
pub use tokio::time::{delay_for, delay_until, Delay};
|
||||
pub use tokio::time::{interval, interval_at, Interval};
|
||||
pub use tokio::time::{sleep, sleep_until, Sleep};
|
||||
pub use tokio::time::{timeout, Timeout};
|
||||
}
|
||||
|
@@ -7,7 +7,7 @@ use tokio::{runtime, task::LocalSet};
|
||||
///
|
||||
/// See [module level][mod] documentation for more details.
|
||||
///
|
||||
/// [mod]: index.html
|
||||
/// [mod]: crate
|
||||
#[derive(Debug)]
|
||||
pub struct Runtime {
|
||||
local: LocalSet,
|
||||
@@ -18,10 +18,9 @@ impl Runtime {
|
||||
#[allow(clippy::new_ret_no_self)]
|
||||
/// Returns a new runtime initialized with default configuration values.
|
||||
pub fn new() -> io::Result<Runtime> {
|
||||
let rt = runtime::Builder::new()
|
||||
let rt = runtime::Builder::new_current_thread()
|
||||
.enable_io()
|
||||
.enable_time()
|
||||
.basic_scheduler()
|
||||
.build()?;
|
||||
|
||||
Ok(Runtime {
|
||||
@@ -30,11 +29,15 @@ impl Runtime {
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn local(&self) -> &LocalSet {
|
||||
&self.local
|
||||
}
|
||||
|
||||
/// Spawn a future onto the single-threaded runtime.
|
||||
///
|
||||
/// See [module level][mod] documentation for more details.
|
||||
///
|
||||
/// [mod]: index.html
|
||||
/// [mod]: crate
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -44,7 +47,7 @@ impl Runtime {
|
||||
///
|
||||
/// # fn dox() {
|
||||
/// // Create the runtime
|
||||
/// let mut rt = Runtime::new().unwrap();
|
||||
/// let rt = Runtime::new().unwrap();
|
||||
///
|
||||
/// // Spawn a future onto the runtime
|
||||
/// rt.spawn(future::lazy(|_| {
|
||||
@@ -82,10 +85,10 @@ impl Runtime {
|
||||
///
|
||||
/// The caller is responsible for ensuring that other spawned futures
|
||||
/// complete execution by calling `block_on` or `run`.
|
||||
pub fn block_on<F>(&mut self, f: F) -> F::Output
|
||||
pub fn block_on<F>(&self, f: F) -> F::Output
|
||||
where
|
||||
F: Future + 'static,
|
||||
F: Future,
|
||||
{
|
||||
self.local.block_on(&mut self.rt, f)
|
||||
self.local.block_on(&self.rt, f)
|
||||
}
|
||||
}
|
||||
|
@@ -3,7 +3,7 @@ use std::future::Future;
|
||||
use std::io;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
use futures_channel::mpsc::UnboundedSender;
|
||||
use tokio::sync::mpsc::UnboundedSender;
|
||||
use tokio::task::LocalSet;
|
||||
|
||||
use crate::arbiter::{Arbiter, SystemCommand};
|
||||
@@ -66,12 +66,11 @@ impl System {
|
||||
/// It means that using this method currently it is impossible to make `actix-rt` work in the
|
||||
/// alternative `tokio` `Runtime`s (e.g. provided by [`tokio_compat`]).
|
||||
///
|
||||
/// [`Arbiter`]: struct.Arbiter.html
|
||||
/// [`tokio_compat`]: https://crates.io/crates/tokio-compat
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// ```rust,ignore
|
||||
/// use tokio::{runtime::Runtime, task::LocalSet};
|
||||
/// use actix_rt::System;
|
||||
/// use futures_util::future::try_join_all;
|
||||
@@ -95,10 +94,9 @@ impl System {
|
||||
/// }
|
||||
///
|
||||
///
|
||||
/// let mut runtime = tokio::runtime::Builder::new()
|
||||
/// .core_threads(2)
|
||||
/// let runtime = tokio::runtime::Builder::new_multi_thread()
|
||||
/// .worker_threads(2)
|
||||
/// .enable_all()
|
||||
/// .threaded_scheduler()
|
||||
/// .build()
|
||||
/// .unwrap();
|
||||
///
|
||||
@@ -131,7 +129,6 @@ impl System {
|
||||
/// It means that using this method currently it is impossible to make `actix-rt` work in the
|
||||
/// alternative `tokio` `Runtime`s (e.g. provided by `tokio_compat`).
|
||||
///
|
||||
/// [`Arbiter`]: struct.Arbiter.html
|
||||
/// [`tokio_compat`]: https://crates.io/crates/tokio-compat
|
||||
///
|
||||
/// # Arguments
|
||||
@@ -142,7 +139,7 @@ impl System {
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// ```rust,ignore
|
||||
/// use tokio::runtime::Runtime;
|
||||
/// use actix_rt::System;
|
||||
/// use futures_util::future::try_join_all;
|
||||
@@ -166,10 +163,9 @@ impl System {
|
||||
/// }
|
||||
///
|
||||
///
|
||||
/// let runtime = tokio::runtime::Builder::new()
|
||||
/// .core_threads(2)
|
||||
/// let runtime = tokio::runtime::Builder::new_multi_thread()
|
||||
/// .worker_threads(2)
|
||||
/// .enable_all()
|
||||
/// .threaded_scheduler()
|
||||
/// .build()
|
||||
/// .unwrap();
|
||||
///
|
||||
@@ -178,7 +174,7 @@ impl System {
|
||||
/// ```
|
||||
pub fn attach_to_tokio<Fut, R>(
|
||||
name: impl Into<String>,
|
||||
mut runtime: tokio::runtime::Runtime,
|
||||
runtime: tokio::runtime::Runtime,
|
||||
rest_operations: Fut,
|
||||
) -> R
|
||||
where
|
||||
@@ -235,7 +231,7 @@ impl System {
|
||||
|
||||
/// Stop the system with a particular exit code.
|
||||
pub fn stop_with_code(&self, code: i32) {
|
||||
let _ = self.sys.unbounded_send(SystemCommand::Exit(code));
|
||||
let _ = self.sys.send(SystemCommand::Exit(code));
|
||||
}
|
||||
|
||||
pub(crate) fn sys(&self) -> &UnboundedSender<SystemCommand> {
|
||||
@@ -258,7 +254,7 @@ impl System {
|
||||
/// Function `f` get called within tokio runtime context.
|
||||
pub fn run<F>(f: F) -> io::Result<()>
|
||||
where
|
||||
F: FnOnce() + 'static,
|
||||
F: FnOnce(),
|
||||
{
|
||||
Self::builder().run(f)
|
||||
}
|
||||
|
@@ -1,25 +1,11 @@
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
#[test]
|
||||
fn start_and_stop() {
|
||||
actix_rt::System::new("start_and_stop").block_on(async move {
|
||||
assert!(
|
||||
actix_rt::Arbiter::is_running(),
|
||||
"System doesn't seem to have started"
|
||||
);
|
||||
});
|
||||
assert!(
|
||||
!actix_rt::Arbiter::is_running(),
|
||||
"System doesn't seem to have stopped"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn await_for_timer() {
|
||||
let time = Duration::from_secs(2);
|
||||
let instant = Instant::now();
|
||||
actix_rt::System::new("test_wait_timer").block_on(async move {
|
||||
tokio::time::delay_for(time).await;
|
||||
tokio::time::sleep(time).await;
|
||||
});
|
||||
assert!(
|
||||
instant.elapsed() >= time,
|
||||
@@ -34,7 +20,7 @@ fn join_another_arbiter() {
|
||||
actix_rt::System::new("test_join_another_arbiter").block_on(async move {
|
||||
let mut arbiter = actix_rt::Arbiter::new();
|
||||
arbiter.send(Box::pin(async move {
|
||||
tokio::time::delay_for(time).await;
|
||||
tokio::time::sleep(time).await;
|
||||
actix_rt::Arbiter::current().stop();
|
||||
}));
|
||||
arbiter.join().unwrap();
|
||||
@@ -49,7 +35,7 @@ fn join_another_arbiter() {
|
||||
let mut arbiter = actix_rt::Arbiter::new();
|
||||
arbiter.exec_fn(move || {
|
||||
actix_rt::spawn(async move {
|
||||
tokio::time::delay_for(time).await;
|
||||
tokio::time::sleep(time).await;
|
||||
actix_rt::Arbiter::current().stop();
|
||||
});
|
||||
});
|
||||
@@ -64,7 +50,7 @@ fn join_another_arbiter() {
|
||||
actix_rt::System::new("test_join_another_arbiter").block_on(async move {
|
||||
let mut arbiter = actix_rt::Arbiter::new();
|
||||
arbiter.send(Box::pin(async move {
|
||||
tokio::time::delay_for(time).await;
|
||||
tokio::time::sleep(time).await;
|
||||
actix_rt::Arbiter::current().stop();
|
||||
}));
|
||||
arbiter.stop();
|
||||
@@ -76,39 +62,65 @@ fn join_another_arbiter() {
|
||||
);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn join_current_arbiter() {
|
||||
// let time = Duration::from_secs(2);
|
||||
//
|
||||
// let instant = Instant::now();
|
||||
// actix_rt::System::new("test_join_current_arbiter").block_on(async move {
|
||||
// actix_rt::spawn(async move {
|
||||
// tokio::time::delay_for(time).await;
|
||||
// actix_rt::Arbiter::current().stop();
|
||||
// });
|
||||
// actix_rt::Arbiter::local_join().await;
|
||||
// });
|
||||
// assert!(
|
||||
// instant.elapsed() >= time,
|
||||
// "Join on current arbiter should wait for all spawned futures"
|
||||
// );
|
||||
//
|
||||
// let large_timer = Duration::from_secs(20);
|
||||
// let instant = Instant::now();
|
||||
// actix_rt::System::new("test_join_current_arbiter").block_on(async move {
|
||||
// actix_rt::spawn(async move {
|
||||
// tokio::time::delay_for(time).await;
|
||||
// actix_rt::Arbiter::current().stop();
|
||||
// });
|
||||
// let f = actix_rt::Arbiter::local_join();
|
||||
// actix_rt::spawn(async move {
|
||||
// tokio::time::delay_for(large_timer).await;
|
||||
// actix_rt::Arbiter::current().stop();
|
||||
// });
|
||||
// f.await;
|
||||
// });
|
||||
// assert!(
|
||||
// instant.elapsed() < large_timer,
|
||||
// "local_join should await only for the already spawned futures"
|
||||
// );
|
||||
// }
|
||||
|
||||
#[test]
|
||||
fn join_current_arbiter() {
|
||||
let time = Duration::from_secs(2);
|
||||
fn non_static_block_on() {
|
||||
let string = String::from("test_str");
|
||||
let str = string.as_str();
|
||||
|
||||
let instant = Instant::now();
|
||||
actix_rt::System::new("test_join_current_arbiter").block_on(async move {
|
||||
actix_rt::spawn(async move {
|
||||
tokio::time::delay_for(time).await;
|
||||
actix_rt::Arbiter::current().stop();
|
||||
});
|
||||
actix_rt::Arbiter::local_join().await;
|
||||
});
|
||||
assert!(
|
||||
instant.elapsed() >= time,
|
||||
"Join on current arbiter should wait for all spawned futures"
|
||||
);
|
||||
let sys = actix_rt::System::new("borrow some");
|
||||
|
||||
let large_timer = Duration::from_secs(20);
|
||||
let instant = Instant::now();
|
||||
actix_rt::System::new("test_join_current_arbiter").block_on(async move {
|
||||
actix_rt::spawn(async move {
|
||||
tokio::time::delay_for(time).await;
|
||||
actix_rt::Arbiter::current().stop();
|
||||
});
|
||||
let f = actix_rt::Arbiter::local_join();
|
||||
actix_rt::spawn(async move {
|
||||
tokio::time::delay_for(large_timer).await;
|
||||
actix_rt::Arbiter::current().stop();
|
||||
});
|
||||
f.await;
|
||||
sys.block_on(async {
|
||||
actix_rt::time::sleep(Duration::from_millis(1)).await;
|
||||
assert_eq!("test_str", str);
|
||||
});
|
||||
assert!(
|
||||
instant.elapsed() < large_timer,
|
||||
"local_join should await only for the already spawned futures"
|
||||
);
|
||||
|
||||
let rt = actix_rt::Runtime::new().unwrap();
|
||||
|
||||
rt.block_on(async {
|
||||
actix_rt::time::sleep(Duration::from_millis(1)).await;
|
||||
assert_eq!("test_str", str);
|
||||
});
|
||||
|
||||
actix_rt::System::run(|| {
|
||||
assert_eq!("test_str", str);
|
||||
actix_rt::System::current().stop();
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
@@ -2,6 +2,10 @@
|
||||
|
||||
## Unreleased - 2020-xx-xx
|
||||
* Added explicit info log message on accept queue pause. [#215]
|
||||
* Prevent double registration of sockets when back-pressure is resolved. [#223]
|
||||
|
||||
[#215]: https://github.com/actix/actix-net/pull/215
|
||||
[#223]: https://github.com/actix/actix-net/pull/223
|
||||
|
||||
|
||||
## 1.0.4 - 2020-09-12
|
||||
|
@@ -370,6 +370,11 @@ impl Accept {
|
||||
if !on {
|
||||
self.backpressure = false;
|
||||
for (token, info) in self.sockets.iter() {
|
||||
if info.timeout.is_some() {
|
||||
// socket will attempt to re-register itself when its timeout completes
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Err(err) = self.register(token, info) {
|
||||
error!("Can not resume socket accept process: {}", err);
|
||||
} else {
|
||||
|
@@ -286,7 +286,7 @@ impl ServerBuilder {
|
||||
|
||||
// handle signals
|
||||
if !self.no_signals {
|
||||
Signals::start(self.server.clone()).unwrap();
|
||||
Signals::start(self.server.clone());
|
||||
}
|
||||
|
||||
// start http server actor
|
||||
|
@@ -2,16 +2,18 @@ use std::collections::HashMap;
|
||||
use std::{fmt, io, net};
|
||||
|
||||
use actix_rt::net::TcpStream;
|
||||
use actix_service as actix;
|
||||
use actix_service::{
|
||||
fn_service, IntoServiceFactory as IntoBaseServiceFactory,
|
||||
ServiceFactory as BaseServiceFactory,
|
||||
};
|
||||
use actix_utils::counter::CounterGuard;
|
||||
use futures_util::future::{ok, Future, FutureExt, LocalBoxFuture};
|
||||
use log::error;
|
||||
|
||||
use super::builder::bind_addr;
|
||||
use super::service::{
|
||||
BoxedServerService, InternalServiceFactory, ServerMessage, StreamService,
|
||||
};
|
||||
use super::service::{BoxedServerService, InternalServiceFactory, StreamService};
|
||||
use super::Token;
|
||||
use crate::socket::StdStream;
|
||||
|
||||
pub struct ServiceConfig {
|
||||
pub(crate) services: Vec<(String, net::TcpListener)>,
|
||||
@@ -142,12 +144,10 @@ impl InternalServiceFactory for ConfiguredService {
|
||||
let name = names.remove(&token).unwrap().0;
|
||||
res.push((
|
||||
token,
|
||||
Box::new(StreamService::new(actix::fn_service(
|
||||
move |_: TcpStream| {
|
||||
error!("Service {:?} is not configured", name);
|
||||
ok::<_, ()>(())
|
||||
},
|
||||
))),
|
||||
Box::new(StreamService::new(fn_service(move |_: TcpStream| {
|
||||
error!("Service {:?} is not configured", name);
|
||||
ok::<_, ()>(())
|
||||
}))),
|
||||
));
|
||||
};
|
||||
}
|
||||
@@ -209,8 +209,8 @@ impl ServiceRuntime {
|
||||
/// *ServiceConfig::bind()* or *ServiceConfig::listen()* methods.
|
||||
pub fn service<T, F>(&mut self, name: &str, service: F)
|
||||
where
|
||||
F: actix::IntoServiceFactory<T>,
|
||||
T: actix::ServiceFactory<Config = (), Request = TcpStream> + 'static,
|
||||
F: IntoBaseServiceFactory<T, TcpStream>,
|
||||
T: BaseServiceFactory<TcpStream, Config = ()> + 'static,
|
||||
T::Future: 'static,
|
||||
T::Service: 'static,
|
||||
T::InitError: fmt::Debug,
|
||||
@@ -238,8 +238,8 @@ impl ServiceRuntime {
|
||||
}
|
||||
|
||||
type BoxedNewService = Box<
|
||||
dyn actix::ServiceFactory<
|
||||
Request = (Option<CounterGuard>, ServerMessage),
|
||||
dyn BaseServiceFactory<
|
||||
(Option<CounterGuard>, StdStream),
|
||||
Response = (),
|
||||
Error = (),
|
||||
InitError = (),
|
||||
@@ -253,20 +253,19 @@ struct ServiceFactory<T> {
|
||||
inner: T,
|
||||
}
|
||||
|
||||
impl<T> actix::ServiceFactory for ServiceFactory<T>
|
||||
impl<T> BaseServiceFactory<(Option<CounterGuard>, StdStream)> for ServiceFactory<T>
|
||||
where
|
||||
T: actix::ServiceFactory<Config = (), Request = TcpStream>,
|
||||
T: BaseServiceFactory<TcpStream, Config = ()>,
|
||||
T::Future: 'static,
|
||||
T::Service: 'static,
|
||||
T::Error: 'static,
|
||||
T::InitError: fmt::Debug + 'static,
|
||||
{
|
||||
type Request = (Option<CounterGuard>, ServerMessage);
|
||||
type Response = ();
|
||||
type Error = ();
|
||||
type InitError = ();
|
||||
type Config = ();
|
||||
type Service = BoxedServerService;
|
||||
type InitError = ();
|
||||
type Future = LocalBoxFuture<'static, Result<BoxedServerService, ()>>;
|
||||
|
||||
fn new_service(&self, _: ()) -> Self::Future {
|
||||
|
@@ -1,6 +1,8 @@
|
||||
//! General purpose TCP server.
|
||||
|
||||
#![deny(rust_2018_idioms)]
|
||||
#![deny(rust_2018_idioms, nonstandard_style)]
|
||||
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
||||
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
||||
|
||||
mod accept;
|
||||
mod builder;
|
||||
|
@@ -1,10 +1,9 @@
|
||||
use std::marker::PhantomData;
|
||||
use std::net::SocketAddr;
|
||||
use std::task::{Context, Poll};
|
||||
use std::time::Duration;
|
||||
|
||||
use actix_rt::spawn;
|
||||
use actix_service::{self as actix, Service, ServiceFactory as ActixServiceFactory};
|
||||
use actix_service::{Service, ServiceFactory as BaseServiceFactory};
|
||||
use actix_utils::counter::CounterGuard;
|
||||
use futures_util::future::{err, ok, LocalBoxFuture, Ready};
|
||||
use futures_util::{FutureExt, TryFutureExt};
|
||||
@@ -13,20 +12,8 @@ use log::error;
|
||||
use super::Token;
|
||||
use crate::socket::{FromStream, StdStream};
|
||||
|
||||
/// Server message
|
||||
pub(crate) enum ServerMessage {
|
||||
/// New stream
|
||||
Connect(StdStream),
|
||||
|
||||
/// Gracefully shutdown
|
||||
Shutdown(Duration),
|
||||
|
||||
/// Force shutdown
|
||||
ForceShutdown,
|
||||
}
|
||||
|
||||
pub trait ServiceFactory<Stream: FromStream>: Send + Clone + 'static {
|
||||
type Factory: actix::ServiceFactory<Config = (), Request = Stream>;
|
||||
type Factory: BaseServiceFactory<Stream, Config = ()>;
|
||||
|
||||
fn create(&self) -> Self::Factory;
|
||||
}
|
||||
@@ -41,31 +28,34 @@ pub(crate) trait InternalServiceFactory: Send {
|
||||
|
||||
pub(crate) type BoxedServerService = Box<
|
||||
dyn Service<
|
||||
Request = (Option<CounterGuard>, ServerMessage),
|
||||
(Option<CounterGuard>, StdStream),
|
||||
Response = (),
|
||||
Error = (),
|
||||
Future = Ready<Result<(), ()>>,
|
||||
>,
|
||||
>;
|
||||
|
||||
pub(crate) struct StreamService<T> {
|
||||
service: T,
|
||||
pub(crate) struct StreamService<S, I> {
|
||||
service: S,
|
||||
_phantom: PhantomData<I>,
|
||||
}
|
||||
|
||||
impl<T> StreamService<T> {
|
||||
pub(crate) fn new(service: T) -> Self {
|
||||
StreamService { service }
|
||||
impl<S, I> StreamService<S, I> {
|
||||
pub(crate) fn new(service: S) -> Self {
|
||||
StreamService {
|
||||
service,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, I> Service for StreamService<T>
|
||||
impl<S, I> Service<(Option<CounterGuard>, StdStream)> for StreamService<S, I>
|
||||
where
|
||||
T: Service<Request = I>,
|
||||
T::Future: 'static,
|
||||
T::Error: 'static,
|
||||
S: Service<I>,
|
||||
S::Future: 'static,
|
||||
S::Error: 'static,
|
||||
I: FromStream,
|
||||
{
|
||||
type Request = (Option<CounterGuard>, ServerMessage);
|
||||
type Response = ();
|
||||
type Error = ();
|
||||
type Future = Ready<Result<(), ()>>;
|
||||
@@ -74,25 +64,20 @@ where
|
||||
self.service.poll_ready(ctx).map_err(|_| ())
|
||||
}
|
||||
|
||||
fn call(&mut self, (guard, req): (Option<CounterGuard>, ServerMessage)) -> Self::Future {
|
||||
match req {
|
||||
ServerMessage::Connect(stream) => {
|
||||
let stream = FromStream::from_stdstream(stream).map_err(|e| {
|
||||
error!("Can not convert to an async tcp stream: {}", e);
|
||||
fn call(&mut self, (guard, req): (Option<CounterGuard>, StdStream)) -> Self::Future {
|
||||
match FromStream::from_stdstream(req) {
|
||||
Ok(stream) => {
|
||||
let f = self.service.call(stream);
|
||||
spawn(async move {
|
||||
let _ = f.await;
|
||||
drop(guard);
|
||||
});
|
||||
|
||||
if let Ok(stream) = stream {
|
||||
let f = self.service.call(stream);
|
||||
spawn(async move {
|
||||
let _ = f.await;
|
||||
drop(guard);
|
||||
});
|
||||
ok(())
|
||||
} else {
|
||||
err(())
|
||||
}
|
||||
ok(())
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Can not convert to an async tcp stream: {}", e);
|
||||
err(())
|
||||
}
|
||||
_ => ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -159,24 +144,10 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl InternalServiceFactory for Box<dyn InternalServiceFactory> {
|
||||
fn name(&self, token: Token) -> &str {
|
||||
self.as_ref().name(token)
|
||||
}
|
||||
|
||||
fn clone_factory(&self) -> Box<dyn InternalServiceFactory> {
|
||||
self.as_ref().clone_factory()
|
||||
}
|
||||
|
||||
fn create(&self) -> LocalBoxFuture<'static, Result<Vec<(Token, BoxedServerService)>, ()>> {
|
||||
self.as_ref().create()
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, T, I> ServiceFactory<I> for F
|
||||
where
|
||||
F: Fn() -> T + Send + Clone + 'static,
|
||||
T: actix::ServiceFactory<Config = (), Request = I>,
|
||||
T: BaseServiceFactory<I, Config = ()>,
|
||||
I: FromStream,
|
||||
{
|
||||
type Factory = T;
|
||||
|
@@ -1,5 +1,4 @@
|
||||
use std::future::Future;
|
||||
use std::io;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
@@ -24,13 +23,13 @@ pub(crate) enum Signal {
|
||||
pub(crate) struct Signals {
|
||||
srv: Server,
|
||||
#[cfg(not(unix))]
|
||||
stream: Pin<Box<dyn Future<Output = io::Result<()>>>>,
|
||||
stream: Pin<Box<dyn Future<Output = std::io::Result<()>>>>,
|
||||
#[cfg(unix)]
|
||||
streams: Vec<(Signal, actix_rt::signal::unix::Signal)>,
|
||||
}
|
||||
|
||||
impl Signals {
|
||||
pub(crate) fn start(srv: Server) -> io::Result<()> {
|
||||
pub(crate) fn start(srv: Server) {
|
||||
actix_rt::spawn(lazy(|_| {
|
||||
#[cfg(not(unix))]
|
||||
{
|
||||
@@ -66,8 +65,6 @@ impl Signals {
|
||||
actix_rt::spawn(Signals { srv, streams })
|
||||
}
|
||||
}));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -14,7 +14,7 @@ use futures_util::{future::Future, stream::Stream, FutureExt, TryFutureExt};
|
||||
use log::{error, info, trace};
|
||||
|
||||
use crate::accept::AcceptNotify;
|
||||
use crate::service::{BoxedServerService, InternalServiceFactory, ServerMessage};
|
||||
use crate::service::{BoxedServerService, InternalServiceFactory};
|
||||
use crate::socket::{SocketAddr, StdStream};
|
||||
use crate::Token;
|
||||
|
||||
@@ -228,23 +228,12 @@ impl Worker {
|
||||
self.services.iter_mut().for_each(|srv| {
|
||||
if srv.status == WorkerServiceStatus::Available {
|
||||
srv.status = WorkerServiceStatus::Stopped;
|
||||
actix_rt::spawn(
|
||||
srv.service
|
||||
.call((None, ServerMessage::ForceShutdown))
|
||||
.map(|_| ()),
|
||||
);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
let timeout = self.shutdown_timeout;
|
||||
self.services.iter_mut().for_each(move |srv| {
|
||||
if srv.status == WorkerServiceStatus::Available {
|
||||
srv.status = WorkerServiceStatus::Stopping;
|
||||
actix_rt::spawn(
|
||||
srv.service
|
||||
.call((None, ServerMessage::Shutdown(timeout)))
|
||||
.map(|_| ()),
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -361,7 +350,7 @@ impl Future for Worker {
|
||||
let guard = self.conns.get();
|
||||
let _ = self.services[conn.token.0]
|
||||
.service
|
||||
.call((Some(guard), ServerMessage::Connect(conn.io)));
|
||||
.call((Some(guard), conn.io));
|
||||
} else {
|
||||
self.state = WorkerState::Available;
|
||||
self.availability.set(true);
|
||||
@@ -455,7 +444,7 @@ impl Future for Worker {
|
||||
let guard = self.conns.get();
|
||||
let _ = self.services[msg.token.0]
|
||||
.service
|
||||
.call((Some(guard), ServerMessage::Connect(msg.io)));
|
||||
.call((Some(guard), msg.io));
|
||||
continue;
|
||||
}
|
||||
Ok(false) => {
|
||||
|
@@ -22,13 +22,16 @@ fn test_bind() {
|
||||
let (tx, rx) = mpsc::channel();
|
||||
|
||||
let h = thread::spawn(move || {
|
||||
let sys = actix_rt::System::new("test");
|
||||
let srv = Server::build()
|
||||
.workers(1)
|
||||
.disable_signals()
|
||||
.bind("test", addr, move || fn_service(|_| ok::<_, ()>(())))
|
||||
.unwrap()
|
||||
.start();
|
||||
let mut sys = actix_rt::System::new("test");
|
||||
|
||||
let srv = sys.block_on(lazy(|_| {
|
||||
Server::build()
|
||||
.workers(1)
|
||||
.disable_signals()
|
||||
.bind("test", addr, move || fn_service(|_| ok::<_, ()>(())))
|
||||
.unwrap()
|
||||
.start()
|
||||
}));
|
||||
let _ = tx.send((srv, actix_rt::System::current()));
|
||||
let _ = sys.run();
|
||||
});
|
||||
@@ -46,14 +49,16 @@ fn test_listen() {
|
||||
let (tx, rx) = mpsc::channel();
|
||||
|
||||
let h = thread::spawn(move || {
|
||||
let sys = actix_rt::System::new("test");
|
||||
let mut sys = actix_rt::System::new("test");
|
||||
let lst = net::TcpListener::bind(addr).unwrap();
|
||||
Server::build()
|
||||
.disable_signals()
|
||||
.workers(1)
|
||||
.listen("test", lst, move || fn_service(|_| ok::<_, ()>(())))
|
||||
.unwrap()
|
||||
.start();
|
||||
sys.block_on(lazy(|_| {
|
||||
Server::build()
|
||||
.disable_signals()
|
||||
.workers(1)
|
||||
.listen("test", lst, move || fn_service(|_| ok::<_, ()>(())))
|
||||
.unwrap()
|
||||
.start()
|
||||
}));
|
||||
let _ = tx.send(actix_rt::System::current());
|
||||
let _ = sys.run();
|
||||
});
|
||||
@@ -78,19 +83,21 @@ fn test_start() {
|
||||
let (tx, rx) = mpsc::channel();
|
||||
|
||||
let h = thread::spawn(move || {
|
||||
let sys = actix_rt::System::new("test");
|
||||
let srv: Server = Server::build()
|
||||
.backlog(100)
|
||||
.disable_signals()
|
||||
.bind("test", addr, move || {
|
||||
fn_service(|io: TcpStream| async move {
|
||||
let mut f = Framed::new(io, BytesCodec);
|
||||
f.send(Bytes::from_static(b"test")).await.unwrap();
|
||||
Ok::<_, ()>(())
|
||||
let mut sys = actix_rt::System::new("test");
|
||||
let srv = sys.block_on(lazy(|_| {
|
||||
Server::build()
|
||||
.backlog(100)
|
||||
.disable_signals()
|
||||
.bind("test", addr, move || {
|
||||
fn_service(|io: TcpStream| async move {
|
||||
let mut f = Framed::new(io, BytesCodec);
|
||||
f.send(Bytes::from_static(b"test")).await.unwrap();
|
||||
Ok::<_, ()>(())
|
||||
})
|
||||
})
|
||||
})
|
||||
.unwrap()
|
||||
.start();
|
||||
.unwrap()
|
||||
.start()
|
||||
}));
|
||||
|
||||
let _ = tx.send((srv, actix_rt::System::current()));
|
||||
let _ = sys.run();
|
||||
@@ -144,29 +151,31 @@ fn test_configure() {
|
||||
|
||||
let h = thread::spawn(move || {
|
||||
let num = num2.clone();
|
||||
let sys = actix_rt::System::new("test");
|
||||
let srv = Server::build()
|
||||
.disable_signals()
|
||||
.configure(move |cfg| {
|
||||
let num = num.clone();
|
||||
let lst = net::TcpListener::bind(addr3).unwrap();
|
||||
cfg.bind("addr1", addr1)
|
||||
.unwrap()
|
||||
.bind("addr2", addr2)
|
||||
.unwrap()
|
||||
.listen("addr3", lst)
|
||||
.apply(move |rt| {
|
||||
let num = num.clone();
|
||||
rt.service("addr1", fn_service(|_| ok::<_, ()>(())));
|
||||
rt.service("addr3", fn_service(|_| ok::<_, ()>(())));
|
||||
rt.on_start(lazy(move |_| {
|
||||
let _ = num.fetch_add(1, Relaxed);
|
||||
}))
|
||||
})
|
||||
})
|
||||
.unwrap()
|
||||
.workers(1)
|
||||
.start();
|
||||
let mut sys = actix_rt::System::new("test");
|
||||
let srv = sys.block_on(lazy(|_| {
|
||||
Server::build()
|
||||
.disable_signals()
|
||||
.configure(move |cfg| {
|
||||
let num = num.clone();
|
||||
let lst = net::TcpListener::bind(addr3).unwrap();
|
||||
cfg.bind("addr1", addr1)
|
||||
.unwrap()
|
||||
.bind("addr2", addr2)
|
||||
.unwrap()
|
||||
.listen("addr3", lst)
|
||||
.apply(move |rt| {
|
||||
let num = num.clone();
|
||||
rt.service("addr1", fn_service(|_| ok::<_, ()>(())));
|
||||
rt.service("addr3", fn_service(|_| ok::<_, ()>(())));
|
||||
rt.on_start(lazy(move |_| {
|
||||
let _ = num.fetch_add(1, Relaxed);
|
||||
}))
|
||||
})
|
||||
})
|
||||
.unwrap()
|
||||
.workers(1)
|
||||
.start()
|
||||
}));
|
||||
let _ = tx.send((srv, actix_rt::System::current()));
|
||||
let _ = sys.run();
|
||||
});
|
||||
|
@@ -1,13 +1,31 @@
|
||||
# Changes
|
||||
|
||||
## Unreleased - 2020-xx-xx
|
||||
* Upgrade `pin-project` to `1.0`.
|
||||
|
||||
|
||||
## 2.0.0-beta.1 - 2020-12-28
|
||||
* `Service`, other traits, and many type signatures now take the the request type as a type
|
||||
parameter instead of an associated type. [#232]
|
||||
* Add `always_ready!` and `forward_ready!` macros. [#233]
|
||||
* Crate is now `no_std`. [#233]
|
||||
* Migrate pin projections to `pin-project-lite`. [#233]
|
||||
* Remove `AndThenApplyFn` and Pipeline `and_then_apply_fn`. Use the
|
||||
`.and_then(apply_fn(...))` construction. [#233]
|
||||
* Move non-vital methods to `ServiceExt` and `ServiceFactoryExt` extension traits. [#235]
|
||||
|
||||
[#232]: https://github.com/actix/actix-net/pull/232
|
||||
[#233]: https://github.com/actix/actix-net/pull/233
|
||||
[#235]: https://github.com/actix/actix-net/pull/235
|
||||
|
||||
|
||||
## 1.0.6 - 2020-08-09
|
||||
|
||||
### Fixed
|
||||
|
||||
* Removed unsound custom Cell implementation that allowed obtaining several mutable references to the same data, which is undefined behavior in Rust and could lead to violations of memory safety. External code could obtain several mutable references to the same data through service combinators. Attempts to acquire several mutable references to the same data will instead result in a panic.
|
||||
* Removed unsound custom Cell implementation that allowed obtaining several mutable references to
|
||||
the same data, which is undefined behavior in Rust and could lead to violations of memory safety. External code could obtain several mutable references to the same data through
|
||||
service combinators. Attempts to acquire several mutable references to the same data will instead
|
||||
result in a panic.
|
||||
|
||||
## [1.0.5] - 2020-01-16
|
||||
|
||||
|
@@ -1,7 +1,10 @@
|
||||
[package]
|
||||
name = "actix-service"
|
||||
version = "1.0.6"
|
||||
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
||||
version = "2.0.0-beta.1"
|
||||
authors = [
|
||||
"Nikolay Kim <fafhrd91@gmail.com>",
|
||||
"Rob Ede <robjtede@icloud.com>",
|
||||
]
|
||||
description = "Service trait and combinators for representing asynchronous request/response operations."
|
||||
keywords = ["network", "framework", "async", "futures", "service"]
|
||||
homepage = "https://actix.rs"
|
||||
@@ -17,17 +20,9 @@ name = "actix_service"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
futures-util = "0.3.1"
|
||||
pin-project = "1.0.0"
|
||||
futures-core = { version = "0.3.7", default-features = false }
|
||||
pin-project-lite = "0.2"
|
||||
|
||||
[dev-dependencies]
|
||||
actix-rt = "1.0.0"
|
||||
criterion = "0.3"
|
||||
|
||||
[[bench]]
|
||||
name = "unsafecell_vs_refcell"
|
||||
harness = false
|
||||
|
||||
[[bench]]
|
||||
name = "and_then"
|
||||
harness = false
|
||||
futures-util = { version = "0.3.7", default-features = false }
|
||||
|
@@ -1,332 +0,0 @@
|
||||
use actix_service::boxed::BoxFuture;
|
||||
use actix_service::IntoService;
|
||||
use actix_service::Service;
|
||||
/// Benchmark various implementations of and_then
|
||||
use criterion::{criterion_main, Criterion};
|
||||
use futures_util::future::join_all;
|
||||
use futures_util::future::TryFutureExt;
|
||||
use std::cell::{RefCell, UnsafeCell};
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use std::rc::Rc;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
/*
|
||||
* Test services A,B for AndThen service implementations
|
||||
*/
|
||||
|
||||
async fn svc1(_: ()) -> Result<usize, ()> {
|
||||
Ok(1)
|
||||
}
|
||||
|
||||
async fn svc2(req: usize) -> Result<usize, ()> {
|
||||
Ok(req + 1)
|
||||
}
|
||||
|
||||
/*
|
||||
* AndThenUC - original AndThen service based on UnsafeCell
|
||||
* Cut down version of actix_service::AndThenService based on actix-service::Cell
|
||||
*/
|
||||
|
||||
struct AndThenUC<A, B>(Rc<UnsafeCell<(A, B)>>);
|
||||
|
||||
impl<A, B> AndThenUC<A, B> {
|
||||
fn new(a: A, b: B) -> Self
|
||||
where
|
||||
A: Service,
|
||||
B: Service<Request = A::Response, Error = A::Error>,
|
||||
{
|
||||
Self(Rc::new(UnsafeCell::new((a, b))))
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B> Clone for AndThenUC<A, B> {
|
||||
fn clone(&self) -> Self {
|
||||
Self(self.0.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B> Service for AndThenUC<A, B>
|
||||
where
|
||||
A: Service,
|
||||
B: Service<Request = A::Response, Error = A::Error>,
|
||||
{
|
||||
type Request = A::Request;
|
||||
type Response = B::Response;
|
||||
type Error = A::Error;
|
||||
type Future = AndThenServiceResponse<A, B>;
|
||||
|
||||
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
fn call(&mut self, req: A::Request) -> Self::Future {
|
||||
let fut = unsafe { &mut *(*self.0).get() }.0.call(req);
|
||||
AndThenServiceResponse {
|
||||
state: State::A(fut, Some(self.0.clone())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[pin_project::pin_project]
|
||||
pub(crate) struct AndThenServiceResponse<A, B>
|
||||
where
|
||||
A: Service,
|
||||
B: Service<Request = A::Response, Error = A::Error>,
|
||||
{
|
||||
#[pin]
|
||||
state: State<A, B>,
|
||||
}
|
||||
|
||||
#[pin_project::pin_project(project = StateProj)]
|
||||
enum State<A, B>
|
||||
where
|
||||
A: Service,
|
||||
B: Service<Request = A::Response, Error = A::Error>,
|
||||
{
|
||||
A(#[pin] A::Future, Option<Rc<UnsafeCell<(A, B)>>>),
|
||||
B(#[pin] B::Future),
|
||||
Empty,
|
||||
}
|
||||
|
||||
impl<A, B> Future for AndThenServiceResponse<A, B>
|
||||
where
|
||||
A: Service,
|
||||
B: Service<Request = A::Response, Error = A::Error>,
|
||||
{
|
||||
type Output = Result<B::Response, A::Error>;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let mut this = self.as_mut().project();
|
||||
|
||||
match this.state.as_mut().project() {
|
||||
StateProj::A(fut, b) => match fut.poll(cx)? {
|
||||
Poll::Ready(res) => {
|
||||
let b = b.take().unwrap();
|
||||
this.state.set(State::Empty); // drop fut A
|
||||
let fut = unsafe { &mut (*b.get()).1 }.call(res);
|
||||
this.state.set(State::B(fut));
|
||||
self.poll(cx)
|
||||
}
|
||||
Poll::Pending => Poll::Pending,
|
||||
},
|
||||
StateProj::B(fut) => fut.poll(cx).map(|r| {
|
||||
this.state.set(State::Empty);
|
||||
r
|
||||
}),
|
||||
StateProj::Empty => {
|
||||
panic!("future must not be polled after it returned `Poll::Ready`")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* AndThenRC - AndThen service based on RefCell
|
||||
*/
|
||||
|
||||
struct AndThenRC<A, B>(Rc<RefCell<(A, B)>>);
|
||||
|
||||
impl<A, B> AndThenRC<A, B> {
|
||||
fn new(a: A, b: B) -> Self
|
||||
where
|
||||
A: Service,
|
||||
B: Service<Request = A::Response, Error = A::Error>,
|
||||
{
|
||||
Self(Rc::new(RefCell::new((a, b))))
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B> Clone for AndThenRC<A, B> {
|
||||
fn clone(&self) -> Self {
|
||||
Self(self.0.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B> Service for AndThenRC<A, B>
|
||||
where
|
||||
A: Service,
|
||||
B: Service<Request = A::Response, Error = A::Error>,
|
||||
{
|
||||
type Request = A::Request;
|
||||
type Response = B::Response;
|
||||
type Error = A::Error;
|
||||
type Future = AndThenServiceResponseRC<A, B>;
|
||||
|
||||
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
fn call(&mut self, req: A::Request) -> Self::Future {
|
||||
let fut = self.0.borrow_mut().0.call(req);
|
||||
AndThenServiceResponseRC {
|
||||
state: StateRC::A(fut, Some(self.0.clone())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[pin_project::pin_project]
|
||||
pub(crate) struct AndThenServiceResponseRC<A, B>
|
||||
where
|
||||
A: Service,
|
||||
B: Service<Request = A::Response, Error = A::Error>,
|
||||
{
|
||||
#[pin]
|
||||
state: StateRC<A, B>,
|
||||
}
|
||||
|
||||
#[pin_project::pin_project(project = StateRCProj)]
|
||||
enum StateRC<A, B>
|
||||
where
|
||||
A: Service,
|
||||
B: Service<Request = A::Response, Error = A::Error>,
|
||||
{
|
||||
A(#[pin] A::Future, Option<Rc<RefCell<(A, B)>>>),
|
||||
B(#[pin] B::Future),
|
||||
Empty,
|
||||
}
|
||||
|
||||
impl<A, B> Future for AndThenServiceResponseRC<A, B>
|
||||
where
|
||||
A: Service,
|
||||
B: Service<Request = A::Response, Error = A::Error>,
|
||||
{
|
||||
type Output = Result<B::Response, A::Error>;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let mut this = self.as_mut().project();
|
||||
|
||||
match this.state.as_mut().project() {
|
||||
StateRCProj::A(fut, b) => match fut.poll(cx)? {
|
||||
Poll::Ready(res) => {
|
||||
let b = b.take().unwrap();
|
||||
this.state.set(StateRC::Empty); // drop fut A
|
||||
let fut = b.borrow_mut().1.call(res);
|
||||
this.state.set(StateRC::B(fut));
|
||||
self.poll(cx)
|
||||
}
|
||||
Poll::Pending => Poll::Pending,
|
||||
},
|
||||
StateRCProj::B(fut) => fut.poll(cx).map(|r| {
|
||||
this.state.set(StateRC::Empty);
|
||||
r
|
||||
}),
|
||||
StateRCProj::Empty => {
|
||||
panic!("future must not be polled after it returned `Poll::Ready`")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* AndThenRCFuture - AndThen service based on RefCell
|
||||
* and standard futures::future::and_then combinator in a Box
|
||||
*/
|
||||
|
||||
struct AndThenRCFuture<A, B>(Rc<RefCell<(A, B)>>);
|
||||
|
||||
impl<A, B> AndThenRCFuture<A, B> {
|
||||
fn new(a: A, b: B) -> Self
|
||||
where
|
||||
A: Service,
|
||||
B: Service<Request = A::Response, Error = A::Error>,
|
||||
{
|
||||
Self(Rc::new(RefCell::new((a, b))))
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B> Clone for AndThenRCFuture<A, B> {
|
||||
fn clone(&self) -> Self {
|
||||
Self(self.0.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B> Service for AndThenRCFuture<A, B>
|
||||
where
|
||||
A: Service + 'static,
|
||||
A::Future: 'static,
|
||||
B: Service<Request = A::Response, Error = A::Error> + 'static,
|
||||
B::Future: 'static,
|
||||
{
|
||||
type Request = A::Request;
|
||||
type Response = B::Response;
|
||||
type Error = A::Error;
|
||||
type Future = BoxFuture<Self::Response, Self::Error>;
|
||||
|
||||
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
fn call(&mut self, req: A::Request) -> Self::Future {
|
||||
let fut = self.0.borrow_mut().0.call(req);
|
||||
let core = self.0.clone();
|
||||
let fut2 = move |res| (*core).borrow_mut().1.call(res);
|
||||
Box::pin(fut.and_then(fut2))
|
||||
}
|
||||
}
|
||||
|
||||
/// Criterion Benchmark for async Service
|
||||
/// Should be used from within criterion group:
|
||||
/// ```rust,ignore
|
||||
/// let mut criterion: ::criterion::Criterion<_> =
|
||||
/// ::criterion::Criterion::default().configure_from_args();
|
||||
/// bench_async_service(&mut criterion, ok_service(), "async_service_direct");
|
||||
/// ```
|
||||
///
|
||||
/// Usable for benching Service wrappers:
|
||||
/// Using minimum service code implementation we first measure
|
||||
/// time to run minimum service, then measure time with wrapper.
|
||||
///
|
||||
/// Sample output
|
||||
/// async_service_direct time: [1.0908 us 1.1656 us 1.2613 us]
|
||||
pub fn bench_async_service<S>(c: &mut Criterion, srv: S, name: &str)
|
||||
where
|
||||
S: Service<Request = (), Response = usize, Error = ()> + Clone + 'static,
|
||||
{
|
||||
let mut rt = actix_rt::System::new("test");
|
||||
|
||||
// start benchmark loops
|
||||
c.bench_function(name, move |b| {
|
||||
b.iter_custom(|iters| {
|
||||
let mut srvs: Vec<_> = (1..iters).map(|_| srv.clone()).collect();
|
||||
// exclude request generation, it appears it takes significant time vs call (3us vs 1us)
|
||||
let start = std::time::Instant::now();
|
||||
// benchmark body
|
||||
rt.block_on(async move { join_all(srvs.iter_mut().map(|srv| srv.call(()))).await });
|
||||
// check that at least first request succeeded
|
||||
start.elapsed()
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
pub fn service_benches() {
|
||||
let mut criterion: ::criterion::Criterion<_> =
|
||||
::criterion::Criterion::default().configure_from_args();
|
||||
bench_async_service(
|
||||
&mut criterion,
|
||||
AndThenUC::new(svc1.into_service(), svc2.into_service()),
|
||||
"AndThen with UnsafeCell",
|
||||
);
|
||||
bench_async_service(
|
||||
&mut criterion,
|
||||
AndThenRC::new(svc1.into_service(), svc2.into_service()),
|
||||
"AndThen with RefCell",
|
||||
);
|
||||
bench_async_service(
|
||||
&mut criterion,
|
||||
AndThenUC::new(svc1.into_service(), svc2.into_service()),
|
||||
"AndThen with UnsafeCell",
|
||||
);
|
||||
bench_async_service(
|
||||
&mut criterion,
|
||||
AndThenRC::new(svc1.into_service(), svc2.into_service()),
|
||||
"AndThen with RefCell",
|
||||
);
|
||||
bench_async_service(
|
||||
&mut criterion,
|
||||
AndThenRCFuture::new(svc1.into_service(), svc2.into_service()),
|
||||
"AndThen with RefCell via future::and_then",
|
||||
);
|
||||
}
|
||||
|
||||
criterion_main!(service_benches);
|
@@ -1,112 +0,0 @@
|
||||
use actix_service::Service;
|
||||
use criterion::{criterion_main, Criterion};
|
||||
use futures_util::future::join_all;
|
||||
use futures_util::future::{ok, Ready};
|
||||
use std::cell::{RefCell, UnsafeCell};
|
||||
use std::rc::Rc;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
struct SrvUC(Rc<UnsafeCell<usize>>);
|
||||
|
||||
impl Default for SrvUC {
|
||||
fn default() -> Self {
|
||||
Self(Rc::new(UnsafeCell::new(0)))
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for SrvUC {
|
||||
fn clone(&self) -> Self {
|
||||
Self(self.0.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl Service for SrvUC {
|
||||
type Request = ();
|
||||
type Response = usize;
|
||||
type Error = ();
|
||||
type Future = Ready<Result<Self::Response, ()>>;
|
||||
|
||||
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
fn call(&mut self, _: ()) -> Self::Future {
|
||||
unsafe { *(*self.0).get() = *(*self.0).get() + 1 };
|
||||
ok(unsafe { *self.0.get() })
|
||||
}
|
||||
}
|
||||
|
||||
struct SrvRC(Rc<RefCell<usize>>);
|
||||
|
||||
impl Default for SrvRC {
|
||||
fn default() -> Self {
|
||||
Self(Rc::new(RefCell::new(0)))
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for SrvRC {
|
||||
fn clone(&self) -> Self {
|
||||
Self(self.0.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl Service for SrvRC {
|
||||
type Request = ();
|
||||
type Response = usize;
|
||||
type Error = ();
|
||||
type Future = Ready<Result<Self::Response, ()>>;
|
||||
|
||||
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
fn call(&mut self, _: ()) -> Self::Future {
|
||||
let prev = *self.0.borrow();
|
||||
*(*self.0).borrow_mut() = prev + 1;
|
||||
ok(*self.0.borrow())
|
||||
}
|
||||
}
|
||||
|
||||
/// Criterion Benchmark for async Service
|
||||
/// Should be used from within criterion group:
|
||||
/// ```rust,ignore
|
||||
/// let mut criterion: ::criterion::Criterion<_> =
|
||||
/// ::criterion::Criterion::default().configure_from_args();
|
||||
/// bench_async_service(&mut criterion, ok_service(), "async_service_direct");
|
||||
/// ```
|
||||
///
|
||||
/// Usable for benching Service wrappers:
|
||||
/// Using minimum service code implementation we first measure
|
||||
/// time to run minimum service, then measure time with wrapper.
|
||||
///
|
||||
/// Sample output
|
||||
/// async_service_direct time: [1.0908 us 1.1656 us 1.2613 us]
|
||||
pub fn bench_async_service<S>(c: &mut Criterion, srv: S, name: &str)
|
||||
where
|
||||
S: Service<Request = (), Response = usize, Error = ()> + Clone + 'static,
|
||||
{
|
||||
let mut rt = actix_rt::System::new("test");
|
||||
|
||||
// start benchmark loops
|
||||
c.bench_function(name, move |b| {
|
||||
b.iter_custom(|iters| {
|
||||
let mut srvs: Vec<_> = (1..iters).map(|_| srv.clone()).collect();
|
||||
// exclude request generation, it appears it takes significant time vs call (3us vs 1us)
|
||||
let start = std::time::Instant::now();
|
||||
// benchmark body
|
||||
rt.block_on(async move { join_all(srvs.iter_mut().map(|srv| srv.call(()))).await });
|
||||
// check that at least first request succeeded
|
||||
start.elapsed()
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
pub fn service_benches() {
|
||||
let mut criterion: ::criterion::Criterion<_> =
|
||||
::criterion::Criterion::default().configure_from_args();
|
||||
bench_async_service(&mut criterion, SrvUC::default(), "Service with UnsafeCell");
|
||||
bench_async_service(&mut criterion, SrvRC::default(), "Service with RefCell");
|
||||
bench_async_service(&mut criterion, SrvUC::default(), "Service with UnsafeCell");
|
||||
bench_async_service(&mut criterion, SrvRC::default(), "Service with RefCell");
|
||||
}
|
||||
criterion_main!(service_benches);
|
@@ -1,8 +1,13 @@
|
||||
use std::cell::RefCell;
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use std::rc::Rc;
|
||||
use std::task::{Context, Poll};
|
||||
use alloc::rc::Rc;
|
||||
use core::{
|
||||
cell::RefCell,
|
||||
future::Future,
|
||||
marker::PhantomData,
|
||||
pin::Pin,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
|
||||
use pin_project_lite::pin_project;
|
||||
|
||||
use super::{Service, ServiceFactory};
|
||||
|
||||
@@ -10,34 +15,33 @@ use super::{Service, ServiceFactory};
|
||||
/// of another service which completes successfully.
|
||||
///
|
||||
/// This is created by the `Pipeline::and_then` method.
|
||||
pub(crate) struct AndThenService<A, B>(Rc<RefCell<(A, B)>>);
|
||||
pub(crate) struct AndThenService<A, B, Req>(Rc<RefCell<(A, B)>>, PhantomData<Req>);
|
||||
|
||||
impl<A, B> AndThenService<A, B> {
|
||||
impl<A, B, Req> AndThenService<A, B, Req> {
|
||||
/// Create new `AndThen` combinator
|
||||
pub(crate) fn new(a: A, b: B) -> Self
|
||||
where
|
||||
A: Service,
|
||||
B: Service<Request = A::Response, Error = A::Error>,
|
||||
A: Service<Req>,
|
||||
B: Service<A::Response, Error = A::Error>,
|
||||
{
|
||||
Self(Rc::new(RefCell::new((a, b))))
|
||||
Self(Rc::new(RefCell::new((a, b))), PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B> Clone for AndThenService<A, B> {
|
||||
impl<A, B, Req> Clone for AndThenService<A, B, Req> {
|
||||
fn clone(&self) -> Self {
|
||||
AndThenService(self.0.clone())
|
||||
AndThenService(self.0.clone(), PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B> Service for AndThenService<A, B>
|
||||
impl<A, B, Req> Service<Req> for AndThenService<A, B, Req>
|
||||
where
|
||||
A: Service,
|
||||
B: Service<Request = A::Response, Error = A::Error>,
|
||||
A: Service<Req>,
|
||||
B: Service<A::Response, Error = A::Error>,
|
||||
{
|
||||
type Request = A::Request;
|
||||
type Response = B::Response;
|
||||
type Error = A::Error;
|
||||
type Future = AndThenServiceResponse<A, B>;
|
||||
type Future = AndThenServiceResponse<A, B, Req>;
|
||||
|
||||
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
let mut srv = self.0.borrow_mut();
|
||||
@@ -49,38 +53,51 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn call(&mut self, req: A::Request) -> Self::Future {
|
||||
fn call(&mut self, req: Req) -> Self::Future {
|
||||
AndThenServiceResponse {
|
||||
state: State::A(self.0.borrow_mut().0.call(req), Some(self.0.clone())),
|
||||
state: State::A {
|
||||
fut: self.0.borrow_mut().0.call(req),
|
||||
b: Some(self.0.clone()),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[pin_project::pin_project]
|
||||
pub(crate) struct AndThenServiceResponse<A, B>
|
||||
where
|
||||
A: Service,
|
||||
B: Service<Request = A::Response, Error = A::Error>,
|
||||
{
|
||||
#[pin]
|
||||
state: State<A, B>,
|
||||
pin_project! {
|
||||
pub(crate) struct AndThenServiceResponse<A, B, Req>
|
||||
where
|
||||
A: Service<Req>,
|
||||
B: Service<A::Response, Error = A::Error>,
|
||||
{
|
||||
#[pin]
|
||||
state: State<A, B, Req>,
|
||||
}
|
||||
}
|
||||
|
||||
#[pin_project::pin_project(project = StateProj)]
|
||||
enum State<A, B>
|
||||
where
|
||||
A: Service,
|
||||
B: Service<Request = A::Response, Error = A::Error>,
|
||||
{
|
||||
A(#[pin] A::Future, Option<Rc<RefCell<(A, B)>>>),
|
||||
B(#[pin] B::Future),
|
||||
Empty,
|
||||
pin_project! {
|
||||
#[project = StateProj]
|
||||
enum State<A, B, Req>
|
||||
where
|
||||
A: Service<Req>,
|
||||
B: Service<A::Response, Error = A::Error>,
|
||||
{
|
||||
A {
|
||||
#[pin]
|
||||
fut: A::Future,
|
||||
b: Option<Rc<RefCell<(A, B)>>>,
|
||||
},
|
||||
B {
|
||||
#[pin]
|
||||
fut: B::Future,
|
||||
},
|
||||
Empty,
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B> Future for AndThenServiceResponse<A, B>
|
||||
impl<A, B, Req> Future for AndThenServiceResponse<A, B, Req>
|
||||
where
|
||||
A: Service,
|
||||
B: Service<Request = A::Response, Error = A::Error>,
|
||||
A: Service<Req>,
|
||||
B: Service<A::Response, Error = A::Error>,
|
||||
{
|
||||
type Output = Result<B::Response, A::Error>;
|
||||
|
||||
@@ -88,17 +105,17 @@ where
|
||||
let mut this = self.as_mut().project();
|
||||
|
||||
match this.state.as_mut().project() {
|
||||
StateProj::A(fut, b) => match fut.poll(cx)? {
|
||||
StateProj::A { fut, b } => match fut.poll(cx)? {
|
||||
Poll::Ready(res) => {
|
||||
let b = b.take().unwrap();
|
||||
this.state.set(State::Empty); // drop fut A
|
||||
let fut = b.borrow_mut().1.call(res);
|
||||
this.state.set(State::B(fut));
|
||||
this.state.set(State::B { fut });
|
||||
self.poll(cx)
|
||||
}
|
||||
Poll::Pending => Poll::Pending,
|
||||
},
|
||||
StateProj::B(fut) => fut.poll(cx).map(|r| {
|
||||
StateProj::B { fut } => fut.poll(cx).map(|r| {
|
||||
this.state.set(State::Empty);
|
||||
r
|
||||
}),
|
||||
@@ -110,27 +127,28 @@ where
|
||||
}
|
||||
|
||||
/// `.and_then()` service factory combinator
|
||||
pub(crate) struct AndThenServiceFactory<A, B>
|
||||
pub(crate) struct AndThenServiceFactory<A, B, Req>
|
||||
where
|
||||
A: ServiceFactory,
|
||||
A: ServiceFactory<Req>,
|
||||
A::Config: Clone,
|
||||
B: ServiceFactory<
|
||||
A::Response,
|
||||
Config = A::Config,
|
||||
Request = A::Response,
|
||||
Error = A::Error,
|
||||
InitError = A::InitError,
|
||||
>,
|
||||
{
|
||||
inner: Rc<(A, B)>,
|
||||
_phantom: PhantomData<Req>,
|
||||
}
|
||||
|
||||
impl<A, B> AndThenServiceFactory<A, B>
|
||||
impl<A, B, Req> AndThenServiceFactory<A, B, Req>
|
||||
where
|
||||
A: ServiceFactory,
|
||||
A: ServiceFactory<Req>,
|
||||
A::Config: Clone,
|
||||
B: ServiceFactory<
|
||||
A::Response,
|
||||
Config = A::Config,
|
||||
Request = A::Response,
|
||||
Error = A::Error,
|
||||
InitError = A::InitError,
|
||||
>,
|
||||
@@ -139,29 +157,29 @@ where
|
||||
pub(crate) fn new(a: A, b: B) -> Self {
|
||||
Self {
|
||||
inner: Rc::new((a, b)),
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B> ServiceFactory for AndThenServiceFactory<A, B>
|
||||
impl<A, B, Req> ServiceFactory<Req> for AndThenServiceFactory<A, B, Req>
|
||||
where
|
||||
A: ServiceFactory,
|
||||
A: ServiceFactory<Req>,
|
||||
A::Config: Clone,
|
||||
B: ServiceFactory<
|
||||
A::Response,
|
||||
Config = A::Config,
|
||||
Request = A::Response,
|
||||
Error = A::Error,
|
||||
InitError = A::InitError,
|
||||
>,
|
||||
{
|
||||
type Request = A::Request;
|
||||
type Response = B::Response;
|
||||
type Error = A::Error;
|
||||
|
||||
type Config = A::Config;
|
||||
type Service = AndThenService<A::Service, B::Service>;
|
||||
type Service = AndThenService<A::Service, B::Service, Req>;
|
||||
type InitError = A::InitError;
|
||||
type Future = AndThenServiceFactoryResponse<A, B>;
|
||||
type Future = AndThenServiceFactoryResponse<A, B, Req>;
|
||||
|
||||
fn new_service(&self, cfg: A::Config) -> Self::Future {
|
||||
let inner = &*self.inner;
|
||||
@@ -172,13 +190,13 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B> Clone for AndThenServiceFactory<A, B>
|
||||
impl<A, B, Req> Clone for AndThenServiceFactory<A, B, Req>
|
||||
where
|
||||
A: ServiceFactory,
|
||||
A: ServiceFactory<Req>,
|
||||
A::Config: Clone,
|
||||
B: ServiceFactory<
|
||||
A::Response,
|
||||
Config = A::Config,
|
||||
Request = A::Response,
|
||||
Error = A::Error,
|
||||
InitError = A::InitError,
|
||||
>,
|
||||
@@ -186,29 +204,31 @@ where
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
inner: self.inner.clone(),
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[pin_project::pin_project]
|
||||
pub(crate) struct AndThenServiceFactoryResponse<A, B>
|
||||
where
|
||||
A: ServiceFactory,
|
||||
B: ServiceFactory<Request = A::Response>,
|
||||
{
|
||||
#[pin]
|
||||
fut_a: A::Future,
|
||||
#[pin]
|
||||
fut_b: B::Future,
|
||||
pin_project! {
|
||||
pub(crate) struct AndThenServiceFactoryResponse<A, B, Req>
|
||||
where
|
||||
A: ServiceFactory<Req>,
|
||||
B: ServiceFactory<A::Response>,
|
||||
{
|
||||
#[pin]
|
||||
fut_a: A::Future,
|
||||
#[pin]
|
||||
fut_b: B::Future,
|
||||
|
||||
a: Option<A::Service>,
|
||||
b: Option<B::Service>,
|
||||
a: Option<A::Service>,
|
||||
b: Option<B::Service>,
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B> AndThenServiceFactoryResponse<A, B>
|
||||
impl<A, B, Req> AndThenServiceFactoryResponse<A, B, Req>
|
||||
where
|
||||
A: ServiceFactory,
|
||||
B: ServiceFactory<Request = A::Response>,
|
||||
A: ServiceFactory<Req>,
|
||||
B: ServiceFactory<A::Response>,
|
||||
{
|
||||
fn new(fut_a: A::Future, fut_b: B::Future) -> Self {
|
||||
AndThenServiceFactoryResponse {
|
||||
@@ -220,12 +240,12 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B> Future for AndThenServiceFactoryResponse<A, B>
|
||||
impl<A, B, Req> Future for AndThenServiceFactoryResponse<A, B, Req>
|
||||
where
|
||||
A: ServiceFactory,
|
||||
B: ServiceFactory<Request = A::Response, Error = A::Error, InitError = A::InitError>,
|
||||
A: ServiceFactory<Req>,
|
||||
B: ServiceFactory<A::Response, Error = A::Error, InitError = A::InitError>,
|
||||
{
|
||||
type Output = Result<AndThenService<A::Service, B::Service>, A::InitError>;
|
||||
type Output = Result<AndThenService<A::Service, B::Service, Req>, A::InitError>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let this = self.project();
|
||||
@@ -253,18 +273,21 @@ where
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::cell::Cell;
|
||||
use std::rc::Rc;
|
||||
use std::task::{Context, Poll};
|
||||
use alloc::rc::Rc;
|
||||
use core::{
|
||||
cell::Cell,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
|
||||
use futures_util::future::{lazy, ok, ready, Ready};
|
||||
use futures_util::future::lazy;
|
||||
|
||||
use crate::{fn_factory, pipeline, pipeline_factory, Service, ServiceFactory};
|
||||
use crate::{
|
||||
fn_factory, ok, pipeline, pipeline_factory, ready, Ready, Service, ServiceFactory,
|
||||
};
|
||||
|
||||
struct Srv1(Rc<Cell<usize>>);
|
||||
|
||||
impl Service for Srv1 {
|
||||
type Request = &'static str;
|
||||
impl Service<&'static str> for Srv1 {
|
||||
type Response = &'static str;
|
||||
type Error = ();
|
||||
type Future = Ready<Result<Self::Response, ()>>;
|
||||
@@ -282,8 +305,7 @@ mod tests {
|
||||
#[derive(Clone)]
|
||||
struct Srv2(Rc<Cell<usize>>);
|
||||
|
||||
impl Service for Srv2 {
|
||||
type Request = &'static str;
|
||||
impl Service<&'static str> for Srv2 {
|
||||
type Response = (&'static str, &'static str);
|
||||
type Error = ();
|
||||
type Future = Ready<Result<Self::Response, ()>>;
|
||||
|
@@ -1,326 +0,0 @@
|
||||
use std::cell::RefCell;
|
||||
use std::future::Future;
|
||||
use std::marker::PhantomData;
|
||||
use std::pin::Pin;
|
||||
use std::rc::Rc;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
use crate::{Service, ServiceFactory};
|
||||
|
||||
/// `Apply` service combinator
|
||||
pub(crate) struct AndThenApplyFn<A, B, F, Fut, Res, Err>
|
||||
where
|
||||
A: Service,
|
||||
B: Service,
|
||||
F: FnMut(A::Response, &mut B) -> Fut,
|
||||
Fut: Future<Output = Result<Res, Err>>,
|
||||
Err: From<A::Error> + From<B::Error>,
|
||||
{
|
||||
srv: Rc<RefCell<(A, B, F)>>,
|
||||
r: PhantomData<(Fut, Res, Err)>,
|
||||
}
|
||||
|
||||
impl<A, B, F, Fut, Res, Err> AndThenApplyFn<A, B, F, Fut, Res, Err>
|
||||
where
|
||||
A: Service,
|
||||
B: Service,
|
||||
F: FnMut(A::Response, &mut B) -> Fut,
|
||||
Fut: Future<Output = Result<Res, Err>>,
|
||||
Err: From<A::Error> + From<B::Error>,
|
||||
{
|
||||
/// Create new `Apply` combinator
|
||||
pub(crate) fn new(a: A, b: B, f: F) -> Self {
|
||||
Self {
|
||||
srv: Rc::new(RefCell::new((a, b, f))),
|
||||
r: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B, F, Fut, Res, Err> Clone for AndThenApplyFn<A, B, F, Fut, Res, Err>
|
||||
where
|
||||
A: Service,
|
||||
B: Service,
|
||||
F: FnMut(A::Response, &mut B) -> Fut,
|
||||
Fut: Future<Output = Result<Res, Err>>,
|
||||
Err: From<A::Error> + From<B::Error>,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
AndThenApplyFn {
|
||||
srv: self.srv.clone(),
|
||||
r: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B, F, Fut, Res, Err> Service for AndThenApplyFn<A, B, F, Fut, Res, Err>
|
||||
where
|
||||
A: Service,
|
||||
B: Service,
|
||||
F: FnMut(A::Response, &mut B) -> Fut,
|
||||
Fut: Future<Output = Result<Res, Err>>,
|
||||
Err: From<A::Error> + From<B::Error>,
|
||||
{
|
||||
type Request = A::Request;
|
||||
type Response = Res;
|
||||
type Error = Err;
|
||||
type Future = AndThenApplyFnFuture<A, B, F, Fut, Res, Err>;
|
||||
|
||||
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
let mut inner = self.srv.borrow_mut();
|
||||
let not_ready = inner.0.poll_ready(cx)?.is_pending();
|
||||
if inner.1.poll_ready(cx)?.is_pending() || not_ready {
|
||||
Poll::Pending
|
||||
} else {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
}
|
||||
|
||||
fn call(&mut self, req: A::Request) -> Self::Future {
|
||||
let fut = self.srv.borrow_mut().0.call(req);
|
||||
AndThenApplyFnFuture {
|
||||
state: State::A(fut, Some(self.srv.clone())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[pin_project::pin_project]
|
||||
pub(crate) struct AndThenApplyFnFuture<A, B, F, Fut, Res, Err>
|
||||
where
|
||||
A: Service,
|
||||
B: Service,
|
||||
F: FnMut(A::Response, &mut B) -> Fut,
|
||||
Fut: Future<Output = Result<Res, Err>>,
|
||||
Err: From<A::Error>,
|
||||
Err: From<B::Error>,
|
||||
{
|
||||
#[pin]
|
||||
state: State<A, B, F, Fut, Res, Err>,
|
||||
}
|
||||
|
||||
#[pin_project::pin_project(project = StateProj)]
|
||||
enum State<A, B, F, Fut, Res, Err>
|
||||
where
|
||||
A: Service,
|
||||
B: Service,
|
||||
F: FnMut(A::Response, &mut B) -> Fut,
|
||||
Fut: Future<Output = Result<Res, Err>>,
|
||||
Err: From<A::Error>,
|
||||
Err: From<B::Error>,
|
||||
{
|
||||
A(#[pin] A::Future, Option<Rc<RefCell<(A, B, F)>>>),
|
||||
B(#[pin] Fut),
|
||||
Empty,
|
||||
}
|
||||
|
||||
impl<A, B, F, Fut, Res, Err> Future for AndThenApplyFnFuture<A, B, F, Fut, Res, Err>
|
||||
where
|
||||
A: Service,
|
||||
B: Service,
|
||||
F: FnMut(A::Response, &mut B) -> Fut,
|
||||
Fut: Future<Output = Result<Res, Err>>,
|
||||
Err: From<A::Error> + From<B::Error>,
|
||||
{
|
||||
type Output = Result<Res, Err>;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let mut this = self.as_mut().project();
|
||||
|
||||
match this.state.as_mut().project() {
|
||||
StateProj::A(fut, b) => match fut.poll(cx)? {
|
||||
Poll::Ready(res) => {
|
||||
let b = b.take().unwrap();
|
||||
this.state.set(State::Empty);
|
||||
let (_, b, f) = &mut *b.borrow_mut();
|
||||
let fut = f(res, b);
|
||||
this.state.set(State::B(fut));
|
||||
self.poll(cx)
|
||||
}
|
||||
Poll::Pending => Poll::Pending,
|
||||
},
|
||||
StateProj::B(fut) => fut.poll(cx).map(|r| {
|
||||
this.state.set(State::Empty);
|
||||
r
|
||||
}),
|
||||
StateProj::Empty => {
|
||||
panic!("future must not be polled after it returned `Poll::Ready`")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// `AndThenApplyFn` service factory
|
||||
pub(crate) struct AndThenApplyFnFactory<A, B, F, Fut, Res, Err> {
|
||||
srv: Rc<(A, B, F)>,
|
||||
r: PhantomData<(Fut, Res, Err)>,
|
||||
}
|
||||
|
||||
impl<A, B, F, Fut, Res, Err> AndThenApplyFnFactory<A, B, F, Fut, Res, Err>
|
||||
where
|
||||
A: ServiceFactory,
|
||||
B: ServiceFactory<Config = A::Config, InitError = A::InitError>,
|
||||
F: FnMut(A::Response, &mut B::Service) -> Fut + Clone,
|
||||
Fut: Future<Output = Result<Res, Err>>,
|
||||
Err: From<A::Error> + From<B::Error>,
|
||||
{
|
||||
/// Create new `ApplyNewService` new service instance
|
||||
pub(crate) fn new(a: A, b: B, f: F) -> Self {
|
||||
Self {
|
||||
srv: Rc::new((a, b, f)),
|
||||
r: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B, F, Fut, Res, Err> Clone for AndThenApplyFnFactory<A, B, F, Fut, Res, Err> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
srv: self.srv.clone(),
|
||||
r: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B, F, Fut, Res, Err> ServiceFactory for AndThenApplyFnFactory<A, B, F, Fut, Res, Err>
|
||||
where
|
||||
A: ServiceFactory,
|
||||
A::Config: Clone,
|
||||
B: ServiceFactory<Config = A::Config, InitError = A::InitError>,
|
||||
F: FnMut(A::Response, &mut B::Service) -> Fut + Clone,
|
||||
Fut: Future<Output = Result<Res, Err>>,
|
||||
Err: From<A::Error> + From<B::Error>,
|
||||
{
|
||||
type Request = A::Request;
|
||||
type Response = Res;
|
||||
type Error = Err;
|
||||
type Service = AndThenApplyFn<A::Service, B::Service, F, Fut, Res, Err>;
|
||||
type Config = A::Config;
|
||||
type InitError = A::InitError;
|
||||
type Future = AndThenApplyFnFactoryResponse<A, B, F, Fut, Res, Err>;
|
||||
|
||||
fn new_service(&self, cfg: A::Config) -> Self::Future {
|
||||
let srv = &*self.srv;
|
||||
AndThenApplyFnFactoryResponse {
|
||||
a: None,
|
||||
b: None,
|
||||
f: srv.2.clone(),
|
||||
fut_a: srv.0.new_service(cfg.clone()),
|
||||
fut_b: srv.1.new_service(cfg),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[pin_project::pin_project]
|
||||
pub(crate) struct AndThenApplyFnFactoryResponse<A, B, F, Fut, Res, Err>
|
||||
where
|
||||
A: ServiceFactory,
|
||||
B: ServiceFactory<Config = A::Config, InitError = A::InitError>,
|
||||
F: FnMut(A::Response, &mut B::Service) -> Fut + Clone,
|
||||
Fut: Future<Output = Result<Res, Err>>,
|
||||
Err: From<A::Error>,
|
||||
Err: From<B::Error>,
|
||||
{
|
||||
#[pin]
|
||||
fut_b: B::Future,
|
||||
#[pin]
|
||||
fut_a: A::Future,
|
||||
f: F,
|
||||
a: Option<A::Service>,
|
||||
b: Option<B::Service>,
|
||||
}
|
||||
|
||||
impl<A, B, F, Fut, Res, Err> Future for AndThenApplyFnFactoryResponse<A, B, F, Fut, Res, Err>
|
||||
where
|
||||
A: ServiceFactory,
|
||||
B: ServiceFactory<Config = A::Config, InitError = A::InitError>,
|
||||
F: FnMut(A::Response, &mut B::Service) -> Fut + Clone,
|
||||
Fut: Future<Output = Result<Res, Err>>,
|
||||
Err: From<A::Error> + From<B::Error>,
|
||||
{
|
||||
type Output =
|
||||
Result<AndThenApplyFn<A::Service, B::Service, F, Fut, Res, Err>, A::InitError>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let this = self.project();
|
||||
|
||||
if this.a.is_none() {
|
||||
if let Poll::Ready(service) = this.fut_a.poll(cx)? {
|
||||
*this.a = Some(service);
|
||||
}
|
||||
}
|
||||
|
||||
if this.b.is_none() {
|
||||
if let Poll::Ready(service) = this.fut_b.poll(cx)? {
|
||||
*this.b = Some(service);
|
||||
}
|
||||
}
|
||||
|
||||
if this.a.is_some() && this.b.is_some() {
|
||||
Poll::Ready(Ok(AndThenApplyFn {
|
||||
srv: Rc::new(RefCell::new((
|
||||
this.a.take().unwrap(),
|
||||
this.b.take().unwrap(),
|
||||
this.f.clone(),
|
||||
))),
|
||||
r: PhantomData,
|
||||
}))
|
||||
} else {
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use futures_util::future::{lazy, ok, Ready, TryFutureExt};
|
||||
|
||||
use crate::{fn_service, pipeline, pipeline_factory, Service, ServiceFactory};
|
||||
|
||||
#[derive(Clone)]
|
||||
struct Srv;
|
||||
impl Service for Srv {
|
||||
type Request = ();
|
||||
type Response = ();
|
||||
type Error = ();
|
||||
type Future = Ready<Result<(), ()>>;
|
||||
|
||||
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
#[allow(clippy::unit_arg)]
|
||||
fn call(&mut self, req: Self::Request) -> Self::Future {
|
||||
ok(req)
|
||||
}
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_service() {
|
||||
let mut srv = pipeline(ok).and_then_apply_fn(Srv, |req: &'static str, s| {
|
||||
s.call(()).map_ok(move |res| (req, res))
|
||||
});
|
||||
let res = lazy(|cx| srv.poll_ready(cx)).await;
|
||||
assert_eq!(res, Poll::Ready(Ok(())));
|
||||
|
||||
let res = srv.call("srv").await;
|
||||
assert!(res.is_ok());
|
||||
assert_eq!(res.unwrap(), ("srv", ()));
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_service_factory() {
|
||||
let new_srv = pipeline_factory(|| ok::<_, ()>(fn_service(ok))).and_then_apply_fn(
|
||||
|| ok(Srv),
|
||||
|req: &'static str, s| s.call(()).map_ok(move |res| (req, res)),
|
||||
);
|
||||
let mut srv = new_srv.new_service(()).await.unwrap();
|
||||
let res = lazy(|cx| srv.poll_ready(cx)).await;
|
||||
assert_eq!(res, Poll::Ready(Ok(())));
|
||||
|
||||
let res = srv.call("srv").await;
|
||||
assert!(res.is_ok());
|
||||
assert_eq!(res.unwrap(), ("srv", ()));
|
||||
}
|
||||
}
|
@@ -1,227 +1,230 @@
|
||||
use std::future::Future;
|
||||
use std::marker::PhantomData;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
use core::{
|
||||
future::Future,
|
||||
marker::PhantomData,
|
||||
pin::Pin,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
|
||||
use futures_core::ready;
|
||||
use pin_project_lite::pin_project;
|
||||
|
||||
use super::{IntoService, IntoServiceFactory, Service, ServiceFactory};
|
||||
|
||||
/// Apply transform function to a service.
|
||||
pub fn apply_fn<T, F, R, In, Out, Err, U>(service: U, f: F) -> Apply<T, F, R, In, Out, Err>
|
||||
///
|
||||
/// The In and Out type params refer to the request and response types for the wrapped service.
|
||||
pub fn apply_fn<I, S, F, Fut, Req, In, Res, Err>(
|
||||
service: I,
|
||||
wrap_fn: F,
|
||||
) -> Apply<S, F, Req, In, Res, Err>
|
||||
where
|
||||
T: Service<Error = Err>,
|
||||
F: FnMut(In, &mut T) -> R,
|
||||
R: Future<Output = Result<Out, Err>>,
|
||||
U: IntoService<T>,
|
||||
I: IntoService<S, In>,
|
||||
S: Service<In, Error = Err>,
|
||||
F: FnMut(Req, &mut S) -> Fut,
|
||||
Fut: Future<Output = Result<Res, Err>>,
|
||||
{
|
||||
Apply::new(service.into_service(), f)
|
||||
Apply::new(service.into_service(), wrap_fn)
|
||||
}
|
||||
|
||||
/// Service factory that produces `apply_fn` service.
|
||||
pub fn apply_fn_factory<T, F, R, In, Out, Err, U>(
|
||||
service: U,
|
||||
///
|
||||
/// The In and Out type params refer to the request and response types for the wrapped service.
|
||||
pub fn apply_fn_factory<I, SF, F, Fut, Req, In, Res, Err>(
|
||||
service: I,
|
||||
f: F,
|
||||
) -> ApplyServiceFactory<T, F, R, In, Out, Err>
|
||||
) -> ApplyFactory<SF, F, Req, In, Res, Err>
|
||||
where
|
||||
T: ServiceFactory<Error = Err>,
|
||||
F: FnMut(In, &mut T::Service) -> R + Clone,
|
||||
R: Future<Output = Result<Out, Err>>,
|
||||
U: IntoServiceFactory<T>,
|
||||
I: IntoServiceFactory<SF, In>,
|
||||
SF: ServiceFactory<In, Error = Err>,
|
||||
F: FnMut(Req, &mut SF::Service) -> Fut + Clone,
|
||||
Fut: Future<Output = Result<Res, Err>>,
|
||||
{
|
||||
ApplyServiceFactory::new(service.into_factory(), f)
|
||||
ApplyFactory::new(service.into_factory(), f)
|
||||
}
|
||||
|
||||
/// `Apply` service combinator
|
||||
pub struct Apply<T, F, R, In, Out, Err>
|
||||
/// `Apply` service combinator.
|
||||
///
|
||||
/// The In and Out type params refer to the request and response types for the wrapped service.
|
||||
pub struct Apply<S, F, Req, In, Res, Err>
|
||||
where
|
||||
T: Service<Error = Err>,
|
||||
S: Service<In, Error = Err>,
|
||||
{
|
||||
service: T,
|
||||
f: F,
|
||||
r: PhantomData<(In, Out, R)>,
|
||||
service: S,
|
||||
wrap_fn: F,
|
||||
_phantom: PhantomData<(Req, In, Res, Err)>,
|
||||
}
|
||||
|
||||
impl<T, F, R, In, Out, Err> Apply<T, F, R, In, Out, Err>
|
||||
impl<S, F, Fut, Req, In, Res, Err> Apply<S, F, Req, In, Res, Err>
|
||||
where
|
||||
T: Service<Error = Err>,
|
||||
F: FnMut(In, &mut T) -> R,
|
||||
R: Future<Output = Result<Out, Err>>,
|
||||
S: Service<In, Error = Err>,
|
||||
F: FnMut(Req, &mut S) -> Fut,
|
||||
Fut: Future<Output = Result<Res, Err>>,
|
||||
{
|
||||
/// Create new `Apply` combinator
|
||||
fn new(service: T, f: F) -> Self {
|
||||
fn new(service: S, wrap_fn: F) -> Self {
|
||||
Self {
|
||||
service,
|
||||
f,
|
||||
r: PhantomData,
|
||||
wrap_fn,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, F, R, In, Out, Err> Clone for Apply<T, F, R, In, Out, Err>
|
||||
impl<S, F, Fut, Req, In, Res, Err> Clone for Apply<S, F, Req, In, Res, Err>
|
||||
where
|
||||
T: Service<Error = Err> + Clone,
|
||||
F: FnMut(In, &mut T) -> R + Clone,
|
||||
R: Future<Output = Result<Out, Err>>,
|
||||
S: Service<In, Error = Err> + Clone,
|
||||
F: FnMut(Req, &mut S) -> Fut + Clone,
|
||||
Fut: Future<Output = Result<Res, Err>>,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Apply {
|
||||
service: self.service.clone(),
|
||||
f: self.f.clone(),
|
||||
r: PhantomData,
|
||||
wrap_fn: self.wrap_fn.clone(),
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, F, R, In, Out, Err> Service for Apply<T, F, R, In, Out, Err>
|
||||
impl<S, F, Fut, Req, In, Res, Err> Service<Req> for Apply<S, F, Req, In, Res, Err>
|
||||
where
|
||||
T: Service<Error = Err>,
|
||||
F: FnMut(In, &mut T) -> R,
|
||||
R: Future<Output = Result<Out, Err>>,
|
||||
S: Service<In, Error = Err>,
|
||||
F: FnMut(Req, &mut S) -> Fut,
|
||||
Fut: Future<Output = Result<Res, Err>>,
|
||||
{
|
||||
type Request = In;
|
||||
type Response = Out;
|
||||
type Response = Res;
|
||||
type Error = Err;
|
||||
type Future = R;
|
||||
type Future = Fut;
|
||||
|
||||
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Poll::Ready(futures_util::ready!(self.service.poll_ready(cx)))
|
||||
}
|
||||
crate::forward_ready!(service);
|
||||
|
||||
fn call(&mut self, req: In) -> Self::Future {
|
||||
(self.f)(req, &mut self.service)
|
||||
fn call(&mut self, req: Req) -> Self::Future {
|
||||
(self.wrap_fn)(req, &mut self.service)
|
||||
}
|
||||
}
|
||||
|
||||
/// `apply()` service factory
|
||||
pub struct ApplyServiceFactory<T, F, R, In, Out, Err>
|
||||
where
|
||||
T: ServiceFactory<Error = Err>,
|
||||
F: FnMut(In, &mut T::Service) -> R + Clone,
|
||||
R: Future<Output = Result<Out, Err>>,
|
||||
{
|
||||
service: T,
|
||||
f: F,
|
||||
r: PhantomData<(R, In, Out)>,
|
||||
/// `ApplyFactory` service factory combinator.
|
||||
pub struct ApplyFactory<SF, F, Req, In, Res, Err> {
|
||||
factory: SF,
|
||||
wrap_fn: F,
|
||||
_phantom: PhantomData<(Req, In, Res, Err)>,
|
||||
}
|
||||
|
||||
impl<T, F, R, In, Out, Err> ApplyServiceFactory<T, F, R, In, Out, Err>
|
||||
impl<SF, F, Fut, Req, In, Res, Err> ApplyFactory<SF, F, Req, In, Res, Err>
|
||||
where
|
||||
T: ServiceFactory<Error = Err>,
|
||||
F: FnMut(In, &mut T::Service) -> R + Clone,
|
||||
R: Future<Output = Result<Out, Err>>,
|
||||
SF: ServiceFactory<In, Error = Err>,
|
||||
F: FnMut(Req, &mut SF::Service) -> Fut + Clone,
|
||||
Fut: Future<Output = Result<Res, Err>>,
|
||||
{
|
||||
/// Create new `ApplyNewService` new service instance
|
||||
fn new(service: T, f: F) -> Self {
|
||||
/// Create new `ApplyFactory` new service instance
|
||||
fn new(factory: SF, wrap_fn: F) -> Self {
|
||||
Self {
|
||||
f,
|
||||
service,
|
||||
r: PhantomData,
|
||||
factory,
|
||||
wrap_fn,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, F, R, In, Out, Err> Clone for ApplyServiceFactory<T, F, R, In, Out, Err>
|
||||
impl<SF, F, Fut, Req, In, Res, Err> Clone for ApplyFactory<SF, F, Req, In, Res, Err>
|
||||
where
|
||||
T: ServiceFactory<Error = Err> + Clone,
|
||||
F: FnMut(In, &mut T::Service) -> R + Clone,
|
||||
R: Future<Output = Result<Out, Err>>,
|
||||
SF: ServiceFactory<In, Error = Err> + Clone,
|
||||
F: FnMut(Req, &mut SF::Service) -> Fut + Clone,
|
||||
Fut: Future<Output = Result<Res, Err>>,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
service: self.service.clone(),
|
||||
f: self.f.clone(),
|
||||
r: PhantomData,
|
||||
factory: self.factory.clone(),
|
||||
wrap_fn: self.wrap_fn.clone(),
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, F, R, In, Out, Err> ServiceFactory for ApplyServiceFactory<T, F, R, In, Out, Err>
|
||||
impl<SF, F, Fut, Req, In, Res, Err> ServiceFactory<Req>
|
||||
for ApplyFactory<SF, F, Req, In, Res, Err>
|
||||
where
|
||||
T: ServiceFactory<Error = Err>,
|
||||
F: FnMut(In, &mut T::Service) -> R + Clone,
|
||||
R: Future<Output = Result<Out, Err>>,
|
||||
SF: ServiceFactory<In, Error = Err>,
|
||||
F: FnMut(Req, &mut SF::Service) -> Fut + Clone,
|
||||
Fut: Future<Output = Result<Res, Err>>,
|
||||
{
|
||||
type Request = In;
|
||||
type Response = Out;
|
||||
type Response = Res;
|
||||
type Error = Err;
|
||||
|
||||
type Config = T::Config;
|
||||
type Service = Apply<T::Service, F, R, In, Out, Err>;
|
||||
type InitError = T::InitError;
|
||||
type Future = ApplyServiceFactoryResponse<T, F, R, In, Out, Err>;
|
||||
type Config = SF::Config;
|
||||
type Service = Apply<SF::Service, F, Req, In, Res, Err>;
|
||||
type InitError = SF::InitError;
|
||||
type Future = ApplyServiceFactoryResponse<SF, F, Fut, Req, In, Res, Err>;
|
||||
|
||||
fn new_service(&self, cfg: T::Config) -> Self::Future {
|
||||
ApplyServiceFactoryResponse::new(self.service.new_service(cfg), self.f.clone())
|
||||
fn new_service(&self, cfg: SF::Config) -> Self::Future {
|
||||
let svc = self.factory.new_service(cfg);
|
||||
ApplyServiceFactoryResponse::new(svc, self.wrap_fn.clone())
|
||||
}
|
||||
}
|
||||
|
||||
#[pin_project::pin_project]
|
||||
pub struct ApplyServiceFactoryResponse<T, F, R, In, Out, Err>
|
||||
where
|
||||
T: ServiceFactory<Error = Err>,
|
||||
F: FnMut(In, &mut T::Service) -> R,
|
||||
R: Future<Output = Result<Out, Err>>,
|
||||
{
|
||||
#[pin]
|
||||
fut: T::Future,
|
||||
f: Option<F>,
|
||||
r: PhantomData<(In, Out)>,
|
||||
pin_project! {
|
||||
pub struct ApplyServiceFactoryResponse<SF, F, Fut, Req, In, Res, Err>
|
||||
where
|
||||
SF: ServiceFactory<In, Error = Err>,
|
||||
F: FnMut(Req, &mut SF::Service) -> Fut,
|
||||
Fut: Future<Output = Result<Res, Err>>,
|
||||
{
|
||||
#[pin]
|
||||
fut: SF::Future,
|
||||
wrap_fn: Option<F>,
|
||||
_phantom: PhantomData<(Req, Res)>,
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, F, R, In, Out, Err> ApplyServiceFactoryResponse<T, F, R, In, Out, Err>
|
||||
impl<SF, F, Fut, Req, In, Res, Err> ApplyServiceFactoryResponse<SF, F, Fut, Req, In, Res, Err>
|
||||
where
|
||||
T: ServiceFactory<Error = Err>,
|
||||
F: FnMut(In, &mut T::Service) -> R,
|
||||
R: Future<Output = Result<Out, Err>>,
|
||||
SF: ServiceFactory<In, Error = Err>,
|
||||
F: FnMut(Req, &mut SF::Service) -> Fut,
|
||||
Fut: Future<Output = Result<Res, Err>>,
|
||||
{
|
||||
fn new(fut: T::Future, f: F) -> Self {
|
||||
fn new(fut: SF::Future, wrap_fn: F) -> Self {
|
||||
Self {
|
||||
f: Some(f),
|
||||
fut,
|
||||
r: PhantomData,
|
||||
wrap_fn: Some(wrap_fn),
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, F, R, In, Out, Err> Future for ApplyServiceFactoryResponse<T, F, R, In, Out, Err>
|
||||
impl<SF, F, Fut, Req, In, Res, Err> Future
|
||||
for ApplyServiceFactoryResponse<SF, F, Fut, Req, In, Res, Err>
|
||||
where
|
||||
T: ServiceFactory<Error = Err>,
|
||||
F: FnMut(In, &mut T::Service) -> R,
|
||||
R: Future<Output = Result<Out, Err>>,
|
||||
SF: ServiceFactory<In, Error = Err>,
|
||||
F: FnMut(Req, &mut SF::Service) -> Fut,
|
||||
Fut: Future<Output = Result<Res, Err>>,
|
||||
{
|
||||
type Output = Result<Apply<T::Service, F, R, In, Out, Err>, T::InitError>;
|
||||
type Output = Result<Apply<SF::Service, F, Req, In, Res, Err>, SF::InitError>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let this = self.project();
|
||||
|
||||
if let Poll::Ready(svc) = this.fut.poll(cx)? {
|
||||
Poll::Ready(Ok(Apply::new(svc, this.f.take().unwrap())))
|
||||
} else {
|
||||
Poll::Pending
|
||||
}
|
||||
let svc = ready!(this.fut.poll(cx))?;
|
||||
Poll::Ready(Ok(Apply::new(svc, this.wrap_fn.take().unwrap())))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::task::{Context, Poll};
|
||||
use core::task::Poll;
|
||||
|
||||
use futures_util::future::{lazy, ok, Ready};
|
||||
use futures_util::future::lazy;
|
||||
|
||||
use super::*;
|
||||
use crate::{pipeline, pipeline_factory, Service, ServiceFactory};
|
||||
use crate::{ok, pipeline, pipeline_factory, Ready, Service, ServiceFactory};
|
||||
|
||||
#[derive(Clone)]
|
||||
struct Srv;
|
||||
|
||||
impl Service for Srv {
|
||||
type Request = ();
|
||||
impl Service<()> for Srv {
|
||||
type Response = ();
|
||||
type Error = ();
|
||||
type Future = Ready<Result<(), ()>>;
|
||||
|
||||
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
crate::always_ready!();
|
||||
|
||||
fn call(&mut self, _: ()) -> Self::Future {
|
||||
ok(())
|
||||
|
@@ -1,227 +1,237 @@
|
||||
use std::cell::RefCell;
|
||||
use std::future::Future;
|
||||
use std::marker::PhantomData;
|
||||
use std::pin::Pin;
|
||||
use std::rc::Rc;
|
||||
use std::task::{Context, Poll};
|
||||
use alloc::rc::Rc;
|
||||
use core::{
|
||||
cell::RefCell,
|
||||
future::Future,
|
||||
marker::PhantomData,
|
||||
pin::Pin,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
|
||||
use pin_project_lite::pin_project;
|
||||
|
||||
use crate::{Service, ServiceFactory};
|
||||
|
||||
/// Convert `Fn(Config, &mut Service1) -> Future<Service2>` fn to a service factory
|
||||
pub fn apply_cfg<F, C, T, R, S, E>(
|
||||
srv: T,
|
||||
/// Convert `Fn(Config, &mut Service1) -> Future<Service2>` fn to a service factory.
|
||||
pub fn apply_cfg<S1, Req, F, Cfg, Fut, S2, Err>(
|
||||
srv: S1,
|
||||
f: F,
|
||||
) -> impl ServiceFactory<
|
||||
Config = C,
|
||||
Request = S::Request,
|
||||
Response = S::Response,
|
||||
Error = S::Error,
|
||||
Service = S,
|
||||
InitError = E,
|
||||
Future = R,
|
||||
Req,
|
||||
Config = Cfg,
|
||||
Response = S2::Response,
|
||||
Error = S2::Error,
|
||||
Service = S2,
|
||||
InitError = Err,
|
||||
Future = Fut,
|
||||
> + Clone
|
||||
where
|
||||
F: FnMut(C, &mut T) -> R,
|
||||
T: Service,
|
||||
R: Future<Output = Result<S, E>>,
|
||||
S: Service,
|
||||
S1: Service<Req>,
|
||||
F: FnMut(Cfg, &mut S1) -> Fut,
|
||||
Fut: Future<Output = Result<S2, Err>>,
|
||||
S2: Service<Req>,
|
||||
{
|
||||
ApplyConfigService {
|
||||
srv: Rc::new(RefCell::new((srv, f))),
|
||||
_t: PhantomData,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert `Fn(Config, &mut Service1) -> Future<Service2>` fn to a service factory
|
||||
/// Convert `Fn(Config, &mut ServiceFactory1) -> Future<ServiceFactory2>` fn to a service factory.
|
||||
///
|
||||
/// Service1 get constructed from `T` factory.
|
||||
pub fn apply_cfg_factory<F, C, T, R, S>(
|
||||
factory: T,
|
||||
pub fn apply_cfg_factory<SF, Req, F, Cfg, Fut, S>(
|
||||
factory: SF,
|
||||
f: F,
|
||||
) -> impl ServiceFactory<
|
||||
Config = C,
|
||||
Request = S::Request,
|
||||
Req,
|
||||
Config = Cfg,
|
||||
Response = S::Response,
|
||||
Error = S::Error,
|
||||
Service = S,
|
||||
InitError = T::InitError,
|
||||
InitError = SF::InitError,
|
||||
> + Clone
|
||||
where
|
||||
F: FnMut(C, &mut T::Service) -> R,
|
||||
T: ServiceFactory<Config = ()>,
|
||||
T::InitError: From<T::Error>,
|
||||
R: Future<Output = Result<S, T::InitError>>,
|
||||
S: Service,
|
||||
SF: ServiceFactory<Req, Config = ()>,
|
||||
F: FnMut(Cfg, &mut SF::Service) -> Fut,
|
||||
SF::InitError: From<SF::Error>,
|
||||
Fut: Future<Output = Result<S, SF::InitError>>,
|
||||
S: Service<Req>,
|
||||
{
|
||||
ApplyConfigServiceFactory {
|
||||
srv: Rc::new(RefCell::new((factory, f))),
|
||||
_t: PhantomData,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert `Fn(Config, &mut Server) -> Future<Service>` fn to NewService\
|
||||
struct ApplyConfigService<F, C, T, R, S, E>
|
||||
struct ApplyConfigService<S1, Req, F, Cfg, Fut, S2, Err>
|
||||
where
|
||||
F: FnMut(C, &mut T) -> R,
|
||||
T: Service,
|
||||
R: Future<Output = Result<S, E>>,
|
||||
S: Service,
|
||||
S1: Service<Req>,
|
||||
F: FnMut(Cfg, &mut S1) -> Fut,
|
||||
Fut: Future<Output = Result<S2, Err>>,
|
||||
S2: Service<Req>,
|
||||
{
|
||||
srv: Rc<RefCell<(T, F)>>,
|
||||
_t: PhantomData<(C, R, S)>,
|
||||
srv: Rc<RefCell<(S1, F)>>,
|
||||
_phantom: PhantomData<(Cfg, Req, Fut, S2)>,
|
||||
}
|
||||
|
||||
impl<F, C, T, R, S, E> Clone for ApplyConfigService<F, C, T, R, S, E>
|
||||
impl<S1, Req, F, Cfg, Fut, S2, Err> Clone for ApplyConfigService<S1, Req, F, Cfg, Fut, S2, Err>
|
||||
where
|
||||
F: FnMut(C, &mut T) -> R,
|
||||
T: Service,
|
||||
R: Future<Output = Result<S, E>>,
|
||||
S: Service,
|
||||
S1: Service<Req>,
|
||||
F: FnMut(Cfg, &mut S1) -> Fut,
|
||||
Fut: Future<Output = Result<S2, Err>>,
|
||||
S2: Service<Req>,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
ApplyConfigService {
|
||||
srv: self.srv.clone(),
|
||||
_t: PhantomData,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, C, T, R, S, E> ServiceFactory for ApplyConfigService<F, C, T, R, S, E>
|
||||
impl<S1, Req, F, Cfg, Fut, S2, Err> ServiceFactory<Req>
|
||||
for ApplyConfigService<S1, Req, F, Cfg, Fut, S2, Err>
|
||||
where
|
||||
F: FnMut(C, &mut T) -> R,
|
||||
T: Service,
|
||||
R: Future<Output = Result<S, E>>,
|
||||
S: Service,
|
||||
S1: Service<Req>,
|
||||
F: FnMut(Cfg, &mut S1) -> Fut,
|
||||
Fut: Future<Output = Result<S2, Err>>,
|
||||
S2: Service<Req>,
|
||||
{
|
||||
type Config = C;
|
||||
type Request = S::Request;
|
||||
type Response = S::Response;
|
||||
type Error = S::Error;
|
||||
type Service = S;
|
||||
type Config = Cfg;
|
||||
type Response = S2::Response;
|
||||
type Error = S2::Error;
|
||||
type Service = S2;
|
||||
|
||||
type InitError = E;
|
||||
type Future = R;
|
||||
type InitError = Err;
|
||||
type Future = Fut;
|
||||
|
||||
fn new_service(&self, cfg: C) -> Self::Future {
|
||||
fn new_service(&self, cfg: Cfg) -> Self::Future {
|
||||
let (t, f) = &mut *self.srv.borrow_mut();
|
||||
f(cfg, t)
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert `Fn(&Config) -> Future<Service>` fn to NewService
|
||||
struct ApplyConfigServiceFactory<F, C, T, R, S>
|
||||
struct ApplyConfigServiceFactory<SF, Req, F, Cfg, Fut, S>
|
||||
where
|
||||
F: FnMut(C, &mut T::Service) -> R,
|
||||
T: ServiceFactory<Config = ()>,
|
||||
R: Future<Output = Result<S, T::InitError>>,
|
||||
S: Service,
|
||||
SF: ServiceFactory<Req, Config = ()>,
|
||||
F: FnMut(Cfg, &mut SF::Service) -> Fut,
|
||||
Fut: Future<Output = Result<S, SF::InitError>>,
|
||||
S: Service<Req>,
|
||||
{
|
||||
srv: Rc<RefCell<(T, F)>>,
|
||||
_t: PhantomData<(C, R, S)>,
|
||||
srv: Rc<RefCell<(SF, F)>>,
|
||||
_phantom: PhantomData<(Cfg, Req, Fut, S)>,
|
||||
}
|
||||
|
||||
impl<F, C, T, R, S> Clone for ApplyConfigServiceFactory<F, C, T, R, S>
|
||||
impl<SF, Req, F, Cfg, Fut, S> Clone for ApplyConfigServiceFactory<SF, Req, F, Cfg, Fut, S>
|
||||
where
|
||||
F: FnMut(C, &mut T::Service) -> R,
|
||||
T: ServiceFactory<Config = ()>,
|
||||
R: Future<Output = Result<S, T::InitError>>,
|
||||
S: Service,
|
||||
SF: ServiceFactory<Req, Config = ()>,
|
||||
F: FnMut(Cfg, &mut SF::Service) -> Fut,
|
||||
Fut: Future<Output = Result<S, SF::InitError>>,
|
||||
S: Service<Req>,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
srv: self.srv.clone(),
|
||||
_t: PhantomData,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, C, T, R, S> ServiceFactory for ApplyConfigServiceFactory<F, C, T, R, S>
|
||||
impl<SF, Req, F, Cfg, Fut, S> ServiceFactory<Req>
|
||||
for ApplyConfigServiceFactory<SF, Req, F, Cfg, Fut, S>
|
||||
where
|
||||
F: FnMut(C, &mut T::Service) -> R,
|
||||
T: ServiceFactory<Config = ()>,
|
||||
T::InitError: From<T::Error>,
|
||||
R: Future<Output = Result<S, T::InitError>>,
|
||||
S: Service,
|
||||
SF: ServiceFactory<Req, Config = ()>,
|
||||
SF::InitError: From<SF::Error>,
|
||||
F: FnMut(Cfg, &mut SF::Service) -> Fut,
|
||||
Fut: Future<Output = Result<S, SF::InitError>>,
|
||||
S: Service<Req>,
|
||||
{
|
||||
type Config = C;
|
||||
type Request = S::Request;
|
||||
type Config = Cfg;
|
||||
type Response = S::Response;
|
||||
type Error = S::Error;
|
||||
type Service = S;
|
||||
|
||||
type InitError = T::InitError;
|
||||
type Future = ApplyConfigServiceFactoryResponse<F, C, T, R, S>;
|
||||
type InitError = SF::InitError;
|
||||
type Future = ApplyConfigServiceFactoryResponse<SF, Req, F, Cfg, Fut, S>;
|
||||
|
||||
fn new_service(&self, cfg: C) -> Self::Future {
|
||||
fn new_service(&self, cfg: Cfg) -> Self::Future {
|
||||
ApplyConfigServiceFactoryResponse {
|
||||
cfg: Some(cfg),
|
||||
store: self.srv.clone(),
|
||||
state: State::A(self.srv.borrow().0.new_service(())),
|
||||
state: State::A {
|
||||
fut: self.srv.borrow().0.new_service(()),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[pin_project::pin_project]
|
||||
struct ApplyConfigServiceFactoryResponse<F, C, T, R, S>
|
||||
where
|
||||
F: FnMut(C, &mut T::Service) -> R,
|
||||
T: ServiceFactory<Config = ()>,
|
||||
T::InitError: From<T::Error>,
|
||||
R: Future<Output = Result<S, T::InitError>>,
|
||||
S: Service,
|
||||
{
|
||||
cfg: Option<C>,
|
||||
store: Rc<RefCell<(T, F)>>,
|
||||
#[pin]
|
||||
state: State<T, R, S>,
|
||||
pin_project! {
|
||||
struct ApplyConfigServiceFactoryResponse<SF, Req, F, Cfg, Fut, S>
|
||||
where
|
||||
SF: ServiceFactory<Req, Config = ()>,
|
||||
SF::InitError: From<SF::Error>,
|
||||
F: FnMut(Cfg, &mut SF::Service) -> Fut,
|
||||
Fut: Future<Output = Result<S, SF::InitError>>,
|
||||
S: Service<Req>,
|
||||
{
|
||||
cfg: Option<Cfg>,
|
||||
store: Rc<RefCell<(SF, F)>>,
|
||||
#[pin]
|
||||
state: State<SF, Fut, S, Req>,
|
||||
}
|
||||
}
|
||||
|
||||
#[pin_project::pin_project(project = StateProj)]
|
||||
enum State<T, R, S>
|
||||
where
|
||||
T: ServiceFactory<Config = ()>,
|
||||
T::InitError: From<T::Error>,
|
||||
R: Future<Output = Result<S, T::InitError>>,
|
||||
S: Service,
|
||||
{
|
||||
A(#[pin] T::Future),
|
||||
B(T::Service),
|
||||
C(#[pin] R),
|
||||
pin_project! {
|
||||
#[project = StateProj]
|
||||
enum State<SF, Fut, S, Req>
|
||||
where
|
||||
SF: ServiceFactory<Req, Config = ()>,
|
||||
SF::InitError: From<SF::Error>,
|
||||
Fut: Future<Output = Result<S, SF::InitError>>,
|
||||
S: Service<Req>,
|
||||
{
|
||||
A { #[pin] fut: SF::Future },
|
||||
B { svc: SF::Service },
|
||||
C { #[pin] fut: Fut },
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, C, T, R, S> Future for ApplyConfigServiceFactoryResponse<F, C, T, R, S>
|
||||
impl<SF, Req, F, Cfg, Fut, S> Future
|
||||
for ApplyConfigServiceFactoryResponse<SF, Req, F, Cfg, Fut, S>
|
||||
where
|
||||
F: FnMut(C, &mut T::Service) -> R,
|
||||
T: ServiceFactory<Config = ()>,
|
||||
T::InitError: From<T::Error>,
|
||||
R: Future<Output = Result<S, T::InitError>>,
|
||||
S: Service,
|
||||
SF: ServiceFactory<Req, Config = ()>,
|
||||
SF::InitError: From<SF::Error>,
|
||||
F: FnMut(Cfg, &mut SF::Service) -> Fut,
|
||||
Fut: Future<Output = Result<S, SF::InitError>>,
|
||||
S: Service<Req>,
|
||||
{
|
||||
type Output = Result<S, T::InitError>;
|
||||
type Output = Result<S, SF::InitError>;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let mut this = self.as_mut().project();
|
||||
|
||||
match this.state.as_mut().project() {
|
||||
StateProj::A(fut) => match fut.poll(cx)? {
|
||||
StateProj::A { fut } => match fut.poll(cx)? {
|
||||
Poll::Pending => Poll::Pending,
|
||||
Poll::Ready(srv) => {
|
||||
this.state.set(State::B(srv));
|
||||
Poll::Ready(svc) => {
|
||||
this.state.set(State::B { svc });
|
||||
self.poll(cx)
|
||||
}
|
||||
},
|
||||
StateProj::B(srv) => match srv.poll_ready(cx)? {
|
||||
StateProj::B { svc } => match svc.poll_ready(cx)? {
|
||||
Poll::Ready(_) => {
|
||||
{
|
||||
let (_, f) = &mut *this.store.borrow_mut();
|
||||
let fut = f(this.cfg.take().unwrap(), srv);
|
||||
this.state.set(State::C(fut));
|
||||
let fut = f(this.cfg.take().unwrap(), svc);
|
||||
this.state.set(State::C { fut });
|
||||
}
|
||||
self.poll(cx)
|
||||
}
|
||||
Poll::Pending => Poll::Pending,
|
||||
},
|
||||
StateProj::C(fut) => fut.poll(cx),
|
||||
StateProj::C { fut } => fut.poll(cx),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,145 +1,141 @@
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
use futures_util::future::FutureExt;
|
||||
use alloc::boxed::Box;
|
||||
use core::{
|
||||
future::Future,
|
||||
marker::PhantomData,
|
||||
pin::Pin,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
|
||||
use crate::{Service, ServiceFactory};
|
||||
|
||||
pub type BoxFuture<I, E> = Pin<Box<dyn Future<Output = Result<I, E>>>>;
|
||||
pub type BoxFuture<T> = Pin<Box<dyn Future<Output = T>>>;
|
||||
|
||||
pub type BoxService<Req, Res, Err> =
|
||||
Box<dyn Service<Request = Req, Response = Res, Error = Err, Future = BoxFuture<Res, Err>>>;
|
||||
Box<dyn Service<Req, Response = Res, Error = Err, Future = BoxFuture<Result<Res, Err>>>>;
|
||||
|
||||
pub struct BoxServiceFactory<C, Req, Res, Err, InitErr>(Inner<C, Req, Res, Err, InitErr>);
|
||||
pub struct BoxServiceFactory<Cfg, Req, Res, Err, InitErr>(Inner<Cfg, Req, Res, Err, InitErr>);
|
||||
|
||||
/// Create boxed service factory
|
||||
pub fn factory<T>(
|
||||
factory: T,
|
||||
) -> BoxServiceFactory<T::Config, T::Request, T::Response, T::Error, T::InitError>
|
||||
pub fn factory<SF, Req>(
|
||||
factory: SF,
|
||||
) -> BoxServiceFactory<SF::Config, Req, SF::Response, SF::Error, SF::InitError>
|
||||
where
|
||||
T: ServiceFactory + 'static,
|
||||
T::Request: 'static,
|
||||
T::Response: 'static,
|
||||
T::Service: 'static,
|
||||
T::Future: 'static,
|
||||
T::Error: 'static,
|
||||
T::InitError: 'static,
|
||||
SF: ServiceFactory<Req> + 'static,
|
||||
Req: 'static,
|
||||
SF::Response: 'static,
|
||||
SF::Service: 'static,
|
||||
SF::Future: 'static,
|
||||
SF::Error: 'static,
|
||||
SF::InitError: 'static,
|
||||
{
|
||||
BoxServiceFactory(Box::new(FactoryWrapper {
|
||||
factory,
|
||||
_t: std::marker::PhantomData,
|
||||
_t: PhantomData,
|
||||
}))
|
||||
}
|
||||
|
||||
/// Create boxed service
|
||||
pub fn service<T>(service: T) -> BoxService<T::Request, T::Response, T::Error>
|
||||
pub fn service<S, Req>(service: S) -> BoxService<Req, S::Response, S::Error>
|
||||
where
|
||||
T: Service + 'static,
|
||||
T::Future: 'static,
|
||||
S: Service<Req> + 'static,
|
||||
Req: 'static,
|
||||
S::Future: 'static,
|
||||
{
|
||||
Box::new(ServiceWrapper(service))
|
||||
Box::new(ServiceWrapper(service, PhantomData))
|
||||
}
|
||||
|
||||
type Inner<C, Req, Res, Err, InitErr> = Box<
|
||||
dyn ServiceFactory<
|
||||
Req,
|
||||
Config = C,
|
||||
Request = Req,
|
||||
Response = Res,
|
||||
Error = Err,
|
||||
InitError = InitErr,
|
||||
Service = BoxService<Req, Res, Err>,
|
||||
Future = BoxFuture<BoxService<Req, Res, Err>, InitErr>,
|
||||
Future = BoxFuture<Result<BoxService<Req, Res, Err>, InitErr>>,
|
||||
>,
|
||||
>;
|
||||
|
||||
impl<C, Req, Res, Err, InitErr> ServiceFactory for BoxServiceFactory<C, Req, Res, Err, InitErr>
|
||||
impl<C, Req, Res, Err, InitErr> ServiceFactory<Req>
|
||||
for BoxServiceFactory<C, Req, Res, Err, InitErr>
|
||||
where
|
||||
Req: 'static,
|
||||
Res: 'static,
|
||||
Err: 'static,
|
||||
InitErr: 'static,
|
||||
{
|
||||
type Request = Req;
|
||||
type Response = Res;
|
||||
type Error = Err;
|
||||
type InitError = InitErr;
|
||||
type Config = C;
|
||||
type Service = BoxService<Req, Res, Err>;
|
||||
|
||||
type Future = BoxFuture<Self::Service, InitErr>;
|
||||
type Future = BoxFuture<Result<Self::Service, InitErr>>;
|
||||
|
||||
fn new_service(&self, cfg: C) -> Self::Future {
|
||||
self.0.new_service(cfg)
|
||||
}
|
||||
}
|
||||
|
||||
struct FactoryWrapper<C, T: ServiceFactory> {
|
||||
factory: T,
|
||||
_t: std::marker::PhantomData<C>,
|
||||
struct FactoryWrapper<SF, Req, Cfg> {
|
||||
factory: SF,
|
||||
_t: PhantomData<(Req, Cfg)>,
|
||||
}
|
||||
|
||||
impl<C, T, Req, Res, Err, InitErr> ServiceFactory for FactoryWrapper<C, T>
|
||||
impl<SF, Req, Cfg, Res, Err, InitErr> ServiceFactory<Req> for FactoryWrapper<SF, Req, Cfg>
|
||||
where
|
||||
Req: 'static,
|
||||
Res: 'static,
|
||||
Err: 'static,
|
||||
InitErr: 'static,
|
||||
T: ServiceFactory<
|
||||
Config = C,
|
||||
Request = Req,
|
||||
Response = Res,
|
||||
Error = Err,
|
||||
InitError = InitErr,
|
||||
>,
|
||||
T::Future: 'static,
|
||||
T::Service: 'static,
|
||||
<T::Service as Service>::Future: 'static,
|
||||
SF: ServiceFactory<Req, Config = Cfg, Response = Res, Error = Err, InitError = InitErr>,
|
||||
SF::Future: 'static,
|
||||
SF::Service: 'static,
|
||||
<SF::Service as Service<Req>>::Future: 'static,
|
||||
{
|
||||
type Request = Req;
|
||||
type Response = Res;
|
||||
type Error = Err;
|
||||
type InitError = InitErr;
|
||||
type Config = C;
|
||||
type Config = Cfg;
|
||||
type Service = BoxService<Req, Res, Err>;
|
||||
type Future = BoxFuture<Self::Service, Self::InitError>;
|
||||
type Future = BoxFuture<Result<Self::Service, Self::InitError>>;
|
||||
|
||||
fn new_service(&self, cfg: C) -> Self::Future {
|
||||
Box::pin(
|
||||
self.factory
|
||||
.new_service(cfg)
|
||||
.map(|res| res.map(ServiceWrapper::boxed)),
|
||||
)
|
||||
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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct ServiceWrapper<T: Service>(T);
|
||||
struct ServiceWrapper<S: Service<Req>, Req>(S, PhantomData<Req>);
|
||||
|
||||
impl<T> ServiceWrapper<T>
|
||||
impl<S, Req> ServiceWrapper<S, Req>
|
||||
where
|
||||
T: Service + 'static,
|
||||
T::Future: 'static,
|
||||
S: Service<Req> + 'static,
|
||||
Req: 'static,
|
||||
S::Future: 'static,
|
||||
{
|
||||
fn boxed(service: T) -> BoxService<T::Request, T::Response, T::Error> {
|
||||
Box::new(ServiceWrapper(service))
|
||||
fn boxed(service: S) -> BoxService<Req, S::Response, S::Error> {
|
||||
Box::new(ServiceWrapper(service, PhantomData))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Req, Res, Err> Service for ServiceWrapper<T>
|
||||
impl<S, Req, Res, Err> Service<Req> for ServiceWrapper<S, Req>
|
||||
where
|
||||
T: Service<Request = Req, Response = Res, Error = Err>,
|
||||
T::Future: 'static,
|
||||
S: Service<Req, Response = Res, Error = Err>,
|
||||
S::Future: 'static,
|
||||
{
|
||||
type Request = Req;
|
||||
type Response = Res;
|
||||
type Error = Err;
|
||||
type Future = BoxFuture<Res, Err>;
|
||||
type Future = BoxFuture<Result<Res, Err>>;
|
||||
|
||||
fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
self.0.poll_ready(ctx)
|
||||
}
|
||||
|
||||
fn call(&mut self, req: Self::Request) -> Self::Future {
|
||||
fn call(&mut self, req: Req) -> Self::Future {
|
||||
Box::pin(self.0.call(req))
|
||||
}
|
||||
}
|
||||
|
70
actix-service/src/ext.rs
Normal file
70
actix-service/src/ext.rs
Normal file
@@ -0,0 +1,70 @@
|
||||
use crate::{dev, Service, ServiceFactory};
|
||||
|
||||
pub trait ServiceExt<Req>: Service<Req> {
|
||||
/// Map this service's output to a different type, returning a new service
|
||||
/// of the resulting type.
|
||||
///
|
||||
/// This function is similar to the `Option::map` or `Iterator::map` where
|
||||
/// it will change the type of the underlying service.
|
||||
///
|
||||
/// Note that this function consumes the receiving service and returns a
|
||||
/// wrapped version of it, similar to the existing `map` methods in the
|
||||
/// standard library.
|
||||
fn map<F, R>(self, f: F) -> dev::Map<Self, F, Req, R>
|
||||
where
|
||||
Self: Sized,
|
||||
F: FnMut(Self::Response) -> R,
|
||||
{
|
||||
dev::Map::new(self, f)
|
||||
}
|
||||
|
||||
/// Map this service's error to a different error, returning a new service.
|
||||
///
|
||||
/// This function is similar to the `Result::map_err` where it will change
|
||||
/// the error type of the underlying service. For example, this can be useful to
|
||||
/// ensure that services have the same error type.
|
||||
///
|
||||
/// Note that this function consumes the receiving service and returns a
|
||||
/// wrapped version of it.
|
||||
fn map_err<F, E>(self, f: F) -> dev::MapErr<Self, Req, F, E>
|
||||
where
|
||||
Self: Sized,
|
||||
F: Fn(Self::Error) -> E,
|
||||
{
|
||||
dev::MapErr::new(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, Req> ServiceExt<Req> for S where S: Service<Req> {}
|
||||
|
||||
pub trait ServiceFactoryExt<Req>: ServiceFactory<Req> {
|
||||
/// Map this service's output to a different type, returning a new service
|
||||
/// of the resulting type.
|
||||
fn map<F, R>(self, f: F) -> crate::map::MapServiceFactory<Self, F, Req, R>
|
||||
where
|
||||
Self: Sized,
|
||||
F: FnMut(Self::Response) -> R + Clone,
|
||||
{
|
||||
crate::map::MapServiceFactory::new(self, f)
|
||||
}
|
||||
|
||||
/// Map this service's error to a different error, returning a new service.
|
||||
fn map_err<F, E>(self, f: F) -> crate::map_err::MapErrServiceFactory<Self, Req, F, E>
|
||||
where
|
||||
Self: Sized,
|
||||
F: Fn(Self::Error) -> E + Clone,
|
||||
{
|
||||
crate::map_err::MapErrServiceFactory::new(self, f)
|
||||
}
|
||||
|
||||
/// Map this factory's init error to a different error, returning a new service.
|
||||
fn map_init_err<F, E>(self, f: F) -> crate::map_init_err::MapInitErr<Self, F, Req, E>
|
||||
where
|
||||
Self: Sized,
|
||||
F: Fn(Self::InitError) -> E + Clone,
|
||||
{
|
||||
crate::map_init_err::MapInitErr::new(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, Req> ServiceFactoryExt<Req> for S where S: ServiceFactory<Req> {}
|
@@ -1,10 +1,6 @@
|
||||
use std::future::Future;
|
||||
use std::marker::PhantomData;
|
||||
use std::task::{Context, Poll};
|
||||
use core::{future::Future, marker::PhantomData, task::Poll};
|
||||
|
||||
use futures_util::future::{ok, Ready};
|
||||
|
||||
use crate::{IntoService, IntoServiceFactory, Service, ServiceFactory};
|
||||
use crate::{ok, IntoService, IntoServiceFactory, Ready, Service, ServiceFactory};
|
||||
|
||||
/// Create `ServiceFactory` for function that can act as a `Service`
|
||||
pub fn fn_service<F, Fut, Req, Res, Err, Cfg>(
|
||||
@@ -53,9 +49,11 @@ where
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
pub fn fn_factory<F, Cfg, Srv, Fut, Err>(f: F) -> FnServiceNoConfig<F, Cfg, Srv, Fut, Err>
|
||||
pub fn fn_factory<F, Cfg, Srv, Req, Fut, Err>(
|
||||
f: F,
|
||||
) -> FnServiceNoConfig<F, Cfg, Srv, Req, Fut, Err>
|
||||
where
|
||||
Srv: Service,
|
||||
Srv: Service<Req>,
|
||||
F: Fn() -> Fut,
|
||||
Fut: Future<Output = Result<Srv, Err>>,
|
||||
{
|
||||
@@ -92,13 +90,13 @@ where
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
pub fn fn_factory_with_config<F, Fut, Cfg, Srv, Err>(
|
||||
pub fn fn_factory_with_config<F, Fut, Cfg, Srv, Req, Err>(
|
||||
f: F,
|
||||
) -> FnServiceConfig<F, Fut, Cfg, Srv, Err>
|
||||
) -> FnServiceConfig<F, Fut, Cfg, Srv, Req, Err>
|
||||
where
|
||||
F: Fn(Cfg) -> Fut,
|
||||
Fut: Future<Output = Result<Srv, Err>>,
|
||||
Srv: Service,
|
||||
Srv: Service<Req>,
|
||||
{
|
||||
FnServiceConfig::new(f)
|
||||
}
|
||||
@@ -132,26 +130,23 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, Fut, Req, Res, Err> Service for FnService<F, Fut, Req, Res, Err>
|
||||
impl<F, Fut, Req, Res, Err> Service<Req> for FnService<F, Fut, Req, Res, Err>
|
||||
where
|
||||
F: FnMut(Req) -> Fut,
|
||||
Fut: Future<Output = Result<Res, Err>>,
|
||||
{
|
||||
type Request = Req;
|
||||
type Response = Res;
|
||||
type Error = Err;
|
||||
type Future = Fut;
|
||||
|
||||
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
crate::always_ready!();
|
||||
|
||||
fn call(&mut self, req: Req) -> Self::Future {
|
||||
(self.f)(req)
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, Fut, Req, Res, Err> IntoService<FnService<F, Fut, Req, Res, Err>> for F
|
||||
impl<F, Fut, Req, Res, Err> IntoService<FnService<F, Fut, Req, Res, Err>, Req> for F
|
||||
where
|
||||
F: FnMut(Req) -> Fut,
|
||||
Fut: Future<Output = Result<Res, Err>>,
|
||||
@@ -190,31 +185,28 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, Fut, Req, Res, Err> Service for FnServiceFactory<F, Fut, Req, Res, Err, ()>
|
||||
impl<F, Fut, Req, Res, Err> Service<Req> for FnServiceFactory<F, Fut, Req, Res, Err, ()>
|
||||
where
|
||||
F: FnMut(Req) -> Fut + Clone,
|
||||
Fut: Future<Output = Result<Res, Err>>,
|
||||
{
|
||||
type Request = Req;
|
||||
type Response = Res;
|
||||
type Error = Err;
|
||||
type Future = Fut;
|
||||
|
||||
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
crate::always_ready!();
|
||||
|
||||
fn call(&mut self, req: Self::Request) -> Self::Future {
|
||||
fn call(&mut self, req: Req) -> Self::Future {
|
||||
(self.f)(req)
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, Fut, Req, Res, Err, Cfg> ServiceFactory for FnServiceFactory<F, Fut, Req, Res, Err, Cfg>
|
||||
impl<F, Fut, Req, Res, Err, Cfg> ServiceFactory<Req>
|
||||
for FnServiceFactory<F, Fut, Req, Res, Err, Cfg>
|
||||
where
|
||||
F: FnMut(Req) -> Fut + Clone,
|
||||
Fut: Future<Output = Result<Res, Err>>,
|
||||
{
|
||||
type Request = Req;
|
||||
type Response = Res;
|
||||
type Error = Err;
|
||||
|
||||
@@ -229,7 +221,7 @@ where
|
||||
}
|
||||
|
||||
impl<F, Fut, Req, Res, Err, Cfg>
|
||||
IntoServiceFactory<FnServiceFactory<F, Fut, Req, Res, Err, Cfg>> for F
|
||||
IntoServiceFactory<FnServiceFactory<F, Fut, Req, Res, Err, Cfg>, Req> for F
|
||||
where
|
||||
F: Fn(Req) -> Fut + Clone,
|
||||
Fut: Future<Output = Result<Res, Err>>,
|
||||
@@ -240,32 +232,32 @@ where
|
||||
}
|
||||
|
||||
/// Convert `Fn(&Config) -> Future<Service>` fn to NewService
|
||||
pub struct FnServiceConfig<F, Fut, Cfg, Srv, Err>
|
||||
pub struct FnServiceConfig<F, Fut, Cfg, Srv, Req, Err>
|
||||
where
|
||||
F: Fn(Cfg) -> Fut,
|
||||
Fut: Future<Output = Result<Srv, Err>>,
|
||||
Srv: Service,
|
||||
Srv: Service<Req>,
|
||||
{
|
||||
f: F,
|
||||
_t: PhantomData<(Fut, Cfg, Srv, Err)>,
|
||||
_t: PhantomData<(Fut, Cfg, Req, Srv, Err)>,
|
||||
}
|
||||
|
||||
impl<F, Fut, Cfg, Srv, Err> FnServiceConfig<F, Fut, Cfg, Srv, Err>
|
||||
impl<F, Fut, Cfg, Srv, Req, Err> FnServiceConfig<F, Fut, Cfg, Srv, Req, Err>
|
||||
where
|
||||
F: Fn(Cfg) -> Fut,
|
||||
Fut: Future<Output = Result<Srv, Err>>,
|
||||
Srv: Service,
|
||||
Srv: Service<Req>,
|
||||
{
|
||||
fn new(f: F) -> Self {
|
||||
FnServiceConfig { f, _t: PhantomData }
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, Fut, Cfg, Srv, Err> Clone for FnServiceConfig<F, Fut, Cfg, Srv, Err>
|
||||
impl<F, Fut, Cfg, Srv, Req, Err> Clone for FnServiceConfig<F, Fut, Cfg, Srv, Req, Err>
|
||||
where
|
||||
F: Fn(Cfg) -> Fut + Clone,
|
||||
Fut: Future<Output = Result<Srv, Err>>,
|
||||
Srv: Service,
|
||||
Srv: Service<Req>,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
FnServiceConfig {
|
||||
@@ -275,13 +267,13 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, Fut, Cfg, Srv, Err> ServiceFactory for FnServiceConfig<F, Fut, Cfg, Srv, Err>
|
||||
impl<F, Fut, Cfg, Srv, Req, Err> ServiceFactory<Req>
|
||||
for FnServiceConfig<F, Fut, Cfg, Srv, Req, Err>
|
||||
where
|
||||
F: Fn(Cfg) -> Fut,
|
||||
Fut: Future<Output = Result<Srv, Err>>,
|
||||
Srv: Service,
|
||||
Srv: Service<Req>,
|
||||
{
|
||||
type Request = Srv::Request;
|
||||
type Response = Srv::Response;
|
||||
type Error = Srv::Error;
|
||||
|
||||
@@ -296,76 +288,77 @@ where
|
||||
}
|
||||
|
||||
/// Converter for `Fn() -> Future<Service>` fn
|
||||
pub struct FnServiceNoConfig<F, C, S, R, E>
|
||||
pub struct FnServiceNoConfig<F, Cfg, Srv, Req, Fut, Err>
|
||||
where
|
||||
F: Fn() -> R,
|
||||
S: Service,
|
||||
R: Future<Output = Result<S, E>>,
|
||||
F: Fn() -> Fut,
|
||||
Srv: Service<Req>,
|
||||
Fut: Future<Output = Result<Srv, Err>>,
|
||||
{
|
||||
f: F,
|
||||
_t: PhantomData<C>,
|
||||
_t: PhantomData<(Cfg, Req)>,
|
||||
}
|
||||
|
||||
impl<F, C, S, R, E> FnServiceNoConfig<F, C, S, R, E>
|
||||
impl<F, Cfg, Srv, Req, Fut, Err> FnServiceNoConfig<F, Cfg, Srv, Req, Fut, Err>
|
||||
where
|
||||
F: Fn() -> R,
|
||||
R: Future<Output = Result<S, E>>,
|
||||
S: Service,
|
||||
F: Fn() -> Fut,
|
||||
Fut: Future<Output = Result<Srv, Err>>,
|
||||
Srv: Service<Req>,
|
||||
{
|
||||
fn new(f: F) -> Self {
|
||||
Self { f, _t: PhantomData }
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, C, S, R, E> ServiceFactory for FnServiceNoConfig<F, C, S, R, E>
|
||||
impl<F, Cfg, Srv, Req, Fut, Err> ServiceFactory<Req>
|
||||
for FnServiceNoConfig<F, Cfg, Srv, Req, Fut, Err>
|
||||
where
|
||||
F: Fn() -> R,
|
||||
R: Future<Output = Result<S, E>>,
|
||||
S: Service,
|
||||
F: Fn() -> Fut,
|
||||
Fut: Future<Output = Result<Srv, Err>>,
|
||||
Srv: Service<Req>,
|
||||
{
|
||||
type Request = S::Request;
|
||||
type Response = S::Response;
|
||||
type Error = S::Error;
|
||||
type Service = S;
|
||||
type Config = C;
|
||||
type InitError = E;
|
||||
type Future = R;
|
||||
type Response = Srv::Response;
|
||||
type Error = Srv::Error;
|
||||
type Service = Srv;
|
||||
type Config = Cfg;
|
||||
type InitError = Err;
|
||||
type Future = Fut;
|
||||
|
||||
fn new_service(&self, _: C) -> Self::Future {
|
||||
fn new_service(&self, _: Cfg) -> Self::Future {
|
||||
(self.f)()
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, C, S, R, E> Clone for FnServiceNoConfig<F, C, S, R, E>
|
||||
impl<F, Cfg, Srv, Req, Fut, Err> Clone for FnServiceNoConfig<F, Cfg, Srv, Req, Fut, Err>
|
||||
where
|
||||
F: Fn() -> R + Clone,
|
||||
R: Future<Output = Result<S, E>>,
|
||||
S: Service,
|
||||
F: Fn() -> Fut + Clone,
|
||||
Fut: Future<Output = Result<Srv, Err>>,
|
||||
Srv: Service<Req>,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Self::new(self.f.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, C, S, R, E> IntoServiceFactory<FnServiceNoConfig<F, C, S, R, E>> for F
|
||||
impl<F, Cfg, Srv, Req, Fut, Err>
|
||||
IntoServiceFactory<FnServiceNoConfig<F, Cfg, Srv, Req, Fut, Err>, Req> for F
|
||||
where
|
||||
F: Fn() -> R,
|
||||
R: Future<Output = Result<S, E>>,
|
||||
S: Service,
|
||||
F: Fn() -> Fut,
|
||||
Fut: Future<Output = Result<Srv, Err>>,
|
||||
Srv: Service<Req>,
|
||||
{
|
||||
fn into_factory(self) -> FnServiceNoConfig<F, C, S, R, E> {
|
||||
fn into_factory(self) -> FnServiceNoConfig<F, Cfg, Srv, Req, Fut, Err> {
|
||||
FnServiceNoConfig::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::task::Poll;
|
||||
use core::task::Poll;
|
||||
|
||||
use futures_util::future::{lazy, ok};
|
||||
use futures_util::future::lazy;
|
||||
|
||||
use super::*;
|
||||
use crate::{Service, ServiceFactory};
|
||||
use crate::{ok, Service, ServiceFactory};
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_fn_service() {
|
||||
|
@@ -1,36 +1,47 @@
|
||||
//! See [`Service`](trait.Service.html) docs for information on this crate's foundational trait.
|
||||
//! See [`Service`] docs for information on this crate's foundational trait.
|
||||
|
||||
#![deny(rust_2018_idioms, warnings)]
|
||||
#![no_std]
|
||||
#![deny(rust_2018_idioms, nonstandard_style)]
|
||||
#![allow(clippy::type_complexity)]
|
||||
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
||||
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::future::Future;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
use std::task::{self, Context, Poll};
|
||||
extern crate alloc;
|
||||
|
||||
use alloc::{boxed::Box, rc::Rc, sync::Arc};
|
||||
use core::{
|
||||
cell::RefCell,
|
||||
future::Future,
|
||||
task::{self, Context, Poll},
|
||||
};
|
||||
|
||||
mod and_then;
|
||||
mod and_then_apply_fn;
|
||||
mod apply;
|
||||
mod apply_cfg;
|
||||
pub mod boxed;
|
||||
mod ext;
|
||||
mod fn_service;
|
||||
mod map;
|
||||
mod map_config;
|
||||
mod map_err;
|
||||
mod map_init_err;
|
||||
mod pipeline;
|
||||
mod ready;
|
||||
mod then;
|
||||
mod transform;
|
||||
mod transform_err;
|
||||
|
||||
pub use self::apply::{apply_fn, apply_fn_factory};
|
||||
pub use self::apply_cfg::{apply_cfg, apply_cfg_factory};
|
||||
pub use self::ext::{ServiceExt, ServiceFactoryExt};
|
||||
pub use self::fn_service::{fn_factory, fn_factory_with_config, fn_service};
|
||||
pub use self::map_config::{map_config, unit_config};
|
||||
pub use self::pipeline::{pipeline, pipeline_factory, Pipeline, PipelineFactory};
|
||||
pub use self::transform::{apply, Transform};
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use self::ready::{err, ok, ready, Ready};
|
||||
|
||||
/// An asynchronous operation from `Request` to a `Response`.
|
||||
///
|
||||
/// The `Service` trait models a request/response interaction, receiving requests and returning
|
||||
@@ -65,19 +76,16 @@ pub use self::transform::{apply, Transform};
|
||||
/// ```
|
||||
///
|
||||
/// 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.fn_service.html).
|
||||
/// could be rewritten as a simple function and passed to [fn_service](fn_service()).
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// async fn my_service(req: u8) -> Result<u64, MyError>;
|
||||
/// ```
|
||||
pub trait Service {
|
||||
/// Requests handled by the service.
|
||||
type Request;
|
||||
|
||||
pub trait Service<Req> {
|
||||
/// Responses given by the service.
|
||||
type Response;
|
||||
|
||||
/// Errors produced by the service.
|
||||
/// Errors produced by the service when polling readiness or executing call.
|
||||
type Error;
|
||||
|
||||
/// The future response value.
|
||||
@@ -107,40 +115,7 @@ pub trait Service {
|
||||
///
|
||||
/// Calling `call` without calling `poll_ready` is permitted. The
|
||||
/// implementation must be resilient to this fact.
|
||||
fn call(&mut self, req: Self::Request) -> Self::Future;
|
||||
|
||||
/// Map this service's output to a different type, returning a new service
|
||||
/// of the resulting type.
|
||||
///
|
||||
/// This function is similar to the `Option::map` or `Iterator::map` where
|
||||
/// it will change the type of the underlying service.
|
||||
///
|
||||
/// Note that this function consumes the receiving service and returns a
|
||||
/// wrapped version of it, similar to the existing `map` methods in the
|
||||
/// standard library.
|
||||
fn map<F, R>(self, f: F) -> crate::dev::Map<Self, F, R>
|
||||
where
|
||||
Self: Sized,
|
||||
F: FnMut(Self::Response) -> R,
|
||||
{
|
||||
crate::dev::Map::new(self, f)
|
||||
}
|
||||
|
||||
/// Map this service's error to a different error, returning a new service.
|
||||
///
|
||||
/// This function is similar to the `Result::map_err` where it will change
|
||||
/// the error type of the underlying service. For example, this can be useful to
|
||||
/// ensure that services have the same error type.
|
||||
///
|
||||
/// Note that this function consumes the receiving service and returns a
|
||||
/// wrapped version of it.
|
||||
fn map_err<F, E>(self, f: F) -> crate::dev::MapErr<Self, F, E>
|
||||
where
|
||||
Self: Sized,
|
||||
F: Fn(Self::Error) -> E,
|
||||
{
|
||||
crate::dev::MapErr::new(self, f)
|
||||
}
|
||||
fn call(&mut self, req: Req) -> Self::Future;
|
||||
}
|
||||
|
||||
/// Factory for creating `Service`s.
|
||||
@@ -152,10 +127,7 @@ pub trait Service {
|
||||
/// requests on that new TCP stream.
|
||||
///
|
||||
/// `Config` is a service factory configuration type.
|
||||
pub trait ServiceFactory {
|
||||
/// Requests handled by the created services.
|
||||
type Request;
|
||||
|
||||
pub trait ServiceFactory<Req> {
|
||||
/// Responses given by the created services.
|
||||
type Response;
|
||||
|
||||
@@ -166,11 +138,7 @@ pub trait ServiceFactory {
|
||||
type Config;
|
||||
|
||||
/// The kind of `Service` created by this factory.
|
||||
type Service: Service<
|
||||
Request = Self::Request,
|
||||
Response = Self::Response,
|
||||
Error = Self::Error,
|
||||
>;
|
||||
type Service: Service<Req, Response = Self::Response, Error = Self::Error>;
|
||||
|
||||
/// Errors potentially raised while building a service.
|
||||
type InitError;
|
||||
@@ -180,41 +148,12 @@ pub trait ServiceFactory {
|
||||
|
||||
/// Create and return a new service asynchronously.
|
||||
fn new_service(&self, cfg: Self::Config) -> Self::Future;
|
||||
|
||||
/// Map this service's output to a different type, returning a new service
|
||||
/// of the resulting type.
|
||||
fn map<F, R>(self, f: F) -> crate::map::MapServiceFactory<Self, F, R>
|
||||
where
|
||||
Self: Sized,
|
||||
F: FnMut(Self::Response) -> R + Clone,
|
||||
{
|
||||
crate::map::MapServiceFactory::new(self, f)
|
||||
}
|
||||
|
||||
/// Map this service's error to a different error, returning a new service.
|
||||
fn map_err<F, E>(self, f: F) -> crate::map_err::MapErrServiceFactory<Self, F, E>
|
||||
where
|
||||
Self: Sized,
|
||||
F: Fn(Self::Error) -> E + Clone,
|
||||
{
|
||||
crate::map_err::MapErrServiceFactory::new(self, f)
|
||||
}
|
||||
|
||||
/// Map this factory's init error to a different error, returning a new service.
|
||||
fn map_init_err<F, E>(self, f: F) -> crate::map_init_err::MapInitErr<Self, F, E>
|
||||
where
|
||||
Self: Sized,
|
||||
F: Fn(Self::InitError) -> E + Clone,
|
||||
{
|
||||
crate::map_init_err::MapInitErr::new(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, S> Service for &'a mut S
|
||||
impl<'a, S, Req> Service<Req> for &'a mut S
|
||||
where
|
||||
S: Service + 'a,
|
||||
S: Service<Req> + 'a,
|
||||
{
|
||||
type Request = S::Request;
|
||||
type Response = S::Response;
|
||||
type Error = S::Error;
|
||||
type Future = S::Future;
|
||||
@@ -223,16 +162,15 @@ where
|
||||
(**self).poll_ready(ctx)
|
||||
}
|
||||
|
||||
fn call(&mut self, request: Self::Request) -> S::Future {
|
||||
fn call(&mut self, request: Req) -> S::Future {
|
||||
(**self).call(request)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> Service for Box<S>
|
||||
impl<S, Req> Service<Req> for Box<S>
|
||||
where
|
||||
S: Service + ?Sized,
|
||||
S: Service<Req> + ?Sized,
|
||||
{
|
||||
type Request = S::Request;
|
||||
type Response = S::Response;
|
||||
type Error = S::Error;
|
||||
type Future = S::Future;
|
||||
@@ -241,16 +179,15 @@ where
|
||||
(**self).poll_ready(ctx)
|
||||
}
|
||||
|
||||
fn call(&mut self, request: Self::Request) -> S::Future {
|
||||
fn call(&mut self, request: Req) -> S::Future {
|
||||
(**self).call(request)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> Service for RefCell<S>
|
||||
impl<S, Req> Service<Req> for RefCell<S>
|
||||
where
|
||||
S: Service,
|
||||
S: Service<Req>,
|
||||
{
|
||||
type Request = S::Request;
|
||||
type Response = S::Response;
|
||||
type Error = S::Error;
|
||||
type Future = S::Future;
|
||||
@@ -259,16 +196,15 @@ where
|
||||
self.borrow_mut().poll_ready(ctx)
|
||||
}
|
||||
|
||||
fn call(&mut self, request: Self::Request) -> S::Future {
|
||||
fn call(&mut self, request: Req) -> S::Future {
|
||||
self.borrow_mut().call(request)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> Service for Rc<RefCell<S>>
|
||||
impl<S, Req> Service<Req> for Rc<RefCell<S>>
|
||||
where
|
||||
S: Service,
|
||||
S: Service<Req>,
|
||||
{
|
||||
type Request = S::Request;
|
||||
type Response = S::Response;
|
||||
type Error = S::Error;
|
||||
type Future = S::Future;
|
||||
@@ -277,16 +213,15 @@ where
|
||||
self.borrow_mut().poll_ready(ctx)
|
||||
}
|
||||
|
||||
fn call(&mut self, request: Self::Request) -> S::Future {
|
||||
fn call(&mut self, request: Req) -> S::Future {
|
||||
(&mut (**self).borrow_mut()).call(request)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> ServiceFactory for Rc<S>
|
||||
impl<S, Req> ServiceFactory<Req> for Rc<S>
|
||||
where
|
||||
S: ServiceFactory,
|
||||
S: ServiceFactory<Req>,
|
||||
{
|
||||
type Request = S::Request;
|
||||
type Response = S::Response;
|
||||
type Error = S::Error;
|
||||
type Config = S::Config;
|
||||
@@ -299,11 +234,10 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> ServiceFactory for Arc<S>
|
||||
impl<S, Req> ServiceFactory<Req> for Arc<S>
|
||||
where
|
||||
S: ServiceFactory,
|
||||
S: ServiceFactory<Req>,
|
||||
{
|
||||
type Request = S::Request;
|
||||
type Response = S::Response;
|
||||
type Error = S::Error;
|
||||
type Config = S::Config;
|
||||
@@ -317,52 +251,52 @@ where
|
||||
}
|
||||
|
||||
/// Trait for types that can be converted to a `Service`
|
||||
pub trait IntoService<T>
|
||||
pub trait IntoService<S, Req>
|
||||
where
|
||||
T: Service,
|
||||
S: Service<Req>,
|
||||
{
|
||||
/// Convert to a `Service`
|
||||
fn into_service(self) -> T;
|
||||
fn into_service(self) -> S;
|
||||
}
|
||||
|
||||
/// Trait for types that can be converted to a `ServiceFactory`
|
||||
pub trait IntoServiceFactory<T>
|
||||
pub trait IntoServiceFactory<SF, Req>
|
||||
where
|
||||
T: ServiceFactory,
|
||||
SF: ServiceFactory<Req>,
|
||||
{
|
||||
/// Convert `Self` to a `ServiceFactory`
|
||||
fn into_factory(self) -> T;
|
||||
fn into_factory(self) -> SF;
|
||||
}
|
||||
|
||||
impl<T> IntoService<T> for T
|
||||
impl<S, Req> IntoService<S, Req> for S
|
||||
where
|
||||
T: Service,
|
||||
S: Service<Req>,
|
||||
{
|
||||
fn into_service(self) -> T {
|
||||
fn into_service(self) -> S {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IntoServiceFactory<T> for T
|
||||
impl<SF, Req> IntoServiceFactory<SF, Req> for SF
|
||||
where
|
||||
T: ServiceFactory,
|
||||
SF: ServiceFactory<Req>,
|
||||
{
|
||||
fn into_factory(self) -> T {
|
||||
fn into_factory(self) -> SF {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert object of type `T` to a service `S`
|
||||
pub fn into_service<T, S>(tp: T) -> S
|
||||
/// Convert object of type `U` to a service `S`
|
||||
pub fn into_service<I, S, Req>(tp: I) -> S
|
||||
where
|
||||
S: Service,
|
||||
T: IntoService<S>,
|
||||
I: IntoService<S, Req>,
|
||||
S: Service<Req>,
|
||||
{
|
||||
tp.into_service()
|
||||
}
|
||||
|
||||
pub mod dev {
|
||||
pub use crate::apply::{Apply, ApplyServiceFactory};
|
||||
pub use crate::apply::{Apply, ApplyFactory};
|
||||
pub use crate::fn_service::{
|
||||
FnService, FnServiceConfig, FnServiceFactory, FnServiceNoConfig,
|
||||
};
|
||||
@@ -373,3 +307,27 @@ pub mod dev {
|
||||
pub use crate::transform::ApplyTransform;
|
||||
pub use crate::transform_err::TransformMapInitErr;
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! always_ready {
|
||||
() => {
|
||||
fn poll_ready(
|
||||
&mut self,
|
||||
_: &mut ::core::task::Context<'_>,
|
||||
) -> ::core::task::Poll<Result<(), Self::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! forward_ready {
|
||||
($field:ident) => {
|
||||
fn poll_ready(
|
||||
&mut self,
|
||||
cx: &mut ::core::task::Context<'_>,
|
||||
) -> ::core::task::Poll<Result<(), Self::Error>> {
|
||||
self.$field.poll_ready(cx)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@@ -1,25 +1,29 @@
|
||||
use std::future::Future;
|
||||
use std::marker::PhantomData;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
use core::{
|
||||
future::Future,
|
||||
marker::PhantomData,
|
||||
pin::Pin,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
|
||||
use pin_project_lite::pin_project;
|
||||
|
||||
use super::{Service, ServiceFactory};
|
||||
|
||||
/// Service for the `map` combinator, changing the type of a service's response.
|
||||
///
|
||||
/// This is created by the `ServiceExt::map` method.
|
||||
pub struct Map<A, F, Response> {
|
||||
pub struct Map<A, F, Req, Res> {
|
||||
service: A,
|
||||
f: F,
|
||||
_t: PhantomData<Response>,
|
||||
_t: PhantomData<(Req, Res)>,
|
||||
}
|
||||
|
||||
impl<A, F, Response> Map<A, F, Response> {
|
||||
impl<A, F, Req, Res> Map<A, F, Req, Res> {
|
||||
/// Create new `Map` combinator
|
||||
pub(crate) fn new(service: A, f: F) -> Self
|
||||
where
|
||||
A: Service,
|
||||
F: FnMut(A::Response) -> Response,
|
||||
A: Service<Req>,
|
||||
F: FnMut(A::Response) -> Res,
|
||||
{
|
||||
Self {
|
||||
service,
|
||||
@@ -29,7 +33,7 @@ impl<A, F, Response> Map<A, F, Response> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, F, Response> Clone for Map<A, F, Response>
|
||||
impl<A, F, Req, Res> Clone for Map<A, F, Req, Res>
|
||||
where
|
||||
A: Clone,
|
||||
F: Clone,
|
||||
@@ -43,52 +47,50 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, F, Response> Service for Map<A, F, Response>
|
||||
impl<A, F, Req, Res> Service<Req> for Map<A, F, Req, Res>
|
||||
where
|
||||
A: Service,
|
||||
F: FnMut(A::Response) -> Response + Clone,
|
||||
A: Service<Req>,
|
||||
F: FnMut(A::Response) -> Res + Clone,
|
||||
{
|
||||
type Request = A::Request;
|
||||
type Response = Response;
|
||||
type Response = Res;
|
||||
type Error = A::Error;
|
||||
type Future = MapFuture<A, F, Response>;
|
||||
type Future = MapFuture<A, F, Req, Res>;
|
||||
|
||||
fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
self.service.poll_ready(ctx)
|
||||
}
|
||||
crate::forward_ready!(service);
|
||||
|
||||
fn call(&mut self, req: A::Request) -> Self::Future {
|
||||
fn call(&mut self, req: Req) -> Self::Future {
|
||||
MapFuture::new(self.service.call(req), self.f.clone())
|
||||
}
|
||||
}
|
||||
|
||||
#[pin_project::pin_project]
|
||||
pub struct MapFuture<A, F, Response>
|
||||
where
|
||||
A: Service,
|
||||
F: FnMut(A::Response) -> Response,
|
||||
{
|
||||
f: F,
|
||||
#[pin]
|
||||
fut: A::Future,
|
||||
pin_project! {
|
||||
pub struct MapFuture<A, F, Req, Res>
|
||||
where
|
||||
A: Service<Req>,
|
||||
F: FnMut(A::Response) -> Res,
|
||||
{
|
||||
f: F,
|
||||
#[pin]
|
||||
fut: A::Future,
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, F, Response> MapFuture<A, F, Response>
|
||||
impl<A, F, Req, Res> MapFuture<A, F, Req, Res>
|
||||
where
|
||||
A: Service,
|
||||
F: FnMut(A::Response) -> Response,
|
||||
A: Service<Req>,
|
||||
F: FnMut(A::Response) -> Res,
|
||||
{
|
||||
fn new(fut: A::Future, f: F) -> Self {
|
||||
MapFuture { f, fut }
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, F, Response> Future for MapFuture<A, F, Response>
|
||||
impl<A, F, Req, Res> Future for MapFuture<A, F, Req, Res>
|
||||
where
|
||||
A: Service,
|
||||
F: FnMut(A::Response) -> Response,
|
||||
A: Service<Req>,
|
||||
F: FnMut(A::Response) -> Res,
|
||||
{
|
||||
type Output = Result<Response, A::Error>;
|
||||
type Output = Result<Res, A::Error>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let this = self.project();
|
||||
@@ -102,17 +104,17 @@ where
|
||||
}
|
||||
|
||||
/// `MapNewService` new service combinator
|
||||
pub struct MapServiceFactory<A, F, Res> {
|
||||
pub struct MapServiceFactory<A, F, Req, Res> {
|
||||
a: A,
|
||||
f: F,
|
||||
r: PhantomData<Res>,
|
||||
r: PhantomData<(Res, Req)>,
|
||||
}
|
||||
|
||||
impl<A, F, Res> MapServiceFactory<A, F, Res> {
|
||||
impl<A, F, Req, Res> MapServiceFactory<A, F, Req, Res> {
|
||||
/// Create new `Map` new service instance
|
||||
pub(crate) fn new(a: A, f: F) -> Self
|
||||
where
|
||||
A: ServiceFactory,
|
||||
A: ServiceFactory<Req>,
|
||||
F: FnMut(A::Response) -> Res,
|
||||
{
|
||||
Self {
|
||||
@@ -123,7 +125,7 @@ impl<A, F, Res> MapServiceFactory<A, F, Res> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, F, Res> Clone for MapServiceFactory<A, F, Res>
|
||||
impl<A, F, Req, Res> Clone for MapServiceFactory<A, F, Req, Res>
|
||||
where
|
||||
A: Clone,
|
||||
F: Clone,
|
||||
@@ -137,39 +139,39 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, F, Res> ServiceFactory for MapServiceFactory<A, F, Res>
|
||||
impl<A, F, Req, Res> ServiceFactory<Req> for MapServiceFactory<A, F, Req, Res>
|
||||
where
|
||||
A: ServiceFactory,
|
||||
A: ServiceFactory<Req>,
|
||||
F: FnMut(A::Response) -> Res + Clone,
|
||||
{
|
||||
type Request = A::Request;
|
||||
type Response = Res;
|
||||
type Error = A::Error;
|
||||
|
||||
type Config = A::Config;
|
||||
type Service = Map<A::Service, F, Res>;
|
||||
type Service = Map<A::Service, F, Req, Res>;
|
||||
type InitError = A::InitError;
|
||||
type Future = MapServiceFuture<A, F, Res>;
|
||||
type Future = MapServiceFuture<A, F, Req, Res>;
|
||||
|
||||
fn new_service(&self, cfg: A::Config) -> Self::Future {
|
||||
MapServiceFuture::new(self.a.new_service(cfg), self.f.clone())
|
||||
}
|
||||
}
|
||||
|
||||
#[pin_project::pin_project]
|
||||
pub struct MapServiceFuture<A, F, Res>
|
||||
where
|
||||
A: ServiceFactory,
|
||||
F: FnMut(A::Response) -> Res,
|
||||
{
|
||||
#[pin]
|
||||
fut: A::Future,
|
||||
f: Option<F>,
|
||||
pin_project! {
|
||||
pub struct MapServiceFuture<A, F, Req, Res>
|
||||
where
|
||||
A: ServiceFactory<Req>,
|
||||
F: FnMut(A::Response) -> Res,
|
||||
{
|
||||
#[pin]
|
||||
fut: A::Future,
|
||||
f: Option<F>,
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, F, Res> MapServiceFuture<A, F, Res>
|
||||
impl<A, F, Req, Res> MapServiceFuture<A, F, Req, Res>
|
||||
where
|
||||
A: ServiceFactory,
|
||||
A: ServiceFactory<Req>,
|
||||
F: FnMut(A::Response) -> Res,
|
||||
{
|
||||
fn new(fut: A::Future, f: F) -> Self {
|
||||
@@ -177,12 +179,12 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, F, Res> Future for MapServiceFuture<A, F, Res>
|
||||
impl<A, F, Req, Res> Future for MapServiceFuture<A, F, Req, Res>
|
||||
where
|
||||
A: ServiceFactory,
|
||||
A: ServiceFactory<Req>,
|
||||
F: FnMut(A::Response) -> Res,
|
||||
{
|
||||
type Output = Result<Map<A::Service, F, Res>, A::InitError>;
|
||||
type Output = Result<Map<A::Service, F, Req, Res>, A::InitError>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let this = self.project();
|
||||
@@ -197,22 +199,21 @@ where
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use futures_util::future::{lazy, ok, Ready};
|
||||
use futures_util::future::lazy;
|
||||
|
||||
use super::*;
|
||||
use crate::{IntoServiceFactory, Service, ServiceFactory};
|
||||
use crate::{
|
||||
ok, IntoServiceFactory, Ready, Service, ServiceExt, ServiceFactory, ServiceFactoryExt,
|
||||
};
|
||||
|
||||
struct Srv;
|
||||
|
||||
impl Service for Srv {
|
||||
type Request = ();
|
||||
impl Service<()> for Srv {
|
||||
type Response = ();
|
||||
type Error = ();
|
||||
type Future = Ready<Result<(), ()>>;
|
||||
|
||||
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
crate::always_ready!();
|
||||
|
||||
fn call(&mut self, _: ()) -> Self::Future {
|
||||
ok(())
|
||||
|
@@ -1,4 +1,4 @@
|
||||
use std::marker::PhantomData;
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use super::{IntoServiceFactory, ServiceFactory};
|
||||
|
||||
@@ -6,121 +6,123 @@ use super::{IntoServiceFactory, ServiceFactory};
|
||||
///
|
||||
/// Note that this function consumes the receiving service factory and returns
|
||||
/// a wrapped version of it.
|
||||
pub fn map_config<T, U, F, C>(factory: U, f: F) -> MapConfig<T, F, C>
|
||||
pub fn map_config<I, SF, S, Req, F, Cfg>(factory: I, f: F) -> MapConfig<SF, Req, F, Cfg>
|
||||
where
|
||||
T: ServiceFactory,
|
||||
U: IntoServiceFactory<T>,
|
||||
F: Fn(C) -> T::Config,
|
||||
I: IntoServiceFactory<SF, Req>,
|
||||
SF: ServiceFactory<Req>,
|
||||
F: Fn(Cfg) -> SF::Config,
|
||||
{
|
||||
MapConfig::new(factory.into_factory(), f)
|
||||
}
|
||||
|
||||
/// Replace config with unit
|
||||
pub fn unit_config<T, U, C>(factory: U) -> UnitConfig<T, C>
|
||||
/// Replace config with unit.
|
||||
pub fn unit_config<I, SF, Cfg, Req>(factory: I) -> UnitConfig<SF, Cfg, Req>
|
||||
where
|
||||
T: ServiceFactory<Config = ()>,
|
||||
U: IntoServiceFactory<T>,
|
||||
I: IntoServiceFactory<SF, Req>,
|
||||
SF: ServiceFactory<Req, Config = ()>,
|
||||
{
|
||||
UnitConfig::new(factory.into_factory())
|
||||
}
|
||||
|
||||
/// `map_config()` adapter service factory
|
||||
pub struct MapConfig<A, F, C> {
|
||||
a: A,
|
||||
f: F,
|
||||
e: PhantomData<C>,
|
||||
pub struct MapConfig<SF, Req, F, Cfg> {
|
||||
factory: SF,
|
||||
cfg_mapper: F,
|
||||
e: PhantomData<(Cfg, Req)>,
|
||||
}
|
||||
|
||||
impl<A, F, C> MapConfig<A, F, C> {
|
||||
impl<SF, Req, F, Cfg> MapConfig<SF, Req, F, Cfg> {
|
||||
/// Create new `MapConfig` combinator
|
||||
pub(crate) fn new(a: A, f: F) -> Self
|
||||
pub(crate) fn new(factory: SF, cfg_mapper: F) -> Self
|
||||
where
|
||||
A: ServiceFactory,
|
||||
F: Fn(C) -> A::Config,
|
||||
SF: ServiceFactory<Req>,
|
||||
F: Fn(Cfg) -> SF::Config,
|
||||
{
|
||||
Self {
|
||||
a,
|
||||
f,
|
||||
factory,
|
||||
cfg_mapper,
|
||||
e: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, F, C> Clone for MapConfig<A, F, C>
|
||||
impl<SF, Req, F, Cfg> Clone for MapConfig<SF, Req, F, Cfg>
|
||||
where
|
||||
A: Clone,
|
||||
SF: Clone,
|
||||
F: Clone,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
a: self.a.clone(),
|
||||
f: self.f.clone(),
|
||||
factory: self.factory.clone(),
|
||||
cfg_mapper: self.cfg_mapper.clone(),
|
||||
e: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, F, C> ServiceFactory for MapConfig<A, F, C>
|
||||
impl<SF, Req, F, Cfg> ServiceFactory<Req> for MapConfig<SF, Req, F, Cfg>
|
||||
where
|
||||
A: ServiceFactory,
|
||||
F: Fn(C) -> A::Config,
|
||||
SF: ServiceFactory<Req>,
|
||||
F: Fn(Cfg) -> SF::Config,
|
||||
{
|
||||
type Request = A::Request;
|
||||
type Response = A::Response;
|
||||
type Error = A::Error;
|
||||
type Response = SF::Response;
|
||||
type Error = SF::Error;
|
||||
|
||||
type Config = C;
|
||||
type Service = A::Service;
|
||||
type InitError = A::InitError;
|
||||
type Future = A::Future;
|
||||
type Config = Cfg;
|
||||
type Service = SF::Service;
|
||||
type InitError = SF::InitError;
|
||||
type Future = SF::Future;
|
||||
|
||||
fn new_service(&self, cfg: C) -> Self::Future {
|
||||
self.a.new_service((self.f)(cfg))
|
||||
fn new_service(&self, cfg: Self::Config) -> Self::Future {
|
||||
let mapped_cfg = (self.cfg_mapper)(cfg);
|
||||
self.factory.new_service(mapped_cfg)
|
||||
}
|
||||
}
|
||||
|
||||
/// `unit_config()` config combinator
|
||||
pub struct UnitConfig<A, C> {
|
||||
a: A,
|
||||
e: PhantomData<C>,
|
||||
pub struct UnitConfig<SF, Cfg, Req> {
|
||||
factory: SF,
|
||||
_phantom: PhantomData<(Cfg, Req)>,
|
||||
}
|
||||
|
||||
impl<A, C> UnitConfig<A, C>
|
||||
impl<SF, Cfg, Req> UnitConfig<SF, Cfg, Req>
|
||||
where
|
||||
A: ServiceFactory<Config = ()>,
|
||||
SF: ServiceFactory<Req, Config = ()>,
|
||||
{
|
||||
/// Create new `UnitConfig` combinator
|
||||
pub(crate) fn new(a: A) -> Self {
|
||||
Self { a, e: PhantomData }
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, C> Clone for UnitConfig<A, C>
|
||||
where
|
||||
A: Clone,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
pub(crate) fn new(factory: SF) -> Self {
|
||||
Self {
|
||||
a: self.a.clone(),
|
||||
e: PhantomData,
|
||||
factory,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, C> ServiceFactory for UnitConfig<A, C>
|
||||
impl<SF, Cfg, Req> Clone for UnitConfig<SF, Cfg, Req>
|
||||
where
|
||||
A: ServiceFactory<Config = ()>,
|
||||
SF: Clone,
|
||||
{
|
||||
type Request = A::Request;
|
||||
type Response = A::Response;
|
||||
type Error = A::Error;
|
||||
|
||||
type Config = C;
|
||||
type Service = A::Service;
|
||||
type InitError = A::InitError;
|
||||
type Future = A::Future;
|
||||
|
||||
fn new_service(&self, _: C) -> Self::Future {
|
||||
self.a.new_service(())
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
factory: self.factory.clone(),
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<SF, Cfg, Req> ServiceFactory<Req> for UnitConfig<SF, Cfg, Req>
|
||||
where
|
||||
SF: ServiceFactory<Req, Config = ()>,
|
||||
{
|
||||
type Response = SF::Response;
|
||||
type Error = SF::Error;
|
||||
|
||||
type Config = Cfg;
|
||||
type Service = SF::Service;
|
||||
type InitError = SF::InitError;
|
||||
type Future = SF::Future;
|
||||
|
||||
fn new_service(&self, _: Cfg) -> Self::Future {
|
||||
self.factory.new_service(())
|
||||
}
|
||||
}
|
||||
|
@@ -1,7 +1,11 @@
|
||||
use std::future::Future;
|
||||
use std::marker::PhantomData;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
use core::{
|
||||
future::Future,
|
||||
marker::PhantomData,
|
||||
pin::Pin,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
|
||||
use pin_project_lite::pin_project;
|
||||
|
||||
use super::{Service, ServiceFactory};
|
||||
|
||||
@@ -9,18 +13,18 @@ use super::{Service, ServiceFactory};
|
||||
/// error.
|
||||
///
|
||||
/// This is created by the `ServiceExt::map_err` method.
|
||||
pub struct MapErr<A, F, E> {
|
||||
service: A,
|
||||
pub struct MapErr<S, Req, F, E> {
|
||||
service: S,
|
||||
f: F,
|
||||
_t: PhantomData<E>,
|
||||
_t: PhantomData<(E, Req)>,
|
||||
}
|
||||
|
||||
impl<A, F, E> MapErr<A, F, E> {
|
||||
impl<S, Req, F, E> MapErr<S, Req, F, E> {
|
||||
/// Create new `MapErr` combinator
|
||||
pub(crate) fn new(service: A, f: F) -> Self
|
||||
pub(crate) fn new(service: S, f: F) -> Self
|
||||
where
|
||||
A: Service,
|
||||
F: Fn(A::Error) -> E,
|
||||
S: Service<Req>,
|
||||
F: Fn(S::Error) -> E,
|
||||
{
|
||||
Self {
|
||||
service,
|
||||
@@ -30,9 +34,9 @@ impl<A, F, E> MapErr<A, F, E> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, F, E> Clone for MapErr<A, F, E>
|
||||
impl<S, Req, F, E> Clone for MapErr<S, Req, F, E>
|
||||
where
|
||||
A: Clone,
|
||||
S: Clone,
|
||||
F: Clone,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
@@ -44,39 +48,39 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, F, E> Service for MapErr<A, F, E>
|
||||
impl<A, Req, F, E> Service<Req> for MapErr<A, Req, F, E>
|
||||
where
|
||||
A: Service,
|
||||
A: Service<Req>,
|
||||
F: Fn(A::Error) -> E + Clone,
|
||||
{
|
||||
type Request = A::Request;
|
||||
type Response = A::Response;
|
||||
type Error = E;
|
||||
type Future = MapErrFuture<A, F, E>;
|
||||
type Future = MapErrFuture<A, Req, F, E>;
|
||||
|
||||
fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
self.service.poll_ready(ctx).map_err(&self.f)
|
||||
}
|
||||
|
||||
fn call(&mut self, req: A::Request) -> Self::Future {
|
||||
fn call(&mut self, req: Req) -> Self::Future {
|
||||
MapErrFuture::new(self.service.call(req), self.f.clone())
|
||||
}
|
||||
}
|
||||
|
||||
#[pin_project::pin_project]
|
||||
pub struct MapErrFuture<A, F, E>
|
||||
where
|
||||
A: Service,
|
||||
F: Fn(A::Error) -> E,
|
||||
{
|
||||
f: F,
|
||||
#[pin]
|
||||
fut: A::Future,
|
||||
pin_project! {
|
||||
pub struct MapErrFuture<A, Req, F, E>
|
||||
where
|
||||
A: Service<Req>,
|
||||
F: Fn(A::Error) -> E,
|
||||
{
|
||||
f: F,
|
||||
#[pin]
|
||||
fut: A::Future,
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, F, E> MapErrFuture<A, F, E>
|
||||
impl<A, Req, F, E> MapErrFuture<A, Req, F, E>
|
||||
where
|
||||
A: Service,
|
||||
A: Service<Req>,
|
||||
F: Fn(A::Error) -> E,
|
||||
{
|
||||
fn new(fut: A::Future, f: F) -> Self {
|
||||
@@ -84,9 +88,9 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, F, E> Future for MapErrFuture<A, F, E>
|
||||
impl<A, Req, F, E> Future for MapErrFuture<A, Req, F, E>
|
||||
where
|
||||
A: Service,
|
||||
A: Service<Req>,
|
||||
F: Fn(A::Error) -> E,
|
||||
{
|
||||
type Output = Result<A::Response, E>;
|
||||
@@ -101,19 +105,19 @@ where
|
||||
/// service's error.
|
||||
///
|
||||
/// This is created by the `NewServiceExt::map_err` method.
|
||||
pub struct MapErrServiceFactory<A, F, E>
|
||||
pub struct MapErrServiceFactory<A, Req, F, E>
|
||||
where
|
||||
A: ServiceFactory,
|
||||
A: ServiceFactory<Req>,
|
||||
F: Fn(A::Error) -> E + Clone,
|
||||
{
|
||||
a: A,
|
||||
f: F,
|
||||
e: PhantomData<E>,
|
||||
e: PhantomData<(E, Req)>,
|
||||
}
|
||||
|
||||
impl<A, F, E> MapErrServiceFactory<A, F, E>
|
||||
impl<A, Req, F, E> MapErrServiceFactory<A, Req, F, E>
|
||||
where
|
||||
A: ServiceFactory,
|
||||
A: ServiceFactory<Req>,
|
||||
F: Fn(A::Error) -> E + Clone,
|
||||
{
|
||||
/// Create new `MapErr` new service instance
|
||||
@@ -126,9 +130,9 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, F, E> Clone for MapErrServiceFactory<A, F, E>
|
||||
impl<A, Req, F, E> Clone for MapErrServiceFactory<A, Req, F, E>
|
||||
where
|
||||
A: ServiceFactory + Clone,
|
||||
A: ServiceFactory<Req> + Clone,
|
||||
F: Fn(A::Error) -> E + Clone,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
@@ -140,39 +144,39 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, F, E> ServiceFactory for MapErrServiceFactory<A, F, E>
|
||||
impl<A, Req, F, E> ServiceFactory<Req> for MapErrServiceFactory<A, Req, F, E>
|
||||
where
|
||||
A: ServiceFactory,
|
||||
A: ServiceFactory<Req>,
|
||||
F: Fn(A::Error) -> E + Clone,
|
||||
{
|
||||
type Request = A::Request;
|
||||
type Response = A::Response;
|
||||
type Error = E;
|
||||
|
||||
type Config = A::Config;
|
||||
type Service = MapErr<A::Service, F, E>;
|
||||
type Service = MapErr<A::Service, Req, F, E>;
|
||||
type InitError = A::InitError;
|
||||
type Future = MapErrServiceFuture<A, F, E>;
|
||||
type Future = MapErrServiceFuture<A, Req, F, E>;
|
||||
|
||||
fn new_service(&self, cfg: A::Config) -> Self::Future {
|
||||
MapErrServiceFuture::new(self.a.new_service(cfg), self.f.clone())
|
||||
}
|
||||
}
|
||||
|
||||
#[pin_project::pin_project]
|
||||
pub struct MapErrServiceFuture<A, F, E>
|
||||
where
|
||||
A: ServiceFactory,
|
||||
F: Fn(A::Error) -> E,
|
||||
{
|
||||
#[pin]
|
||||
fut: A::Future,
|
||||
f: F,
|
||||
pin_project! {
|
||||
pub struct MapErrServiceFuture<A, Req, F, E>
|
||||
where
|
||||
A: ServiceFactory<Req>,
|
||||
F: Fn(A::Error) -> E,
|
||||
{
|
||||
#[pin]
|
||||
fut: A::Future,
|
||||
f: F,
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, F, E> MapErrServiceFuture<A, F, E>
|
||||
impl<A, Req, F, E> MapErrServiceFuture<A, Req, F, E>
|
||||
where
|
||||
A: ServiceFactory,
|
||||
A: ServiceFactory<Req>,
|
||||
F: Fn(A::Error) -> E,
|
||||
{
|
||||
fn new(fut: A::Future, f: F) -> Self {
|
||||
@@ -180,12 +184,12 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, F, E> Future for MapErrServiceFuture<A, F, E>
|
||||
impl<A, Req, F, E> Future for MapErrServiceFuture<A, Req, F, E>
|
||||
where
|
||||
A: ServiceFactory,
|
||||
A: ServiceFactory<Req>,
|
||||
F: Fn(A::Error) -> E + Clone,
|
||||
{
|
||||
type Output = Result<MapErr<A::Service, F, E>, A::InitError>;
|
||||
type Output = Result<MapErr<A::Service, Req, F, E>, A::InitError>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let this = self.project();
|
||||
@@ -199,15 +203,17 @@ where
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use futures_util::future::{err, lazy, ok, Ready};
|
||||
use futures_util::future::lazy;
|
||||
|
||||
use super::*;
|
||||
use crate::{IntoServiceFactory, Service, ServiceFactory};
|
||||
use crate::{
|
||||
err, ok, IntoServiceFactory, Ready, Service, ServiceExt, ServiceFactory,
|
||||
ServiceFactoryExt,
|
||||
};
|
||||
|
||||
struct Srv;
|
||||
|
||||
impl Service for Srv {
|
||||
type Request = ();
|
||||
impl Service<()> for Srv {
|
||||
type Response = ();
|
||||
type Error = ();
|
||||
type Future = Ready<Result<(), ()>>;
|
||||
|
@@ -1,21 +1,25 @@
|
||||
use std::future::Future;
|
||||
use std::marker::PhantomData;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
use core::{
|
||||
future::Future,
|
||||
marker::PhantomData,
|
||||
pin::Pin,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
|
||||
use pin_project_lite::pin_project;
|
||||
|
||||
use super::ServiceFactory;
|
||||
|
||||
/// `MapInitErr` service combinator
|
||||
pub struct MapInitErr<A, F, E> {
|
||||
pub struct MapInitErr<A, F, Req, Err> {
|
||||
a: A,
|
||||
f: F,
|
||||
e: PhantomData<E>,
|
||||
e: PhantomData<(Req, Err)>,
|
||||
}
|
||||
|
||||
impl<A, F, E> MapInitErr<A, F, E>
|
||||
impl<A, F, Req, Err> MapInitErr<A, F, Req, Err>
|
||||
where
|
||||
A: ServiceFactory,
|
||||
F: Fn(A::InitError) -> E,
|
||||
A: ServiceFactory<Req>,
|
||||
F: Fn(A::InitError) -> Err,
|
||||
{
|
||||
/// Create new `MapInitErr` combinator
|
||||
pub(crate) fn new(a: A, f: F) -> Self {
|
||||
@@ -27,7 +31,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, F, E> Clone for MapInitErr<A, F, E>
|
||||
impl<A, F, Req, E> Clone for MapInitErr<A, F, Req, E>
|
||||
where
|
||||
A: Clone,
|
||||
F: Clone,
|
||||
@@ -41,39 +45,39 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, F, E> ServiceFactory for MapInitErr<A, F, E>
|
||||
impl<A, F, Req, E> ServiceFactory<Req> for MapInitErr<A, F, Req, E>
|
||||
where
|
||||
A: ServiceFactory,
|
||||
A: ServiceFactory<Req>,
|
||||
F: Fn(A::InitError) -> E + Clone,
|
||||
{
|
||||
type Request = A::Request;
|
||||
type Response = A::Response;
|
||||
type Error = A::Error;
|
||||
|
||||
type Config = A::Config;
|
||||
type Service = A::Service;
|
||||
type InitError = E;
|
||||
type Future = MapInitErrFuture<A, F, E>;
|
||||
type Future = MapInitErrFuture<A, F, Req, E>;
|
||||
|
||||
fn new_service(&self, cfg: A::Config) -> Self::Future {
|
||||
MapInitErrFuture::new(self.a.new_service(cfg), self.f.clone())
|
||||
}
|
||||
}
|
||||
|
||||
#[pin_project::pin_project]
|
||||
pub struct MapInitErrFuture<A, F, E>
|
||||
where
|
||||
A: ServiceFactory,
|
||||
F: Fn(A::InitError) -> E,
|
||||
{
|
||||
f: F,
|
||||
#[pin]
|
||||
fut: A::Future,
|
||||
pin_project! {
|
||||
pub struct MapInitErrFuture<A, F, Req, E>
|
||||
where
|
||||
A: ServiceFactory<Req>,
|
||||
F: Fn(A::InitError) -> E,
|
||||
{
|
||||
f: F,
|
||||
#[pin]
|
||||
fut: A::Future,
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, F, E> MapInitErrFuture<A, F, E>
|
||||
impl<A, F, Req, E> MapInitErrFuture<A, F, Req, E>
|
||||
where
|
||||
A: ServiceFactory,
|
||||
A: ServiceFactory<Req>,
|
||||
F: Fn(A::InitError) -> E,
|
||||
{
|
||||
fn new(fut: A::Future, f: F) -> Self {
|
||||
@@ -81,9 +85,9 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, F, E> Future for MapInitErrFuture<A, F, E>
|
||||
impl<A, F, Req, E> Future for MapInitErrFuture<A, F, Req, E>
|
||||
where
|
||||
A: ServiceFactory,
|
||||
A: ServiceFactory<Req>,
|
||||
F: Fn(A::InitError) -> E,
|
||||
{
|
||||
type Output = Result<A::Service, E>;
|
||||
|
@@ -1,8 +1,9 @@
|
||||
use std::future::Future;
|
||||
use std::task::{Context, Poll};
|
||||
use core::{
|
||||
marker::PhantomData,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
|
||||
use crate::and_then::{AndThenService, AndThenServiceFactory};
|
||||
use crate::and_then_apply_fn::{AndThenApplyFn, AndThenApplyFnFactory};
|
||||
use crate::map::{Map, MapServiceFactory};
|
||||
use crate::map_err::{MapErr, MapErrServiceFactory};
|
||||
use crate::map_init_err::MapInitErr;
|
||||
@@ -10,33 +11,39 @@ use crate::then::{ThenService, ThenServiceFactory};
|
||||
use crate::{IntoService, IntoServiceFactory, Service, ServiceFactory};
|
||||
|
||||
/// Construct new pipeline with one service in pipeline chain.
|
||||
pub fn pipeline<F, T>(service: F) -> Pipeline<T>
|
||||
pub fn pipeline<I, S, Req>(service: I) -> Pipeline<S, Req>
|
||||
where
|
||||
F: IntoService<T>,
|
||||
T: Service,
|
||||
I: IntoService<S, Req>,
|
||||
S: Service<Req>,
|
||||
{
|
||||
Pipeline {
|
||||
service: service.into_service(),
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct new pipeline factory with one service factory.
|
||||
pub fn pipeline_factory<T, F>(factory: F) -> PipelineFactory<T>
|
||||
pub fn pipeline_factory<I, SF, Req>(factory: I) -> PipelineFactory<SF, Req>
|
||||
where
|
||||
T: ServiceFactory,
|
||||
F: IntoServiceFactory<T>,
|
||||
I: IntoServiceFactory<SF, Req>,
|
||||
SF: ServiceFactory<Req>,
|
||||
{
|
||||
PipelineFactory {
|
||||
factory: factory.into_factory(),
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Pipeline service - pipeline allows to compose multiple service into one service.
|
||||
pub struct Pipeline<T> {
|
||||
service: T,
|
||||
pub struct Pipeline<S, Req> {
|
||||
service: S,
|
||||
_phantom: PhantomData<Req>,
|
||||
}
|
||||
|
||||
impl<T: Service> Pipeline<T> {
|
||||
impl<S, Req> Pipeline<S, Req>
|
||||
where
|
||||
S: Service<Req>,
|
||||
{
|
||||
/// Call another service after call to this one has resolved successfully.
|
||||
///
|
||||
/// This function can be used to chain two services together and ensure that
|
||||
@@ -46,41 +53,18 @@ impl<T: Service> Pipeline<T> {
|
||||
///
|
||||
/// Note that this function consumes the receiving service and returns a
|
||||
/// wrapped version of it.
|
||||
pub fn and_then<F, U>(
|
||||
pub fn and_then<I, S1>(
|
||||
self,
|
||||
service: F,
|
||||
) -> Pipeline<
|
||||
impl Service<Request = T::Request, Response = U::Response, Error = T::Error> + Clone,
|
||||
>
|
||||
service: I,
|
||||
) -> Pipeline<impl Service<Req, Response = S1::Response, Error = S::Error> + Clone, Req>
|
||||
where
|
||||
Self: Sized,
|
||||
F: IntoService<U>,
|
||||
U: Service<Request = T::Response, Error = T::Error>,
|
||||
I: IntoService<S1, S::Response>,
|
||||
S1: Service<S::Response, Error = S::Error>,
|
||||
{
|
||||
Pipeline {
|
||||
service: AndThenService::new(self.service, service.into_service()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Apply function to specified service and use it as a next service in
|
||||
/// chain.
|
||||
///
|
||||
/// Short version of `pipeline_factory(...).and_then(apply_fn_factory(...))`
|
||||
pub fn and_then_apply_fn<U, I, F, Fut, Res, Err>(
|
||||
self,
|
||||
service: I,
|
||||
f: F,
|
||||
) -> Pipeline<impl Service<Request = T::Request, Response = Res, Error = Err> + Clone>
|
||||
where
|
||||
Self: Sized,
|
||||
I: IntoService<U>,
|
||||
U: Service,
|
||||
F: FnMut(T::Response, &mut U) -> Fut,
|
||||
Fut: Future<Output = Result<Res, Err>>,
|
||||
Err: From<T::Error> + From<U::Error>,
|
||||
{
|
||||
Pipeline {
|
||||
service: AndThenApplyFn::new(self.service, service.into_service(), f),
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,19 +73,18 @@ impl<T: Service> Pipeline<T> {
|
||||
///
|
||||
/// Note that this function consumes the receiving pipeline and returns a
|
||||
/// wrapped version of it.
|
||||
pub fn then<F, U>(
|
||||
pub fn then<F, S1>(
|
||||
self,
|
||||
service: F,
|
||||
) -> Pipeline<
|
||||
impl Service<Request = T::Request, Response = U::Response, Error = T::Error> + Clone,
|
||||
>
|
||||
) -> Pipeline<impl Service<Req, Response = S1::Response, Error = S::Error> + Clone, Req>
|
||||
where
|
||||
Self: Sized,
|
||||
F: IntoService<U>,
|
||||
U: Service<Request = Result<T::Response, T::Error>, Error = T::Error>,
|
||||
F: IntoService<S1, Result<S::Response, S::Error>>,
|
||||
S1: Service<Result<S::Response, S::Error>, Error = S::Error>,
|
||||
{
|
||||
Pipeline {
|
||||
service: ThenService::new(self.service, service.into_service()),
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,13 +97,14 @@ impl<T: Service> Pipeline<T> {
|
||||
/// Note that this function consumes the receiving service and returns a
|
||||
/// wrapped version of it, similar to the existing `map` methods in the
|
||||
/// standard library.
|
||||
pub fn map<F, R>(self, f: F) -> Pipeline<Map<T, F, R>>
|
||||
pub fn map<F, R>(self, f: F) -> Pipeline<Map<S, F, Req, R>, Req>
|
||||
where
|
||||
Self: Sized,
|
||||
F: FnMut(T::Response) -> R,
|
||||
F: FnMut(S::Response) -> R,
|
||||
{
|
||||
Pipeline {
|
||||
service: Map::new(self.service, f),
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,114 +116,85 @@ impl<T: Service> Pipeline<T> {
|
||||
///
|
||||
/// Note that this function consumes the receiving service and returns a
|
||||
/// wrapped version of it.
|
||||
pub fn map_err<F, E>(self, f: F) -> Pipeline<MapErr<T, F, E>>
|
||||
pub fn map_err<F, E>(self, f: F) -> Pipeline<MapErr<S, Req, F, E>, Req>
|
||||
where
|
||||
Self: Sized,
|
||||
F: Fn(T::Error) -> E,
|
||||
F: Fn(S::Error) -> E,
|
||||
{
|
||||
Pipeline {
|
||||
service: MapErr::new(self.service, f),
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Clone for Pipeline<T>
|
||||
impl<T, Req> Clone for Pipeline<T, Req>
|
||||
where
|
||||
T: Clone,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Pipeline {
|
||||
service: self.service.clone(),
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Service> Service for Pipeline<T> {
|
||||
type Request = T::Request;
|
||||
type Response = T::Response;
|
||||
type Error = T::Error;
|
||||
type Future = T::Future;
|
||||
impl<S: Service<Req>, Req> Service<Req> for Pipeline<S, Req> {
|
||||
type Response = S::Response;
|
||||
type Error = S::Error;
|
||||
type Future = S::Future;
|
||||
|
||||
#[inline]
|
||||
fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll<Result<(), T::Error>> {
|
||||
fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll<Result<(), S::Error>> {
|
||||
self.service.poll_ready(ctx)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn call(&mut self, req: T::Request) -> Self::Future {
|
||||
fn call(&mut self, req: Req) -> Self::Future {
|
||||
self.service.call(req)
|
||||
}
|
||||
}
|
||||
|
||||
/// Pipeline factory
|
||||
pub struct PipelineFactory<T> {
|
||||
factory: T,
|
||||
pub struct PipelineFactory<SF, Req> {
|
||||
factory: SF,
|
||||
_phantom: PhantomData<Req>,
|
||||
}
|
||||
|
||||
impl<T: ServiceFactory> PipelineFactory<T> {
|
||||
impl<SF, Req> PipelineFactory<SF, Req>
|
||||
where
|
||||
SF: ServiceFactory<Req>,
|
||||
{
|
||||
/// Call another service after call to this one has resolved successfully.
|
||||
pub fn and_then<F, U>(
|
||||
pub fn and_then<I, SF1>(
|
||||
self,
|
||||
factory: F,
|
||||
factory: I,
|
||||
) -> PipelineFactory<
|
||||
impl ServiceFactory<
|
||||
Request = T::Request,
|
||||
Response = U::Response,
|
||||
Error = T::Error,
|
||||
Config = T::Config,
|
||||
InitError = T::InitError,
|
||||
Service = impl Service<
|
||||
Request = T::Request,
|
||||
Response = U::Response,
|
||||
Error = T::Error,
|
||||
> + Clone,
|
||||
Req,
|
||||
Response = SF1::Response,
|
||||
Error = SF::Error,
|
||||
Config = SF::Config,
|
||||
InitError = SF::InitError,
|
||||
Service = impl Service<Req, Response = SF1::Response, Error = SF::Error> + Clone,
|
||||
> + Clone,
|
||||
Req,
|
||||
>
|
||||
where
|
||||
Self: Sized,
|
||||
T::Config: Clone,
|
||||
F: IntoServiceFactory<U>,
|
||||
U: ServiceFactory<
|
||||
Config = T::Config,
|
||||
Request = T::Response,
|
||||
Error = T::Error,
|
||||
InitError = T::InitError,
|
||||
SF::Config: Clone,
|
||||
I: IntoServiceFactory<SF1, SF::Response>,
|
||||
SF1: ServiceFactory<
|
||||
SF::Response,
|
||||
Config = SF::Config,
|
||||
Error = SF::Error,
|
||||
InitError = SF::InitError,
|
||||
>,
|
||||
{
|
||||
PipelineFactory {
|
||||
factory: AndThenServiceFactory::new(self.factory, factory.into_factory()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Apply function to specified service and use it as a next service in
|
||||
/// chain.
|
||||
///
|
||||
/// Short version of `pipeline_factory(...).and_then(apply_fn_factory(...))`
|
||||
pub fn and_then_apply_fn<U, I, F, Fut, Res, Err>(
|
||||
self,
|
||||
factory: I,
|
||||
f: F,
|
||||
) -> PipelineFactory<
|
||||
impl ServiceFactory<
|
||||
Request = T::Request,
|
||||
Response = Res,
|
||||
Error = Err,
|
||||
Config = T::Config,
|
||||
InitError = T::InitError,
|
||||
Service = impl Service<Request = T::Request, Response = Res, Error = Err> + Clone,
|
||||
> + Clone,
|
||||
>
|
||||
where
|
||||
Self: Sized,
|
||||
T::Config: Clone,
|
||||
I: IntoServiceFactory<U>,
|
||||
U: ServiceFactory<Config = T::Config, InitError = T::InitError>,
|
||||
F: FnMut(T::Response, &mut U::Service) -> Fut + Clone,
|
||||
Fut: Future<Output = Result<Res, Err>>,
|
||||
Err: From<T::Error> + From<U::Error>,
|
||||
{
|
||||
PipelineFactory {
|
||||
factory: AndThenApplyFnFactory::new(self.factory, factory.into_factory(), f),
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,96 +204,103 @@ impl<T: ServiceFactory> PipelineFactory<T> {
|
||||
///
|
||||
/// Note that this function consumes the receiving pipeline and returns a
|
||||
/// wrapped version of it.
|
||||
pub fn then<F, U>(
|
||||
pub fn then<I, SF1>(
|
||||
self,
|
||||
factory: F,
|
||||
factory: I,
|
||||
) -> PipelineFactory<
|
||||
impl ServiceFactory<
|
||||
Request = T::Request,
|
||||
Response = U::Response,
|
||||
Error = T::Error,
|
||||
Config = T::Config,
|
||||
InitError = T::InitError,
|
||||
Service = impl Service<
|
||||
Request = T::Request,
|
||||
Response = U::Response,
|
||||
Error = T::Error,
|
||||
> + Clone,
|
||||
Req,
|
||||
Response = SF1::Response,
|
||||
Error = SF::Error,
|
||||
Config = SF::Config,
|
||||
InitError = SF::InitError,
|
||||
Service = impl Service<Req, Response = SF1::Response, Error = SF::Error> + Clone,
|
||||
> + Clone,
|
||||
Req,
|
||||
>
|
||||
where
|
||||
Self: Sized,
|
||||
T::Config: Clone,
|
||||
F: IntoServiceFactory<U>,
|
||||
U: ServiceFactory<
|
||||
Config = T::Config,
|
||||
Request = Result<T::Response, T::Error>,
|
||||
Error = T::Error,
|
||||
InitError = T::InitError,
|
||||
SF::Config: Clone,
|
||||
I: IntoServiceFactory<SF1, Result<SF::Response, SF::Error>>,
|
||||
SF1: ServiceFactory<
|
||||
Result<SF::Response, SF::Error>,
|
||||
Config = SF::Config,
|
||||
Error = SF::Error,
|
||||
InitError = SF::InitError,
|
||||
>,
|
||||
{
|
||||
PipelineFactory {
|
||||
factory: ThenServiceFactory::new(self.factory, factory.into_factory()),
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Map this service's output to a different type, returning a new service
|
||||
/// of the resulting type.
|
||||
pub fn map<F, R>(self, f: F) -> PipelineFactory<MapServiceFactory<T, F, R>>
|
||||
pub fn map<F, R>(self, f: F) -> PipelineFactory<MapServiceFactory<SF, F, Req, R>, Req>
|
||||
where
|
||||
Self: Sized,
|
||||
F: FnMut(T::Response) -> R + Clone,
|
||||
F: FnMut(SF::Response) -> R + Clone,
|
||||
{
|
||||
PipelineFactory {
|
||||
factory: MapServiceFactory::new(self.factory, f),
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Map this service's error to a different error, returning a new service.
|
||||
pub fn map_err<F, E>(self, f: F) -> PipelineFactory<MapErrServiceFactory<T, F, E>>
|
||||
pub fn map_err<F, E>(
|
||||
self,
|
||||
f: F,
|
||||
) -> PipelineFactory<MapErrServiceFactory<SF, Req, F, E>, Req>
|
||||
where
|
||||
Self: Sized,
|
||||
F: Fn(T::Error) -> E + Clone,
|
||||
F: Fn(SF::Error) -> E + Clone,
|
||||
{
|
||||
PipelineFactory {
|
||||
factory: MapErrServiceFactory::new(self.factory, f),
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Map this factory's init error to a different error, returning a new service.
|
||||
pub fn map_init_err<F, E>(self, f: F) -> PipelineFactory<MapInitErr<T, F, E>>
|
||||
pub fn map_init_err<F, E>(self, f: F) -> PipelineFactory<MapInitErr<SF, F, Req, E>, Req>
|
||||
where
|
||||
Self: Sized,
|
||||
F: Fn(T::InitError) -> E + Clone,
|
||||
F: Fn(SF::InitError) -> E + Clone,
|
||||
{
|
||||
PipelineFactory {
|
||||
factory: MapInitErr::new(self.factory, f),
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Clone for PipelineFactory<T>
|
||||
impl<T, Req> Clone for PipelineFactory<T, Req>
|
||||
where
|
||||
T: Clone,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
PipelineFactory {
|
||||
factory: self.factory.clone(),
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ServiceFactory> ServiceFactory for PipelineFactory<T> {
|
||||
type Config = T::Config;
|
||||
type Request = T::Request;
|
||||
type Response = T::Response;
|
||||
type Error = T::Error;
|
||||
type Service = T::Service;
|
||||
type InitError = T::InitError;
|
||||
type Future = T::Future;
|
||||
impl<SF, Req> ServiceFactory<Req> for PipelineFactory<SF, Req>
|
||||
where
|
||||
SF: ServiceFactory<Req>,
|
||||
{
|
||||
type Config = SF::Config;
|
||||
type Response = SF::Response;
|
||||
type Error = SF::Error;
|
||||
type Service = SF::Service;
|
||||
type InitError = SF::InitError;
|
||||
type Future = SF::Future;
|
||||
|
||||
#[inline]
|
||||
fn new_service(&self, cfg: T::Config) -> Self::Future {
|
||||
fn new_service(&self, cfg: SF::Config) -> Self::Future {
|
||||
self.factory.new_service(cfg)
|
||||
}
|
||||
}
|
||||
|
54
actix-service/src/ready.rs
Normal file
54
actix-service/src/ready.rs
Normal file
@@ -0,0 +1,54 @@
|
||||
//! When MSRV is 1.48, replace with `core::future::Ready` and `core::future::ready()`.
|
||||
|
||||
use core::{
|
||||
future::Future,
|
||||
pin::Pin,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
|
||||
/// Future for the [`ready`](ready()) function.
|
||||
#[derive(Debug, Clone)]
|
||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
||||
pub struct Ready<T> {
|
||||
val: Option<T>,
|
||||
}
|
||||
|
||||
impl<T> Ready<T> {
|
||||
/// Unwraps the value from this immediately ready future.
|
||||
#[inline]
|
||||
pub fn into_inner(mut self) -> T {
|
||||
self.val.take().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Unpin for Ready<T> {}
|
||||
|
||||
impl<T> Future for Ready<T> {
|
||||
type Output = T;
|
||||
|
||||
#[inline]
|
||||
fn poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<T> {
|
||||
let val = self.val.take().expect("Ready can not be polled twice.");
|
||||
Poll::Ready(val)
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a future that is immediately ready with a value.
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn ready<T>(val: T) -> Ready<T> {
|
||||
Ready { val: Some(val) }
|
||||
}
|
||||
|
||||
/// Create a future that is immediately ready with a success value.
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn ok<T, E>(val: T) -> Ready<Result<T, E>> {
|
||||
Ready { val: Some(Ok(val)) }
|
||||
}
|
||||
|
||||
/// Create a future that is immediately ready with an error value.
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn err<T, E>(err: E) -> Ready<Result<T, E>> {
|
||||
Ready {
|
||||
val: Some(Err(err)),
|
||||
}
|
||||
}
|
@@ -1,8 +1,13 @@
|
||||
use std::cell::RefCell;
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use std::rc::Rc;
|
||||
use std::task::{Context, Poll};
|
||||
use alloc::rc::Rc;
|
||||
use core::{
|
||||
cell::RefCell,
|
||||
future::Future,
|
||||
marker::PhantomData,
|
||||
pin::Pin,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
|
||||
use pin_project_lite::pin_project;
|
||||
|
||||
use super::{Service, ServiceFactory};
|
||||
|
||||
@@ -10,34 +15,33 @@ use super::{Service, ServiceFactory};
|
||||
/// another service.
|
||||
///
|
||||
/// This is created by the `Pipeline::then` method.
|
||||
pub(crate) struct ThenService<A, B>(Rc<RefCell<(A, B)>>);
|
||||
pub(crate) struct ThenService<A, B, Req>(Rc<RefCell<(A, B)>>, PhantomData<Req>);
|
||||
|
||||
impl<A, B> ThenService<A, B> {
|
||||
impl<A, B, Req> ThenService<A, B, Req> {
|
||||
/// Create new `.then()` combinator
|
||||
pub(crate) fn new(a: A, b: B) -> ThenService<A, B>
|
||||
pub(crate) fn new(a: A, b: B) -> ThenService<A, B, Req>
|
||||
where
|
||||
A: Service,
|
||||
B: Service<Request = Result<A::Response, A::Error>, Error = A::Error>,
|
||||
A: Service<Req>,
|
||||
B: Service<Result<A::Response, A::Error>, Error = A::Error>,
|
||||
{
|
||||
Self(Rc::new(RefCell::new((a, b))))
|
||||
Self(Rc::new(RefCell::new((a, b))), PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B> Clone for ThenService<A, B> {
|
||||
impl<A, B, Req> Clone for ThenService<A, B, Req> {
|
||||
fn clone(&self) -> Self {
|
||||
ThenService(self.0.clone())
|
||||
ThenService(self.0.clone(), PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B> Service for ThenService<A, B>
|
||||
impl<A, B, Req> Service<Req> for ThenService<A, B, Req>
|
||||
where
|
||||
A: Service,
|
||||
B: Service<Request = Result<A::Response, A::Error>, Error = A::Error>,
|
||||
A: Service<Req>,
|
||||
B: Service<Result<A::Response, A::Error>, Error = A::Error>,
|
||||
{
|
||||
type Request = A::Request;
|
||||
type Response = B::Response;
|
||||
type Error = B::Error;
|
||||
type Future = ThenServiceResponse<A, B>;
|
||||
type Future = ThenServiceResponse<A, B, Req>;
|
||||
|
||||
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
let mut srv = self.0.borrow_mut();
|
||||
@@ -49,38 +53,44 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn call(&mut self, req: A::Request) -> Self::Future {
|
||||
fn call(&mut self, req: Req) -> Self::Future {
|
||||
ThenServiceResponse {
|
||||
state: State::A(self.0.borrow_mut().0.call(req), Some(self.0.clone())),
|
||||
state: State::A {
|
||||
fut: self.0.borrow_mut().0.call(req),
|
||||
b: Some(self.0.clone()),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[pin_project::pin_project]
|
||||
pub(crate) struct ThenServiceResponse<A, B>
|
||||
where
|
||||
A: Service,
|
||||
B: Service<Request = Result<A::Response, A::Error>>,
|
||||
{
|
||||
#[pin]
|
||||
state: State<A, B>,
|
||||
pin_project! {
|
||||
pub(crate) struct ThenServiceResponse<A, B, Req>
|
||||
where
|
||||
A: Service<Req>,
|
||||
B: Service<Result<A::Response, A::Error>>,
|
||||
{
|
||||
#[pin]
|
||||
state: State<A, B, Req>,
|
||||
}
|
||||
}
|
||||
|
||||
#[pin_project::pin_project(project = StateProj)]
|
||||
enum State<A, B>
|
||||
where
|
||||
A: Service,
|
||||
B: Service<Request = Result<A::Response, A::Error>>,
|
||||
{
|
||||
A(#[pin] A::Future, Option<Rc<RefCell<(A, B)>>>),
|
||||
B(#[pin] B::Future),
|
||||
Empty,
|
||||
pin_project! {
|
||||
#[project = StateProj]
|
||||
enum State<A, B, Req>
|
||||
where
|
||||
A: Service<Req>,
|
||||
B: Service<Result<A::Response, A::Error>>,
|
||||
{
|
||||
A { #[pin] fut: A::Future, b: Option<Rc<RefCell<(A, B)>>> },
|
||||
B { #[pin] fut: B::Future },
|
||||
Empty,
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B> Future for ThenServiceResponse<A, B>
|
||||
impl<A, B, Req> Future for ThenServiceResponse<A, B, Req>
|
||||
where
|
||||
A: Service,
|
||||
B: Service<Request = Result<A::Response, A::Error>>,
|
||||
A: Service<Req>,
|
||||
B: Service<Result<A::Response, A::Error>>,
|
||||
{
|
||||
type Output = Result<B::Response, B::Error>;
|
||||
|
||||
@@ -88,17 +98,17 @@ where
|
||||
let mut this = self.as_mut().project();
|
||||
|
||||
match this.state.as_mut().project() {
|
||||
StateProj::A(fut, b) => match fut.poll(cx) {
|
||||
StateProj::A { fut, b } => match fut.poll(cx) {
|
||||
Poll::Ready(res) => {
|
||||
let b = b.take().unwrap();
|
||||
this.state.set(State::Empty); // drop fut A
|
||||
let fut = b.borrow_mut().1.call(res);
|
||||
this.state.set(State::B(fut));
|
||||
this.state.set(State::B { fut });
|
||||
self.poll(cx)
|
||||
}
|
||||
Poll::Pending => Poll::Pending,
|
||||
},
|
||||
StateProj::B(fut) => fut.poll(cx).map(|r| {
|
||||
StateProj::B { fut } => fut.poll(cx).map(|r| {
|
||||
this.state.set(State::Empty);
|
||||
r
|
||||
}),
|
||||
@@ -110,44 +120,43 @@ where
|
||||
}
|
||||
|
||||
/// `.then()` service factory combinator
|
||||
pub(crate) struct ThenServiceFactory<A, B>(Rc<(A, B)>);
|
||||
pub(crate) struct ThenServiceFactory<A, B, Req>(Rc<(A, B)>, PhantomData<Req>);
|
||||
|
||||
impl<A, B> ThenServiceFactory<A, B>
|
||||
impl<A, B, Req> ThenServiceFactory<A, B, Req>
|
||||
where
|
||||
A: ServiceFactory,
|
||||
A: ServiceFactory<Req>,
|
||||
A::Config: Clone,
|
||||
B: ServiceFactory<
|
||||
Result<A::Response, A::Error>,
|
||||
Config = A::Config,
|
||||
Request = Result<A::Response, A::Error>,
|
||||
Error = A::Error,
|
||||
InitError = A::InitError,
|
||||
>,
|
||||
{
|
||||
/// Create new `AndThen` combinator
|
||||
pub(crate) fn new(a: A, b: B) -> Self {
|
||||
Self(Rc::new((a, b)))
|
||||
Self(Rc::new((a, b)), PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B> ServiceFactory for ThenServiceFactory<A, B>
|
||||
impl<A, B, Req> ServiceFactory<Req> for ThenServiceFactory<A, B, Req>
|
||||
where
|
||||
A: ServiceFactory,
|
||||
A: ServiceFactory<Req>,
|
||||
A::Config: Clone,
|
||||
B: ServiceFactory<
|
||||
Result<A::Response, A::Error>,
|
||||
Config = A::Config,
|
||||
Request = Result<A::Response, A::Error>,
|
||||
Error = A::Error,
|
||||
InitError = A::InitError,
|
||||
>,
|
||||
{
|
||||
type Request = A::Request;
|
||||
type Response = B::Response;
|
||||
type Error = A::Error;
|
||||
|
||||
type Config = A::Config;
|
||||
type Service = ThenService<A::Service, B::Service>;
|
||||
type Service = ThenService<A::Service, B::Service, Req>;
|
||||
type InitError = A::InitError;
|
||||
type Future = ThenServiceFactoryResponse<A, B>;
|
||||
type Future = ThenServiceFactoryResponse<A, B, Req>;
|
||||
|
||||
fn new_service(&self, cfg: A::Config) -> Self::Future {
|
||||
let srv = &*self.0;
|
||||
@@ -155,37 +164,38 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B> Clone for ThenServiceFactory<A, B> {
|
||||
impl<A, B, Req> Clone for ThenServiceFactory<A, B, Req> {
|
||||
fn clone(&self) -> Self {
|
||||
Self(self.0.clone())
|
||||
Self(self.0.clone(), PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
#[pin_project::pin_project]
|
||||
pub(crate) struct ThenServiceFactoryResponse<A, B>
|
||||
where
|
||||
A: ServiceFactory,
|
||||
B: ServiceFactory<
|
||||
Config = A::Config,
|
||||
Request = Result<A::Response, A::Error>,
|
||||
Error = A::Error,
|
||||
InitError = A::InitError,
|
||||
>,
|
||||
{
|
||||
#[pin]
|
||||
fut_b: B::Future,
|
||||
#[pin]
|
||||
fut_a: A::Future,
|
||||
a: Option<A::Service>,
|
||||
b: Option<B::Service>,
|
||||
pin_project! {
|
||||
pub(crate) struct ThenServiceFactoryResponse<A, B, Req>
|
||||
where
|
||||
A: ServiceFactory<Req>,
|
||||
B: ServiceFactory<
|
||||
Result<A::Response, A::Error>,
|
||||
Config = A::Config,
|
||||
Error = A::Error,
|
||||
InitError = A::InitError,
|
||||
>,
|
||||
{
|
||||
#[pin]
|
||||
fut_b: B::Future,
|
||||
#[pin]
|
||||
fut_a: A::Future,
|
||||
a: Option<A::Service>,
|
||||
b: Option<B::Service>,
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B> ThenServiceFactoryResponse<A, B>
|
||||
impl<A, B, Req> ThenServiceFactoryResponse<A, B, Req>
|
||||
where
|
||||
A: ServiceFactory,
|
||||
A: ServiceFactory<Req>,
|
||||
B: ServiceFactory<
|
||||
Result<A::Response, A::Error>,
|
||||
Config = A::Config,
|
||||
Request = Result<A::Response, A::Error>,
|
||||
Error = A::Error,
|
||||
InitError = A::InitError,
|
||||
>,
|
||||
@@ -200,17 +210,17 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B> Future for ThenServiceFactoryResponse<A, B>
|
||||
impl<A, B, Req> Future for ThenServiceFactoryResponse<A, B, Req>
|
||||
where
|
||||
A: ServiceFactory,
|
||||
A: ServiceFactory<Req>,
|
||||
B: ServiceFactory<
|
||||
Result<A::Response, A::Error>,
|
||||
Config = A::Config,
|
||||
Request = Result<A::Response, A::Error>,
|
||||
Error = A::Error,
|
||||
InitError = A::InitError,
|
||||
>,
|
||||
{
|
||||
type Output = Result<ThenService<A::Service, B::Service>, A::InitError>;
|
||||
type Output = Result<ThenService<A::Service, B::Service, Req>, A::InitError>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let this = self.project();
|
||||
@@ -238,19 +248,20 @@ where
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::cell::Cell;
|
||||
use std::rc::Rc;
|
||||
use std::task::{Context, Poll};
|
||||
use alloc::rc::Rc;
|
||||
use core::{
|
||||
cell::Cell,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
|
||||
use futures_util::future::{err, lazy, ok, ready, Ready};
|
||||
use futures_util::future::lazy;
|
||||
|
||||
use crate::{pipeline, pipeline_factory, Service, ServiceFactory};
|
||||
use crate::{err, ok, pipeline, pipeline_factory, ready, Ready, Service, ServiceFactory};
|
||||
|
||||
#[derive(Clone)]
|
||||
struct Srv1(Rc<Cell<usize>>);
|
||||
|
||||
impl Service for Srv1 {
|
||||
type Request = Result<&'static str, &'static str>;
|
||||
impl Service<Result<&'static str, &'static str>> for Srv1 {
|
||||
type Response = &'static str;
|
||||
type Error = ();
|
||||
type Future = Ready<Result<Self::Response, Self::Error>>;
|
||||
@@ -270,8 +281,7 @@ mod tests {
|
||||
|
||||
struct Srv2(Rc<Cell<usize>>);
|
||||
|
||||
impl Service for Srv2 {
|
||||
type Request = Result<&'static str, ()>;
|
||||
impl Service<Result<&'static str, ()>> for Srv2 {
|
||||
type Response = (&'static str, &'static str);
|
||||
type Error = ();
|
||||
type Future = Ready<Result<Self::Response, ()>>;
|
||||
|
@@ -1,18 +1,22 @@
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
use std::task::{Context, Poll};
|
||||
use alloc::{rc::Rc, sync::Arc};
|
||||
use core::{
|
||||
future::Future,
|
||||
marker::PhantomData,
|
||||
pin::Pin,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
|
||||
use pin_project_lite::pin_project;
|
||||
|
||||
use crate::transform_err::TransformMapInitErr;
|
||||
use crate::{IntoServiceFactory, Service, ServiceFactory};
|
||||
|
||||
/// Apply transform to a service.
|
||||
pub fn apply<T, S, U>(t: T, factory: U) -> ApplyTransform<T, S>
|
||||
pub fn apply<T, S, I, Req>(t: T, factory: I) -> ApplyTransform<T, S, Req>
|
||||
where
|
||||
S: ServiceFactory,
|
||||
T: Transform<S::Service, InitError = S::InitError>,
|
||||
U: IntoServiceFactory<S>,
|
||||
I: IntoServiceFactory<S, Req>,
|
||||
S: ServiceFactory<Req>,
|
||||
T: Transform<S::Service, Req, InitError = S::InitError>,
|
||||
{
|
||||
ApplyTransform::new(t, factory.into_factory())
|
||||
}
|
||||
@@ -89,10 +93,7 @@ where
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub trait Transform<S> {
|
||||
/// Requests handled by the service.
|
||||
type Request;
|
||||
|
||||
pub trait Transform<S, Req> {
|
||||
/// Responses given by the service.
|
||||
type Response;
|
||||
|
||||
@@ -100,11 +101,7 @@ pub trait Transform<S> {
|
||||
type Error;
|
||||
|
||||
/// The `TransformService` value created by this factory
|
||||
type Transform: Service<
|
||||
Request = Self::Request,
|
||||
Response = Self::Response,
|
||||
Error = Self::Error,
|
||||
>;
|
||||
type Transform: Service<Req, Response = Self::Response, Error = Self::Error>;
|
||||
|
||||
/// Errors produced while building a transform service.
|
||||
type InitError;
|
||||
@@ -117,7 +114,7 @@ pub trait Transform<S> {
|
||||
|
||||
/// Map this transform's factory error to a different error,
|
||||
/// returning a new transform service factory.
|
||||
fn map_init_err<F, E>(self, f: F) -> TransformMapInitErr<Self, S, F, E>
|
||||
fn map_init_err<F, E>(self, f: F) -> TransformMapInitErr<Self, S, Req, F, E>
|
||||
where
|
||||
Self: Sized,
|
||||
F: Fn(Self::InitError) -> E + Clone,
|
||||
@@ -126,11 +123,10 @@ pub trait Transform<S> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, S> Transform<S> for Rc<T>
|
||||
impl<T, S, Req> Transform<S, Req> for Rc<T>
|
||||
where
|
||||
T: Transform<S>,
|
||||
T: Transform<S, Req>,
|
||||
{
|
||||
type Request = T::Request;
|
||||
type Response = T::Response;
|
||||
type Error = T::Error;
|
||||
type InitError = T::InitError;
|
||||
@@ -142,11 +138,10 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, S> Transform<S> for Arc<T>
|
||||
impl<T, S, Req> Transform<S, Req> for Arc<T>
|
||||
where
|
||||
T: Transform<S>,
|
||||
T: Transform<S, Req>,
|
||||
{
|
||||
type Request = T::Request;
|
||||
type Response = T::Response;
|
||||
type Error = T::Error;
|
||||
type InitError = T::InitError;
|
||||
@@ -159,72 +154,76 @@ where
|
||||
}
|
||||
|
||||
/// `Apply` transform to new service
|
||||
pub struct ApplyTransform<T, S>(Rc<(T, S)>);
|
||||
pub struct ApplyTransform<T, S, Req>(Rc<(T, S)>, PhantomData<Req>);
|
||||
|
||||
impl<T, S> ApplyTransform<T, S>
|
||||
impl<T, S, Req> ApplyTransform<T, S, Req>
|
||||
where
|
||||
S: ServiceFactory,
|
||||
T: Transform<S::Service, InitError = S::InitError>,
|
||||
S: ServiceFactory<Req>,
|
||||
T: Transform<S::Service, Req, InitError = S::InitError>,
|
||||
{
|
||||
/// Create new `ApplyTransform` new service instance
|
||||
fn new(t: T, service: S) -> Self {
|
||||
Self(Rc::new((t, service)))
|
||||
Self(Rc::new((t, service)), PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, S> Clone for ApplyTransform<T, S> {
|
||||
impl<T, S, Req> Clone for ApplyTransform<T, S, Req> {
|
||||
fn clone(&self) -> Self {
|
||||
ApplyTransform(self.0.clone())
|
||||
ApplyTransform(self.0.clone(), PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, S> ServiceFactory for ApplyTransform<T, S>
|
||||
impl<T, S, Req> ServiceFactory<Req> for ApplyTransform<T, S, Req>
|
||||
where
|
||||
S: ServiceFactory,
|
||||
T: Transform<S::Service, InitError = S::InitError>,
|
||||
S: ServiceFactory<Req>,
|
||||
T: Transform<S::Service, Req, InitError = S::InitError>,
|
||||
{
|
||||
type Request = T::Request;
|
||||
type Response = T::Response;
|
||||
type Error = T::Error;
|
||||
|
||||
type Config = S::Config;
|
||||
type Service = T::Transform;
|
||||
type InitError = T::InitError;
|
||||
type Future = ApplyTransformFuture<T, S>;
|
||||
type Future = ApplyTransformFuture<T, S, Req>;
|
||||
|
||||
fn new_service(&self, cfg: S::Config) -> Self::Future {
|
||||
ApplyTransformFuture {
|
||||
store: self.0.clone(),
|
||||
state: ApplyTransformFutureState::A(self.0.as_ref().1.new_service(cfg)),
|
||||
state: ApplyTransformFutureState::A {
|
||||
fut: self.0.as_ref().1.new_service(cfg),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[pin_project::pin_project]
|
||||
pub struct ApplyTransformFuture<T, S>
|
||||
where
|
||||
S: ServiceFactory,
|
||||
T: Transform<S::Service, InitError = S::InitError>,
|
||||
{
|
||||
store: Rc<(T, S)>,
|
||||
#[pin]
|
||||
state: ApplyTransformFutureState<T, S>,
|
||||
pin_project! {
|
||||
pub struct ApplyTransformFuture<T, S, Req>
|
||||
where
|
||||
S: ServiceFactory<Req>,
|
||||
T: Transform<S::Service, Req, InitError = S::InitError>,
|
||||
{
|
||||
store: Rc<(T, S)>,
|
||||
#[pin]
|
||||
state: ApplyTransformFutureState<T, S, Req>,
|
||||
}
|
||||
}
|
||||
|
||||
#[pin_project::pin_project(project = ApplyTransformFutureStateProj)]
|
||||
pub enum ApplyTransformFutureState<T, S>
|
||||
where
|
||||
S: ServiceFactory,
|
||||
T: Transform<S::Service, InitError = S::InitError>,
|
||||
{
|
||||
A(#[pin] S::Future),
|
||||
B(#[pin] T::Future),
|
||||
pin_project! {
|
||||
#[project = ApplyTransformFutureStateProj]
|
||||
pub enum ApplyTransformFutureState<T, S, Req>
|
||||
where
|
||||
S: ServiceFactory<Req>,
|
||||
T: Transform<S::Service, Req, InitError = S::InitError>,
|
||||
{
|
||||
A { #[pin] fut: S::Future },
|
||||
B { #[pin] fut: T::Future },
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, S> Future for ApplyTransformFuture<T, S>
|
||||
impl<T, S, Req> Future for ApplyTransformFuture<T, S, Req>
|
||||
where
|
||||
S: ServiceFactory,
|
||||
T: Transform<S::Service, InitError = S::InitError>,
|
||||
S: ServiceFactory<Req>,
|
||||
T: Transform<S::Service, Req, InitError = S::InitError>,
|
||||
{
|
||||
type Output = Result<T::Transform, T::InitError>;
|
||||
|
||||
@@ -232,15 +231,15 @@ where
|
||||
let mut this = self.as_mut().project();
|
||||
|
||||
match this.state.as_mut().project() {
|
||||
ApplyTransformFutureStateProj::A(fut) => match fut.poll(cx)? {
|
||||
ApplyTransformFutureStateProj::A { fut } => match fut.poll(cx)? {
|
||||
Poll::Ready(srv) => {
|
||||
let fut = this.store.0.new_transform(srv);
|
||||
this.state.set(ApplyTransformFutureState::B(fut));
|
||||
this.state.set(ApplyTransformFutureState::B { fut });
|
||||
self.poll(cx)
|
||||
}
|
||||
Poll::Pending => Poll::Pending,
|
||||
},
|
||||
ApplyTransformFutureStateProj::B(fut) => fut.poll(cx),
|
||||
ApplyTransformFutureStateProj::B { fut } => fut.poll(cx),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,7 +1,11 @@
|
||||
use std::future::Future;
|
||||
use std::marker::PhantomData;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
use core::{
|
||||
future::Future,
|
||||
marker::PhantomData,
|
||||
pin::Pin,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
|
||||
use pin_project_lite::pin_project;
|
||||
|
||||
use super::Transform;
|
||||
|
||||
@@ -9,75 +13,75 @@ use super::Transform;
|
||||
/// transform's init error.
|
||||
///
|
||||
/// This is created by the `Transform::map_init_err` method.
|
||||
pub struct TransformMapInitErr<T, S, F, E> {
|
||||
t: T,
|
||||
f: F,
|
||||
e: PhantomData<(S, E)>,
|
||||
pub struct TransformMapInitErr<T, S, Req, F, E> {
|
||||
transform: T,
|
||||
mapper: F,
|
||||
_phantom: PhantomData<(S, Req, E)>,
|
||||
}
|
||||
|
||||
impl<T, S, F, E> TransformMapInitErr<T, S, F, E> {
|
||||
impl<T, S, F, E, Req> TransformMapInitErr<T, S, Req, F, E> {
|
||||
pub(crate) fn new(t: T, f: F) -> Self
|
||||
where
|
||||
T: Transform<S>,
|
||||
T: Transform<S, Req>,
|
||||
F: Fn(T::InitError) -> E,
|
||||
{
|
||||
Self {
|
||||
t,
|
||||
f,
|
||||
e: PhantomData,
|
||||
transform: t,
|
||||
mapper: f,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, S, F, E> Clone for TransformMapInitErr<T, S, F, E>
|
||||
impl<T, S, Req, F, E> Clone for TransformMapInitErr<T, S, Req, F, E>
|
||||
where
|
||||
T: Clone,
|
||||
F: Clone,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
t: self.t.clone(),
|
||||
f: self.f.clone(),
|
||||
e: PhantomData,
|
||||
transform: self.transform.clone(),
|
||||
mapper: self.mapper.clone(),
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, S, F, E> Transform<S> for TransformMapInitErr<T, S, F, E>
|
||||
impl<T, S, F, E, Req> Transform<S, Req> for TransformMapInitErr<T, S, Req, F, E>
|
||||
where
|
||||
T: Transform<S>,
|
||||
T: Transform<S, Req>,
|
||||
F: Fn(T::InitError) -> E + Clone,
|
||||
{
|
||||
type Request = T::Request;
|
||||
type Response = T::Response;
|
||||
type Error = T::Error;
|
||||
type Transform = T::Transform;
|
||||
|
||||
type InitError = E;
|
||||
type Future = TransformMapInitErrFuture<T, S, F, E>;
|
||||
type Future = TransformMapInitErrFuture<T, S, F, E, Req>;
|
||||
|
||||
fn new_transform(&self, service: S) -> Self::Future {
|
||||
TransformMapInitErrFuture {
|
||||
fut: self.t.new_transform(service),
|
||||
f: self.f.clone(),
|
||||
fut: self.transform.new_transform(service),
|
||||
f: self.mapper.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[pin_project::pin_project]
|
||||
pub struct TransformMapInitErrFuture<T, S, F, E>
|
||||
where
|
||||
T: Transform<S>,
|
||||
pin_project! {
|
||||
pub struct TransformMapInitErrFuture<T, S, F, E, Req>
|
||||
where
|
||||
T: Transform<S, Req>,
|
||||
F: Fn(T::InitError) -> E,
|
||||
{
|
||||
#[pin]
|
||||
fut: T::Future,
|
||||
f: F,
|
||||
{
|
||||
#[pin]
|
||||
fut: T::Future,
|
||||
f: F,
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, S, F, E> Future for TransformMapInitErrFuture<T, S, F, E>
|
||||
impl<T, S, F, E, Req> Future for TransformMapInitErrFuture<T, S, F, E, Req>
|
||||
where
|
||||
T: Transform<S>,
|
||||
T: Transform<S, Req>,
|
||||
F: Fn(T::InitError) -> E + Clone,
|
||||
{
|
||||
type Output = Result<T::Transform, E>;
|
||||
|
@@ -1,6 +1,9 @@
|
||||
//! Various helpers for Actix applications to use during testing.
|
||||
#![deny(rust_2018_idioms, warnings)]
|
||||
|
||||
#![deny(rust_2018_idioms, nonstandard_style)]
|
||||
#![allow(clippy::type_complexity, clippy::needless_doctest_main)]
|
||||
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
||||
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
||||
|
||||
use std::sync::mpsc;
|
||||
use std::{net, thread};
|
||||
@@ -80,15 +83,18 @@ impl TestServer {
|
||||
|
||||
// run server in separate thread
|
||||
thread::spawn(move || {
|
||||
let sys = System::new("actix-test-server");
|
||||
let mut sys = System::new("actix-test-server");
|
||||
let tcp = net::TcpListener::bind("127.0.0.1:0").unwrap();
|
||||
let local_addr = tcp.local_addr().unwrap();
|
||||
|
||||
Server::build()
|
||||
.listen("test", tcp, factory)?
|
||||
.workers(1)
|
||||
.disable_signals()
|
||||
.start();
|
||||
sys.block_on(async {
|
||||
Server::build()
|
||||
.listen("test", tcp, factory)
|
||||
.unwrap()
|
||||
.workers(1)
|
||||
.disable_signals()
|
||||
.start();
|
||||
});
|
||||
|
||||
tx.send((System::current(), local_addr)).unwrap();
|
||||
sys.run()
|
||||
|
@@ -1,5 +1,9 @@
|
||||
//! Thread pool for blocking operations
|
||||
|
||||
#![deny(rust_2018_idioms, nonstandard_style)]
|
||||
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
||||
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
||||
|
||||
use std::fmt;
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
|
@@ -5,7 +5,9 @@
|
||||
//! * `rustls` - TLS acceptor using the `rustls` crate.
|
||||
//! * `nativetls` - TLS acceptor using the `native-tls` crate.
|
||||
|
||||
#![deny(rust_2018_idioms)]
|
||||
#![deny(rust_2018_idioms, nonstandard_style)]
|
||||
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
||||
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
||||
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
|
@@ -1,8 +1,10 @@
|
||||
//! Actix tracing - support for tokio tracing with Actix services.
|
||||
#![deny(rust_2018_idioms, warnings)]
|
||||
|
||||
use std::marker::PhantomData;
|
||||
use std::task::{Context, Poll};
|
||||
#![deny(rust_2018_idioms, nonstandard_style)]
|
||||
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
||||
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
||||
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use actix_service::{
|
||||
apply, dev::ApplyTransform, IntoServiceFactory, Service, ServiceFactory, Transform,
|
||||
@@ -24,21 +26,18 @@ impl<S, F> TracingService<S, F> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, F> Service for TracingService<S, F>
|
||||
impl<S, Req, F> Service<Req> for TracingService<S, F>
|
||||
where
|
||||
S: Service,
|
||||
F: Fn(&S::Request) -> Option<tracing::Span>,
|
||||
S: Service<Req>,
|
||||
F: Fn(&Req) -> Option<tracing::Span>,
|
||||
{
|
||||
type Request = S::Request;
|
||||
type Response = S::Response;
|
||||
type Error = S::Error;
|
||||
type Future = Either<S::Future, Instrumented<S::Future>>;
|
||||
|
||||
fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
self.inner.poll_ready(ctx)
|
||||
}
|
||||
actix_service::forward_ready!(inner);
|
||||
|
||||
fn call(&mut self, req: Self::Request) -> Self::Future {
|
||||
fn call(&mut self, req: Req) -> Self::Future {
|
||||
let span = (self.make_span)(&req);
|
||||
let _enter = span.as_ref().map(|s| s.enter());
|
||||
|
||||
@@ -57,8 +56,6 @@ where
|
||||
}
|
||||
|
||||
/// A `Transform` implementation that wraps services with a [`TracingService`].
|
||||
///
|
||||
/// [`TracingService`]: struct.TracingService.html
|
||||
pub struct TracingTransform<S, U, F> {
|
||||
make_span: F,
|
||||
_p: PhantomData<fn(S, U)>,
|
||||
@@ -73,18 +70,12 @@ impl<S, U, F> TracingTransform<S, U, F> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, U, F> Transform<S> for TracingTransform<S, U, F>
|
||||
impl<S, Req, U, F> Transform<S, Req> for TracingTransform<S, U, F>
|
||||
where
|
||||
S: Service,
|
||||
U: ServiceFactory<
|
||||
Request = S::Request,
|
||||
Response = S::Response,
|
||||
Error = S::Error,
|
||||
Service = S,
|
||||
>,
|
||||
F: Fn(&S::Request) -> Option<tracing::Span> + Clone,
|
||||
S: Service<Req>,
|
||||
U: ServiceFactory<Req, Response = S::Response, Error = S::Error, Service = S>,
|
||||
F: Fn(&Req) -> Option<tracing::Span> + Clone,
|
||||
{
|
||||
type Request = S::Request;
|
||||
type Response = S::Response;
|
||||
type Error = S::Error;
|
||||
type Transform = TracingService<S, F>;
|
||||
@@ -109,14 +100,14 @@ where
|
||||
/// |req: &Request| Some(span!(Level::INFO, "request", req.id))
|
||||
/// );
|
||||
/// ```
|
||||
pub fn trace<S, U, F>(
|
||||
service_factory: U,
|
||||
pub fn trace<S, Req, I, F>(
|
||||
service_factory: I,
|
||||
make_span: F,
|
||||
) -> ApplyTransform<TracingTransform<S::Service, S, F>, S>
|
||||
) -> ApplyTransform<TracingTransform<S::Service, S, F>, S, Req>
|
||||
where
|
||||
S: ServiceFactory,
|
||||
F: Fn(&S::Request) -> Option<tracing::Span> + Clone,
|
||||
U: IntoServiceFactory<S>,
|
||||
I: IntoServiceFactory<S, Req>,
|
||||
S: ServiceFactory<Req>,
|
||||
F: Fn(&Req) -> Option<tracing::Span> + Clone,
|
||||
{
|
||||
apply(
|
||||
TracingTransform::new(make_span),
|
||||
|
@@ -1,223 +1,156 @@
|
||||
# Changes
|
||||
|
||||
## Unreleased - 2020-xx-xx
|
||||
* Upgrade `pin-project` to `1.0`.
|
||||
|
||||
|
||||
## 3.0.0-beta.1 - 2020-12-28
|
||||
* Update `bytes` dependency to `1`. [#237]
|
||||
* Use `pin-project-lite` to replace `pin-project`. [#229]
|
||||
* Remove `condition`,`either`,`inflight`,`keepalive`,`oneshot`,`order`,`stream` and `time` mods. [#229]
|
||||
|
||||
[#229]: https://github.com/actix/actix-net/pull/229
|
||||
[#237]: https://github.com/actix/actix-net/pull/237
|
||||
|
||||
|
||||
## 2.0.0 - 2020-08-23
|
||||
* No changes from beta 1.
|
||||
|
||||
|
||||
## 2.0.0-beta.1 - 2020-08-19
|
||||
* Upgrade `tokio-util` to `0.3`.
|
||||
* Remove unsound custom Cell and use `std::cell::RefCell` instead, as well as `actix-service`.
|
||||
* Rename method to correctly spelled `LocalWaker::is_registered`.
|
||||
|
||||
## [1.0.6] - 2020-01-08
|
||||
|
||||
* Add `Clone` impl for `condition::Waiter`
|
||||
## 1.0.6 - 2020-01-08
|
||||
* Add `Clone` impl for `condition::Waiter`.
|
||||
|
||||
## [1.0.5] - 2020-01-08
|
||||
|
||||
## 1.0.5 - 2020-01-08
|
||||
* Add `Condition` type.
|
||||
|
||||
* Add `Pool` of one-shot's.
|
||||
|
||||
## [1.0.4] - 2019-12-20
|
||||
|
||||
## 1.0.4 - 2019-12-20
|
||||
* Add methods to check `LocalWaker` registration state.
|
||||
|
||||
## [1.0.3] - 2019-12-11
|
||||
|
||||
## 1.0.3 - 2019-12-11
|
||||
* Revert InOrder service changes
|
||||
|
||||
## [1.0.2] - 2019-12-11
|
||||
|
||||
* Allow to create `framed::Dispatcher` with custom `mpsc::Receiver`
|
||||
## 1.0.2 - 2019-12-11
|
||||
* Allow to create `framed::Dispatcher` with custom `mpsc::Receiver`.
|
||||
* Add `oneshot::Sender::is_canceled()` method.
|
||||
|
||||
* Add `oneshot::Sender::is_canceled()` method
|
||||
|
||||
## [1.0.1] - 2019-12-11
|
||||
## 1.0.1 - 2019-12-11
|
||||
* Optimize InOrder service.
|
||||
|
||||
* Optimize InOrder service
|
||||
|
||||
## [1.0.0] - 2019-12-11
|
||||
## 1.0.0 - 2019-12-11
|
||||
* Simplify oneshot and mpsc implementations.
|
||||
|
||||
* Simplify oneshot and mpsc implementations
|
||||
|
||||
## [1.0.0-alpha.3] - 2019-12-07
|
||||
## 1.0.0-alpha.3 - 2019-12-07
|
||||
* Migrate to tokio 0.2.
|
||||
* Fix oneshot.
|
||||
|
||||
* Migrate to tokio 0.2
|
||||
|
||||
* Fix oneshot
|
||||
## 1.0.0-alpha.2 - 2019-12-02
|
||||
* Migrate to `std::future`.
|
||||
|
||||
## [1.0.0-alpha.2] - 2019-12-02
|
||||
|
||||
* Migrate to `std::future`
|
||||
|
||||
## [0.4.7] - 2019-10-14
|
||||
|
||||
## 0.4.7 - 2019-10-14
|
||||
* Re-register task on every framed transport poll.
|
||||
|
||||
|
||||
## [0.4.6] - 2019-10-08
|
||||
|
||||
## 0.4.6 - 2019-10-08
|
||||
* Refactor `Counter` type. register current task in available method.
|
||||
|
||||
|
||||
## [0.4.5] - 2019-07-19
|
||||
|
||||
### Removed
|
||||
|
||||
* Deprecated `CloneableService` as it is not safe
|
||||
## 0.4.5 - 2019-07-19
|
||||
* Deprecated `CloneableService` as it is not safe.
|
||||
|
||||
|
||||
## [0.4.4] - 2019-07-17
|
||||
|
||||
### Changed
|
||||
|
||||
* Undeprecate `FramedTransport` as it is actually useful
|
||||
## 0.4.4 - 2019-07-17
|
||||
* Undeprecate `FramedTransport` as it is actually useful.
|
||||
|
||||
|
||||
## [0.4.3] - 2019-07-17
|
||||
|
||||
### Deprecated
|
||||
|
||||
* Deprecate `CloneableService` as it is not safe and in general not very useful
|
||||
|
||||
* Deprecate `FramedTransport` in favor of `actix-ioframe`
|
||||
## 0.4.3 - 2019-07-17
|
||||
* Deprecate `CloneableService` as it is not safe and in general not very useful.
|
||||
* Deprecate `FramedTransport` in favor of `actix-ioframe`.
|
||||
|
||||
|
||||
## [0.4.2] - 2019-06-26
|
||||
|
||||
### Fixed
|
||||
|
||||
* Do not block on sink drop for FramedTransport
|
||||
## 0.4.2 - 2019-06-26
|
||||
* Do not block on sink drop for FramedTransport.
|
||||
|
||||
|
||||
## [0.4.1] - 2019-05-15
|
||||
|
||||
### Changed
|
||||
|
||||
* Change `Either` constructor
|
||||
## 0.4.1 - 2019-05-15
|
||||
* Change `Either` constructor.
|
||||
|
||||
|
||||
## [0.4.0] - 2019-05-11
|
||||
## 0.4.0 - 2019-05-11
|
||||
* Change `Either` to handle two nexted services.
|
||||
* Upgrade actix-service 0.4.
|
||||
* Removed framed related services.
|
||||
* Removed stream related services.
|
||||
|
||||
### Changed
|
||||
|
||||
* Change `Either` to handle two nexted services
|
||||
|
||||
* Upgrade actix-service 0.4
|
||||
|
||||
### Deleted
|
||||
|
||||
* Framed related services
|
||||
|
||||
* Stream related services
|
||||
|
||||
## [0.3.5] - 2019-04-04
|
||||
|
||||
### Added
|
||||
|
||||
## 0.3.5 - 2019-04-04
|
||||
* Allow to send messages to `FramedTransport` via mpsc channel.
|
||||
|
||||
### Changed
|
||||
|
||||
* Remove 'static constraint from Clonable service
|
||||
* Remove `'static` constraint from Clonable service.
|
||||
|
||||
|
||||
## [0.3.4] - 2019-03-12
|
||||
|
||||
### Changed
|
||||
|
||||
## 0.3.4 - 2019-03-12
|
||||
* `TimeoutService`, `InOrderService`, `InFlightService` accepts generic IntoService services.
|
||||
|
||||
### Fixed
|
||||
|
||||
* Fix `InFlightService::poll_ready()` nested service readiness check
|
||||
|
||||
* Fix `InOrderService::poll_ready()` nested service readiness check
|
||||
* Fix `InFlightService::poll_ready()` nested service readiness check.
|
||||
* Fix `InOrderService::poll_ready()` nested service readiness check.
|
||||
|
||||
|
||||
## [0.3.3] - 2019-03-09
|
||||
|
||||
### Changed
|
||||
|
||||
* Revert IntoFuture change
|
||||
|
||||
* Add generic config param for IntoFramed and TakeOne new services
|
||||
## 0.3.3 - 2019-03-09
|
||||
* Revert IntoFuture change.
|
||||
* Add generic config param for IntoFramed and TakeOne new services.
|
||||
|
||||
|
||||
## [0.3.2] - 2019-03-04
|
||||
## 0.3.2 - 2019-03-04
|
||||
* Use IntoFuture for new services.
|
||||
|
||||
### Changed
|
||||
|
||||
* Use IntoFuture for new services
|
||||
## 0.3.1 - 2019-03-04
|
||||
* Use new type of transform trait.
|
||||
|
||||
|
||||
## [0.3.1] - 2019-03-04
|
||||
|
||||
### Changed
|
||||
|
||||
* Use new type of transform trait
|
||||
|
||||
|
||||
## [0.3.0] - 2019-03-02
|
||||
|
||||
### Changed
|
||||
|
||||
## 0.3.0 - 2019-03-02
|
||||
* Use new `NewService` trait
|
||||
|
||||
* BoxedNewService` and `BoxedService` types moved to actix-service crate.
|
||||
|
||||
|
||||
## [0.2.4] - 2019-02-21
|
||||
|
||||
### Changed
|
||||
|
||||
## 0.2.4 - 2019-02-21
|
||||
* Custom `BoxedNewService` implementation.
|
||||
|
||||
|
||||
## [0.2.3] - 2019-02-21
|
||||
|
||||
### Added
|
||||
|
||||
* Add `BoxedNewService` and `BoxedService`
|
||||
## 0.2.3 - 2019-02-21
|
||||
* Add `BoxedNewService` and `BoxedService`.
|
||||
|
||||
|
||||
## [0.2.2] - 2019-02-11
|
||||
|
||||
### Added
|
||||
|
||||
* Add `Display` impl for `TimeoutError`
|
||||
|
||||
* Add `Display` impl for `InOrderError`
|
||||
## 0.2.2 - 2019-02-11
|
||||
* Add `Display` impl for `TimeoutError`.
|
||||
* Add `Display` impl for `InOrderError`.
|
||||
|
||||
|
||||
## [0.2.1] - 2019-02-06
|
||||
|
||||
### Added
|
||||
|
||||
## 0.2.1 - 2019-02-06
|
||||
* Add `InOrder` service. the service yields responses as they become available,
|
||||
in the order that their originating requests were submitted to the service.
|
||||
|
||||
### Changed
|
||||
|
||||
* Convert `Timeout` and `InFlight` services to a transforms
|
||||
* Convert `Timeout` and `InFlight` services to a transforms.
|
||||
|
||||
|
||||
## [0.2.0] - 2019-02-01
|
||||
|
||||
* Fix framed transport error handling
|
||||
|
||||
* Added Clone impl for Either service
|
||||
|
||||
* Added Clone impl for Timeout service factory
|
||||
|
||||
* Added Service and NewService for Stream dispatcher
|
||||
|
||||
* Switch to actix-service 0.2
|
||||
## 0.2.0 - 2019-02-01
|
||||
* Fix framed transport error handling.
|
||||
* Added Clone impl for Either service.
|
||||
* Added Clone impl for Timeout service factory.
|
||||
* Added Service and NewService for Stream dispatcher.
|
||||
* Switch to actix-service 0.2.
|
||||
|
||||
|
||||
## [0.1.0] - 2018-12-09
|
||||
|
||||
* Move utils services to separate crate
|
||||
## 0.1.0 - 2018-12-09
|
||||
* Move utils services to separate crate.
|
||||
|
@@ -1,8 +1,8 @@
|
||||
[package]
|
||||
name = "actix-utils"
|
||||
version = "2.0.0"
|
||||
version = "3.0.0-beta.1"
|
||||
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
||||
description = "Various network related services and utilities for the Actix ecosystem."
|
||||
description = "Various network related services and utilities for the Actix ecosystem"
|
||||
keywords = ["network", "framework", "async", "futures"]
|
||||
homepage = "https://actix.rs"
|
||||
repository = "https://github.com/actix/actix-net.git"
|
||||
@@ -16,15 +16,14 @@ name = "actix_utils"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
actix-codec = "0.3.0"
|
||||
actix-rt = "1.1.1"
|
||||
actix-service = "1.0.6"
|
||||
bitflags = "1.2.1"
|
||||
bytes = "0.5.3"
|
||||
either = "1.5.3"
|
||||
futures-channel = { version = "0.3.4", default-features = false }
|
||||
futures-sink = { version = "0.3.4", default-features = false }
|
||||
futures-util = { version = "0.3.4", default-features = false }
|
||||
actix-codec = "0.4.0-beta.1"
|
||||
actix-rt = "2.0.0-beta.1"
|
||||
actix-service = "2.0.0-beta.1"
|
||||
|
||||
futures-core = { version = "0.3.7", default-features = false }
|
||||
futures-sink = { version = "0.3.7", default-features = false }
|
||||
log = "0.4"
|
||||
pin-project = "1.0.0"
|
||||
slab = "0.4"
|
||||
pin-project-lite = "0.2.0"
|
||||
|
||||
[dev-dependencies]
|
||||
futures-util = { version = "0.3.7", default-features = false }
|
||||
|
@@ -1,129 +0,0 @@
|
||||
use std::cell::RefCell;
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use std::rc::Rc;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
use slab::Slab;
|
||||
|
||||
use crate::task::LocalWaker;
|
||||
|
||||
/// Condition allows to notify multiple receivers at the same time
|
||||
pub struct Condition(Rc<RefCell<Inner>>);
|
||||
|
||||
struct Inner {
|
||||
data: Slab<Option<LocalWaker>>,
|
||||
}
|
||||
|
||||
impl Default for Condition {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl Condition {
|
||||
pub fn new() -> Condition {
|
||||
Condition(Rc::new(RefCell::new(Inner { data: Slab::new() })))
|
||||
}
|
||||
|
||||
/// Get condition waiter
|
||||
pub fn wait(&mut self) -> Waiter {
|
||||
let token = self.0.borrow_mut().data.insert(None);
|
||||
Waiter {
|
||||
token,
|
||||
inner: self.0.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Notify all waiters
|
||||
pub fn notify(&self) {
|
||||
let inner = self.0.borrow();
|
||||
for item in inner.data.iter() {
|
||||
if let Some(waker) = item.1 {
|
||||
waker.wake();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Condition {
|
||||
fn drop(&mut self) {
|
||||
self.notify()
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use = "Waiter do nothing unless polled"]
|
||||
pub struct Waiter {
|
||||
token: usize,
|
||||
inner: Rc<RefCell<Inner>>,
|
||||
}
|
||||
|
||||
impl Clone for Waiter {
|
||||
fn clone(&self) -> Self {
|
||||
let token = self.inner.borrow_mut().data.insert(None);
|
||||
Waiter {
|
||||
token,
|
||||
inner: self.inner.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Future for Waiter {
|
||||
type Output = ();
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let this = self.get_mut();
|
||||
|
||||
let mut inner = this.inner.borrow_mut();
|
||||
let inner = unsafe { inner.data.get_unchecked_mut(this.token) };
|
||||
if inner.is_none() {
|
||||
let waker = LocalWaker::default();
|
||||
waker.register(cx.waker());
|
||||
*inner = Some(waker);
|
||||
Poll::Pending
|
||||
} else if inner.as_mut().unwrap().register(cx.waker()) {
|
||||
Poll::Pending
|
||||
} else {
|
||||
Poll::Ready(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Waiter {
|
||||
fn drop(&mut self) {
|
||||
self.inner.borrow_mut().data.remove(self.token);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use futures_util::future::lazy;
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_condition() {
|
||||
let mut cond = Condition::new();
|
||||
let mut waiter = cond.wait();
|
||||
assert_eq!(
|
||||
lazy(|cx| Pin::new(&mut waiter).poll(cx)).await,
|
||||
Poll::Pending
|
||||
);
|
||||
cond.notify();
|
||||
waiter.await;
|
||||
|
||||
let mut waiter = cond.wait();
|
||||
assert_eq!(
|
||||
lazy(|cx| Pin::new(&mut waiter).poll(cx)).await,
|
||||
Poll::Pending
|
||||
);
|
||||
let mut waiter2 = waiter.clone();
|
||||
assert_eq!(
|
||||
lazy(|cx| Pin::new(&mut waiter2).poll(cx)).await,
|
||||
Poll::Pending
|
||||
);
|
||||
|
||||
drop(cond);
|
||||
waiter.await;
|
||||
waiter2.await;
|
||||
}
|
||||
}
|
@@ -1,6 +1,7 @@
|
||||
use std::cell::Cell;
|
||||
use core::cell::Cell;
|
||||
use core::task;
|
||||
|
||||
use std::rc::Rc;
|
||||
use std::task;
|
||||
|
||||
use crate::task::LocalWaker;
|
||||
|
||||
|
@@ -1,15 +1,17 @@
|
||||
//! Framed dispatcher service and related utilities
|
||||
//! Framed dispatcher service and related utilities.
|
||||
|
||||
#![allow(type_alias_bounds)]
|
||||
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
use std::{fmt, mem};
|
||||
use core::future::Future;
|
||||
use core::pin::Pin;
|
||||
use core::task::{Context, Poll};
|
||||
use core::{fmt, mem};
|
||||
|
||||
use actix_codec::{AsyncRead, AsyncWrite, Decoder, Encoder, Framed};
|
||||
use actix_service::{IntoService, Service};
|
||||
use futures_util::{future::Future, stream::Stream, FutureExt};
|
||||
use futures_core::stream::Stream;
|
||||
use log::debug;
|
||||
use pin_project_lite::pin_project;
|
||||
|
||||
use crate::mpsc;
|
||||
|
||||
@@ -61,28 +63,35 @@ pub enum Message<T> {
|
||||
Close,
|
||||
}
|
||||
|
||||
/// Dispatcher is a future that reads frames from Framed object
|
||||
/// and passes them to the service.
|
||||
#[pin_project::pin_project]
|
||||
pub struct Dispatcher<S, T, U, I>
|
||||
where
|
||||
S: Service<Request = <U as Decoder>::Item, Response = I>,
|
||||
S::Error: 'static,
|
||||
S::Future: 'static,
|
||||
T: AsyncRead + AsyncWrite,
|
||||
U: Encoder<I> + Decoder,
|
||||
I: 'static,
|
||||
<U as Encoder<I>>::Error: std::fmt::Debug,
|
||||
{
|
||||
service: S,
|
||||
state: State<S, U, I>,
|
||||
#[pin]
|
||||
framed: Framed<T, U>,
|
||||
rx: mpsc::Receiver<Result<Message<I>, S::Error>>,
|
||||
tx: mpsc::Sender<Result<Message<I>, S::Error>>,
|
||||
pin_project! {
|
||||
/// Dispatcher is a future that reads frames from Framed object
|
||||
/// and passes them to the service.
|
||||
pub struct Dispatcher<S, T, U, I>
|
||||
where
|
||||
S: Service<<U as Decoder>::Item, Response = I>,
|
||||
S::Error: 'static,
|
||||
S::Future: 'static,
|
||||
T: AsyncRead,
|
||||
T: AsyncWrite,
|
||||
U: Encoder<I>,
|
||||
U: Decoder,
|
||||
I: 'static,
|
||||
<U as Encoder<I>>::Error: fmt::Debug,
|
||||
{
|
||||
service: S,
|
||||
state: State<S, U, I>,
|
||||
#[pin]
|
||||
framed: Framed<T, U>,
|
||||
rx: mpsc::Receiver<Result<Message<I>, S::Error>>,
|
||||
tx: mpsc::Sender<Result<Message<I>, S::Error>>,
|
||||
}
|
||||
}
|
||||
|
||||
enum State<S: Service, U: Encoder<I> + Decoder, I> {
|
||||
enum State<S, U, I>
|
||||
where
|
||||
S: Service<<U as Decoder>::Item>,
|
||||
U: Encoder<I> + Decoder,
|
||||
{
|
||||
Processing,
|
||||
Error(DispatcherError<S::Error, U, I>),
|
||||
FramedError(DispatcherError<S::Error, U, I>),
|
||||
@@ -90,7 +99,11 @@ enum State<S: Service, U: Encoder<I> + Decoder, I> {
|
||||
Stopping,
|
||||
}
|
||||
|
||||
impl<S: Service, U: Encoder<I> + Decoder, I> State<S, U, I> {
|
||||
impl<S, U, I> State<S, U, I>
|
||||
where
|
||||
S: Service<<U as Decoder>::Item>,
|
||||
U: Encoder<I> + Decoder,
|
||||
{
|
||||
fn take_error(&mut self) -> DispatcherError<S::Error, U, I> {
|
||||
match mem::replace(self, State::Processing) {
|
||||
State::Error(err) => err,
|
||||
@@ -108,16 +121,19 @@ impl<S: Service, U: Encoder<I> + Decoder, I> State<S, U, I> {
|
||||
|
||||
impl<S, T, U, I> Dispatcher<S, T, U, I>
|
||||
where
|
||||
S: Service<Request = <U as Decoder>::Item, Response = I>,
|
||||
S: Service<<U as Decoder>::Item, Response = I>,
|
||||
S::Error: 'static,
|
||||
S::Future: 'static,
|
||||
T: AsyncRead + AsyncWrite,
|
||||
U: Decoder + Encoder<I>,
|
||||
I: 'static,
|
||||
<U as Decoder>::Error: std::fmt::Debug,
|
||||
<U as Encoder<I>>::Error: std::fmt::Debug,
|
||||
<U as Decoder>::Error: fmt::Debug,
|
||||
<U as Encoder<I>>::Error: fmt::Debug,
|
||||
{
|
||||
pub fn new<F: IntoService<S>>(framed: Framed<T, U>, service: F) -> Self {
|
||||
pub fn new<F>(framed: Framed<T, U>, service: F) -> Self
|
||||
where
|
||||
F: IntoService<S, <U as Decoder>::Item>,
|
||||
{
|
||||
let (tx, rx) = mpsc::channel();
|
||||
Dispatcher {
|
||||
framed,
|
||||
@@ -129,11 +145,14 @@ where
|
||||
}
|
||||
|
||||
/// Construct new `Dispatcher` instance with customer `mpsc::Receiver`
|
||||
pub fn with_rx<F: IntoService<S>>(
|
||||
pub fn with_rx<F>(
|
||||
framed: Framed<T, U>,
|
||||
service: F,
|
||||
rx: mpsc::Receiver<Result<Message<I>, S::Error>>,
|
||||
) -> Self {
|
||||
) -> Self
|
||||
where
|
||||
F: IntoService<S, <U as Decoder>::Item>,
|
||||
{
|
||||
let tx = rx.sender();
|
||||
Dispatcher {
|
||||
framed,
|
||||
@@ -172,13 +191,13 @@ where
|
||||
|
||||
fn poll_read(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> bool
|
||||
where
|
||||
S: Service<Request = <U as Decoder>::Item, Response = I>,
|
||||
S: Service<<U as Decoder>::Item, Response = I>,
|
||||
S::Error: 'static,
|
||||
S::Future: 'static,
|
||||
T: AsyncRead + AsyncWrite,
|
||||
U: Decoder + Encoder<I>,
|
||||
I: 'static,
|
||||
<U as Encoder<I>>::Error: std::fmt::Debug,
|
||||
<U as Encoder<I>>::Error: fmt::Debug,
|
||||
{
|
||||
loop {
|
||||
let this = self.as_mut().project();
|
||||
@@ -198,9 +217,11 @@ where
|
||||
};
|
||||
|
||||
let tx = this.tx.clone();
|
||||
actix_rt::spawn(this.service.call(item).map(move |item| {
|
||||
let fut = this.service.call(item);
|
||||
actix_rt::spawn(async move {
|
||||
let item = fut.await;
|
||||
let _ = tx.send(item.map(Message::Item));
|
||||
}));
|
||||
});
|
||||
}
|
||||
Poll::Pending => return false,
|
||||
Poll::Ready(Err(err)) => {
|
||||
@@ -214,13 +235,13 @@ where
|
||||
/// write to framed object
|
||||
fn poll_write(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> bool
|
||||
where
|
||||
S: Service<Request = <U as Decoder>::Item, Response = I>,
|
||||
S: Service<<U as Decoder>::Item, Response = I>,
|
||||
S::Error: 'static,
|
||||
S::Future: 'static,
|
||||
T: AsyncRead + AsyncWrite,
|
||||
U: Decoder + Encoder<I>,
|
||||
I: 'static,
|
||||
<U as Encoder<I>>::Error: std::fmt::Debug,
|
||||
<U as Encoder<I>>::Error: fmt::Debug,
|
||||
{
|
||||
loop {
|
||||
let mut this = self.as_mut().project();
|
||||
@@ -265,14 +286,14 @@ where
|
||||
|
||||
impl<S, T, U, I> Future for Dispatcher<S, T, U, I>
|
||||
where
|
||||
S: Service<Request = <U as Decoder>::Item, Response = I>,
|
||||
S: Service<<U as Decoder>::Item, Response = I>,
|
||||
S::Error: 'static,
|
||||
S::Future: 'static,
|
||||
T: AsyncRead + AsyncWrite,
|
||||
U: Decoder + Encoder<I>,
|
||||
I: 'static,
|
||||
<U as Encoder<I>>::Error: std::fmt::Debug,
|
||||
<U as Decoder>::Error: std::fmt::Debug,
|
||||
<U as Encoder<I>>::Error: fmt::Debug,
|
||||
<U as Decoder>::Error: fmt::Debug,
|
||||
{
|
||||
type Output = Result<(), DispatcherError<S::Error, U, I>>;
|
||||
|
||||
@@ -290,10 +311,8 @@ where
|
||||
}
|
||||
State::Error(_) => {
|
||||
// flush write buffer
|
||||
if !this.framed.is_write_buf_empty() {
|
||||
if let Poll::Pending = this.framed.flush(cx) {
|
||||
return Poll::Pending;
|
||||
}
|
||||
if !this.framed.is_write_buf_empty() && this.framed.flush(cx).is_pending() {
|
||||
return Poll::Pending;
|
||||
}
|
||||
Poll::Ready(Err(this.state.take_error()))
|
||||
}
|
||||
|
@@ -1,153 +0,0 @@
|
||||
//! Contains `Either` service and related types and functions.
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
use actix_service::{Service, ServiceFactory};
|
||||
use futures_util::{future, future::Future, ready};
|
||||
|
||||
/// Combine two different service types into a single type.
|
||||
///
|
||||
/// Both services must be of the same request, response, and error types.
|
||||
/// `EitherService` is useful for handling conditional branching in service
|
||||
/// middleware to different inner service types.
|
||||
pub struct EitherService<A, B> {
|
||||
left: A,
|
||||
right: B,
|
||||
}
|
||||
|
||||
impl<A: Clone, B: Clone> Clone for EitherService<A, B> {
|
||||
fn clone(&self) -> Self {
|
||||
EitherService {
|
||||
left: self.left.clone(),
|
||||
right: self.right.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B> Service for EitherService<A, B>
|
||||
where
|
||||
A: Service,
|
||||
B: Service<Response = A::Response, Error = A::Error>,
|
||||
{
|
||||
type Request = either::Either<A::Request, B::Request>;
|
||||
type Response = A::Response;
|
||||
type Error = A::Error;
|
||||
type Future = future::Either<A::Future, B::Future>;
|
||||
|
||||
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
let left = self.left.poll_ready(cx)?;
|
||||
let right = self.right.poll_ready(cx)?;
|
||||
|
||||
if left.is_ready() && right.is_ready() {
|
||||
Poll::Ready(Ok(()))
|
||||
} else {
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
|
||||
fn call(&mut self, req: either::Either<A::Request, B::Request>) -> Self::Future {
|
||||
match req {
|
||||
either::Either::Left(req) => future::Either::Left(self.left.call(req)),
|
||||
either::Either::Right(req) => future::Either::Right(self.right.call(req)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Combine two different new service types into a single service.
|
||||
pub struct Either<A, B> {
|
||||
left: A,
|
||||
right: B,
|
||||
}
|
||||
|
||||
impl<A, B> Either<A, B> {
|
||||
pub fn new(left: A, right: B) -> Either<A, B>
|
||||
where
|
||||
A: ServiceFactory,
|
||||
A::Config: Clone,
|
||||
B: ServiceFactory<
|
||||
Config = A::Config,
|
||||
Response = A::Response,
|
||||
Error = A::Error,
|
||||
InitError = A::InitError,
|
||||
>,
|
||||
{
|
||||
Either { left, right }
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B> ServiceFactory for Either<A, B>
|
||||
where
|
||||
A: ServiceFactory,
|
||||
A::Config: Clone,
|
||||
B: ServiceFactory<
|
||||
Config = A::Config,
|
||||
Response = A::Response,
|
||||
Error = A::Error,
|
||||
InitError = A::InitError,
|
||||
>,
|
||||
{
|
||||
type Request = either::Either<A::Request, B::Request>;
|
||||
type Response = A::Response;
|
||||
type Error = A::Error;
|
||||
type InitError = A::InitError;
|
||||
type Config = A::Config;
|
||||
type Service = EitherService<A::Service, B::Service>;
|
||||
type Future = EitherNewService<A, B>;
|
||||
|
||||
fn new_service(&self, cfg: A::Config) -> Self::Future {
|
||||
EitherNewService {
|
||||
left: None,
|
||||
right: None,
|
||||
left_fut: self.left.new_service(cfg.clone()),
|
||||
right_fut: self.right.new_service(cfg),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Clone, B: Clone> Clone for Either<A, B> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
left: self.left.clone(),
|
||||
right: self.right.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[pin_project::pin_project]
|
||||
pub struct EitherNewService<A: ServiceFactory, B: ServiceFactory> {
|
||||
left: Option<A::Service>,
|
||||
right: Option<B::Service>,
|
||||
#[pin]
|
||||
left_fut: A::Future,
|
||||
#[pin]
|
||||
right_fut: B::Future,
|
||||
}
|
||||
|
||||
impl<A, B> Future for EitherNewService<A, B>
|
||||
where
|
||||
A: ServiceFactory,
|
||||
B: ServiceFactory<Response = A::Response, Error = A::Error, InitError = A::InitError>,
|
||||
{
|
||||
type Output = Result<EitherService<A::Service, B::Service>, A::InitError>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let this = self.project();
|
||||
|
||||
if this.left.is_none() {
|
||||
*this.left = Some(ready!(this.left_fut.poll(cx))?);
|
||||
}
|
||||
if this.right.is_none() {
|
||||
*this.right = Some(ready!(this.right_fut.poll(cx))?);
|
||||
}
|
||||
|
||||
if this.left.is_some() && this.right.is_some() {
|
||||
Poll::Ready(Ok(EitherService {
|
||||
left: this.left.take().unwrap(),
|
||||
right: this.right.take().unwrap(),
|
||||
}))
|
||||
} else {
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,169 +0,0 @@
|
||||
use std::convert::Infallible;
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
use actix_service::{IntoService, Service, Transform};
|
||||
use futures_util::future::{ok, Ready};
|
||||
|
||||
use super::counter::{Counter, CounterGuard};
|
||||
|
||||
/// InFlight - new service for service that can limit number of in-flight
|
||||
/// async requests.
|
||||
///
|
||||
/// Default number of in-flight requests is 15
|
||||
pub struct InFlight {
|
||||
max_inflight: usize,
|
||||
}
|
||||
|
||||
impl InFlight {
|
||||
pub fn new(max: usize) -> Self {
|
||||
Self { max_inflight: max }
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for InFlight {
|
||||
fn default() -> Self {
|
||||
Self::new(15)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> Transform<S> for InFlight
|
||||
where
|
||||
S: Service,
|
||||
{
|
||||
type Request = S::Request;
|
||||
type Response = S::Response;
|
||||
type Error = S::Error;
|
||||
type InitError = Infallible;
|
||||
type Transform = InFlightService<S>;
|
||||
type Future = Ready<Result<Self::Transform, Self::InitError>>;
|
||||
|
||||
fn new_transform(&self, service: S) -> Self::Future {
|
||||
ok(InFlightService::new(self.max_inflight, service))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct InFlightService<S> {
|
||||
count: Counter,
|
||||
service: S,
|
||||
}
|
||||
|
||||
impl<S> InFlightService<S>
|
||||
where
|
||||
S: Service,
|
||||
{
|
||||
pub fn new<U>(max: usize, service: U) -> Self
|
||||
where
|
||||
U: IntoService<S>,
|
||||
{
|
||||
Self {
|
||||
count: Counter::new(max),
|
||||
service: service.into_service(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Service for InFlightService<T>
|
||||
where
|
||||
T: Service,
|
||||
{
|
||||
type Request = T::Request;
|
||||
type Response = T::Response;
|
||||
type Error = T::Error;
|
||||
type Future = InFlightServiceResponse<T>;
|
||||
|
||||
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
if let Poll::Pending = self.service.poll_ready(cx)? {
|
||||
Poll::Pending
|
||||
} else if !self.count.available(cx) {
|
||||
log::trace!("InFlight limit exceeded");
|
||||
Poll::Pending
|
||||
} else {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
}
|
||||
|
||||
fn call(&mut self, req: T::Request) -> Self::Future {
|
||||
InFlightServiceResponse {
|
||||
fut: self.service.call(req),
|
||||
_guard: self.count.get(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[pin_project::pin_project]
|
||||
pub struct InFlightServiceResponse<T: Service> {
|
||||
#[pin]
|
||||
fut: T::Future,
|
||||
_guard: CounterGuard,
|
||||
}
|
||||
|
||||
impl<T: Service> Future for InFlightServiceResponse<T> {
|
||||
type Output = Result<T::Response, T::Error>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
self.project().fut.poll(cx)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use std::task::{Context, Poll};
|
||||
use std::time::Duration;
|
||||
|
||||
use super::*;
|
||||
use actix_service::{apply, fn_factory, Service, ServiceFactory};
|
||||
use futures_util::future::{lazy, ok, FutureExt, LocalBoxFuture};
|
||||
|
||||
struct SleepService(Duration);
|
||||
|
||||
impl Service for SleepService {
|
||||
type Request = ();
|
||||
type Response = ();
|
||||
type Error = ();
|
||||
type Future = LocalBoxFuture<'static, Result<(), ()>>;
|
||||
|
||||
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
fn call(&mut self, _: ()) -> Self::Future {
|
||||
actix_rt::time::delay_for(self.0)
|
||||
.then(|_| ok::<_, ()>(()))
|
||||
.boxed_local()
|
||||
}
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_transform() {
|
||||
let wait_time = Duration::from_millis(50);
|
||||
|
||||
let mut srv = InFlightService::new(1, SleepService(wait_time));
|
||||
assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
|
||||
|
||||
let res = srv.call(());
|
||||
assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Pending);
|
||||
|
||||
let _ = res.await;
|
||||
assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_new_transform() {
|
||||
let wait_time = Duration::from_millis(50);
|
||||
|
||||
let srv = apply(InFlight::new(1), fn_factory(|| ok(SleepService(wait_time))));
|
||||
|
||||
let mut srv = srv.new_service(&()).await.unwrap();
|
||||
assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
|
||||
|
||||
let res = srv.call(());
|
||||
assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Pending);
|
||||
|
||||
let _ = res.await;
|
||||
assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
|
||||
}
|
||||
}
|
@@ -1,125 +0,0 @@
|
||||
use std::convert::Infallible;
|
||||
use std::future::Future;
|
||||
use std::marker::PhantomData;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
use std::time::Duration;
|
||||
|
||||
use actix_rt::time::{delay_until, Delay, Instant};
|
||||
use actix_service::{Service, ServiceFactory};
|
||||
use futures_util::future::{ok, Ready};
|
||||
|
||||
use super::time::{LowResTime, LowResTimeService};
|
||||
|
||||
pub struct KeepAlive<R, E, F> {
|
||||
f: F,
|
||||
ka: Duration,
|
||||
time: LowResTime,
|
||||
_t: PhantomData<(R, E)>,
|
||||
}
|
||||
|
||||
impl<R, E, F> KeepAlive<R, E, F>
|
||||
where
|
||||
F: Fn() -> E + Clone,
|
||||
{
|
||||
pub fn new(ka: Duration, time: LowResTime, f: F) -> Self {
|
||||
KeepAlive {
|
||||
f,
|
||||
ka,
|
||||
time,
|
||||
_t: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<R, E, F> Clone for KeepAlive<R, E, F>
|
||||
where
|
||||
F: Clone,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
KeepAlive {
|
||||
f: self.f.clone(),
|
||||
ka: self.ka,
|
||||
time: self.time.clone(),
|
||||
_t: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<R, E, F> ServiceFactory for KeepAlive<R, E, F>
|
||||
where
|
||||
F: Fn() -> E + Clone,
|
||||
{
|
||||
type Request = R;
|
||||
type Response = R;
|
||||
type Error = E;
|
||||
type InitError = Infallible;
|
||||
type Config = ();
|
||||
type Service = KeepAliveService<R, E, F>;
|
||||
type Future = Ready<Result<Self::Service, Self::InitError>>;
|
||||
|
||||
fn new_service(&self, _: ()) -> Self::Future {
|
||||
ok(KeepAliveService::new(
|
||||
self.ka,
|
||||
self.time.timer(),
|
||||
self.f.clone(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct KeepAliveService<R, E, F> {
|
||||
f: F,
|
||||
ka: Duration,
|
||||
time: LowResTimeService,
|
||||
delay: Delay,
|
||||
expire: Instant,
|
||||
_t: PhantomData<(R, E)>,
|
||||
}
|
||||
|
||||
impl<R, E, F> KeepAliveService<R, E, F>
|
||||
where
|
||||
F: Fn() -> E,
|
||||
{
|
||||
pub fn new(ka: Duration, time: LowResTimeService, f: F) -> Self {
|
||||
let expire = Instant::from_std(time.now() + ka);
|
||||
KeepAliveService {
|
||||
f,
|
||||
ka,
|
||||
time,
|
||||
expire,
|
||||
delay: delay_until(expire),
|
||||
_t: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<R, E, F> Service for KeepAliveService<R, E, F>
|
||||
where
|
||||
F: Fn() -> E,
|
||||
{
|
||||
type Request = R;
|
||||
type Response = R;
|
||||
type Error = E;
|
||||
type Future = Ready<Result<R, E>>;
|
||||
|
||||
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
match Pin::new(&mut self.delay).poll(cx) {
|
||||
Poll::Ready(_) => {
|
||||
let now = Instant::from_std(self.time.now());
|
||||
if self.expire <= now {
|
||||
Poll::Ready(Err((self.f)()))
|
||||
} else {
|
||||
self.delay.reset(self.expire);
|
||||
let _ = Pin::new(&mut self.delay).poll(cx);
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
}
|
||||
Poll::Pending => Poll::Ready(Ok(())),
|
||||
}
|
||||
}
|
||||
|
||||
fn call(&mut self, req: R) -> Self::Future {
|
||||
self.expire = Instant::from_std(self.time.now() + self.ka);
|
||||
ok(req)
|
||||
}
|
||||
}
|
@@ -1,18 +1,12 @@
|
||||
//! Actix utils - various helper services
|
||||
//! Various network related services and utilities for the Actix ecosystem.
|
||||
|
||||
#![deny(rust_2018_idioms)]
|
||||
#![deny(rust_2018_idioms, nonstandard_style)]
|
||||
#![allow(clippy::type_complexity)]
|
||||
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
||||
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
||||
|
||||
pub mod condition;
|
||||
pub mod counter;
|
||||
pub mod dispatcher;
|
||||
pub mod either;
|
||||
pub mod inflight;
|
||||
pub mod keepalive;
|
||||
pub mod mpsc;
|
||||
pub mod oneshot;
|
||||
pub mod order;
|
||||
pub mod stream;
|
||||
pub mod task;
|
||||
pub mod time;
|
||||
pub mod timeout;
|
||||
|
@@ -1,15 +1,17 @@
|
||||
//! A multi-producer, single-consumer, futures-aware, FIFO queue.
|
||||
use std::any::Any;
|
||||
use std::cell::RefCell;
|
||||
|
||||
use core::any::Any;
|
||||
use core::cell::RefCell;
|
||||
use core::fmt;
|
||||
use core::pin::Pin;
|
||||
use core::task::{Context, Poll};
|
||||
|
||||
use std::collections::VecDeque;
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
use std::pin::Pin;
|
||||
use std::rc::Rc;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
use futures_core::stream::Stream;
|
||||
use futures_sink::Sink;
|
||||
use futures_util::stream::Stream;
|
||||
|
||||
use crate::task::LocalWaker;
|
||||
|
||||
|
@@ -1,316 +0,0 @@
|
||||
//! A one-shot, futures-aware channel.
|
||||
use std::cell::RefCell;
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use std::rc::Rc;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
pub use futures_channel::oneshot::Canceled;
|
||||
use slab::Slab;
|
||||
|
||||
use crate::task::LocalWaker;
|
||||
|
||||
/// Creates a new futures-aware, one-shot channel.
|
||||
pub fn channel<T>() -> (Sender<T>, Receiver<T>) {
|
||||
let inner = Rc::new(RefCell::new(Inner {
|
||||
value: None,
|
||||
rx_task: LocalWaker::new(),
|
||||
}));
|
||||
let tx = Sender {
|
||||
inner: inner.clone(),
|
||||
};
|
||||
let rx = Receiver { inner };
|
||||
(tx, rx)
|
||||
}
|
||||
|
||||
/// Creates a new futures-aware, pool of one-shot's.
|
||||
pub fn pool<T>() -> Pool<T> {
|
||||
Pool(Rc::new(RefCell::new(Slab::new())))
|
||||
}
|
||||
|
||||
/// Represents the completion half of a oneshot through which the result of a
|
||||
/// computation is signaled.
|
||||
#[derive(Debug)]
|
||||
pub struct Sender<T> {
|
||||
inner: Rc<RefCell<Inner<T>>>,
|
||||
}
|
||||
|
||||
/// A future representing the completion of a computation happening elsewhere in
|
||||
/// memory.
|
||||
#[derive(Debug)]
|
||||
#[must_use = "futures do nothing unless polled"]
|
||||
pub struct Receiver<T> {
|
||||
inner: Rc<RefCell<Inner<T>>>,
|
||||
}
|
||||
|
||||
// The channels do not ever project Pin to the inner T
|
||||
impl<T> Unpin for Receiver<T> {}
|
||||
impl<T> Unpin for Sender<T> {}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Inner<T> {
|
||||
value: Option<T>,
|
||||
rx_task: LocalWaker,
|
||||
}
|
||||
|
||||
impl<T> Sender<T> {
|
||||
/// Completes this oneshot with a successful result.
|
||||
///
|
||||
/// This function will consume `self` and indicate to the other end, the
|
||||
/// `Receiver`, that the error provided is the result of the computation this
|
||||
/// represents.
|
||||
///
|
||||
/// If the value is successfully enqueued for the remote end to receive,
|
||||
/// then `Ok(())` is returned. If the receiving end was dropped before
|
||||
/// this function was called, however, then `Err` is returned with the value
|
||||
/// provided.
|
||||
pub fn send(self, val: T) -> Result<(), T> {
|
||||
if Rc::strong_count(&self.inner) == 2 {
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
inner.value = Some(val);
|
||||
inner.rx_task.wake();
|
||||
Ok(())
|
||||
} else {
|
||||
Err(val)
|
||||
}
|
||||
}
|
||||
|
||||
/// Tests to see whether this `Sender`'s corresponding `Receiver`
|
||||
/// has gone away.
|
||||
pub fn is_canceled(&self) -> bool {
|
||||
Rc::strong_count(&self.inner) == 1
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for Sender<T> {
|
||||
fn drop(&mut self) {
|
||||
self.inner.borrow().rx_task.wake();
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Future for Receiver<T> {
|
||||
type Output = Result<T, Canceled>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let this = self.get_mut();
|
||||
|
||||
// If we've got a value, then skip the logic below as we're done.
|
||||
if let Some(val) = this.inner.borrow_mut().value.take() {
|
||||
return Poll::Ready(Ok(val));
|
||||
}
|
||||
|
||||
// Check if sender is dropped and return error if it is.
|
||||
if Rc::strong_count(&this.inner) == 1 {
|
||||
Poll::Ready(Err(Canceled))
|
||||
} else {
|
||||
this.inner.borrow().rx_task.register(cx.waker());
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Futures-aware, pool of one-shot's.
|
||||
pub struct Pool<T>(Rc<RefCell<Slab<PoolInner<T>>>>);
|
||||
|
||||
bitflags::bitflags! {
|
||||
pub struct Flags: u8 {
|
||||
const SENDER = 0b0000_0001;
|
||||
const RECEIVER = 0b0000_0010;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct PoolInner<T> {
|
||||
flags: Flags,
|
||||
value: Option<T>,
|
||||
waker: LocalWaker,
|
||||
}
|
||||
|
||||
impl<T> Pool<T> {
|
||||
pub fn channel(&mut self) -> (PSender<T>, PReceiver<T>) {
|
||||
let token = self.0.borrow_mut().insert(PoolInner {
|
||||
flags: Flags::all(),
|
||||
value: None,
|
||||
waker: LocalWaker::default(),
|
||||
});
|
||||
|
||||
(
|
||||
PSender {
|
||||
token,
|
||||
inner: self.0.clone(),
|
||||
},
|
||||
PReceiver {
|
||||
token,
|
||||
inner: self.0.clone(),
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Clone for Pool<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Pool(self.0.clone())
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents the completion half of a oneshot through which the result of a
|
||||
/// computation is signaled.
|
||||
#[derive(Debug)]
|
||||
pub struct PSender<T> {
|
||||
token: usize,
|
||||
inner: Rc<RefCell<Slab<PoolInner<T>>>>,
|
||||
}
|
||||
|
||||
/// A future representing the completion of a computation happening elsewhere in
|
||||
/// memory.
|
||||
#[derive(Debug)]
|
||||
#[must_use = "futures do nothing unless polled"]
|
||||
pub struct PReceiver<T> {
|
||||
token: usize,
|
||||
inner: Rc<RefCell<Slab<PoolInner<T>>>>,
|
||||
}
|
||||
|
||||
// The one-shots do not ever project Pin to the inner T
|
||||
impl<T> Unpin for PReceiver<T> {}
|
||||
impl<T> Unpin for PSender<T> {}
|
||||
|
||||
impl<T> PSender<T> {
|
||||
/// Completes this oneshot with a successful result.
|
||||
///
|
||||
/// This function will consume `self` and indicate to the other end, the
|
||||
/// `Receiver`, that the error provided is the result of the computation this
|
||||
/// represents.
|
||||
///
|
||||
/// If the value is successfully enqueued for the remote end to receive,
|
||||
/// then `Ok(())` is returned. If the receiving end was dropped before
|
||||
/// this function was called, however, then `Err` is returned with the value
|
||||
/// provided.
|
||||
pub fn send(self, val: T) -> Result<(), T> {
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
let inner = unsafe { inner.get_unchecked_mut(self.token) };
|
||||
|
||||
if inner.flags.contains(Flags::RECEIVER) {
|
||||
inner.value = Some(val);
|
||||
inner.waker.wake();
|
||||
Ok(())
|
||||
} else {
|
||||
Err(val)
|
||||
}
|
||||
}
|
||||
|
||||
/// Tests to see whether this `Sender`'s corresponding `Receiver`
|
||||
/// has gone away.
|
||||
pub fn is_canceled(&self) -> bool {
|
||||
!unsafe { self.inner.borrow().get_unchecked(self.token) }
|
||||
.flags
|
||||
.contains(Flags::RECEIVER)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for PSender<T> {
|
||||
fn drop(&mut self) {
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
let inner_token = unsafe { inner.get_unchecked_mut(self.token) };
|
||||
if inner_token.flags.contains(Flags::RECEIVER) {
|
||||
inner_token.waker.wake();
|
||||
inner_token.flags.remove(Flags::SENDER);
|
||||
} else {
|
||||
inner.remove(self.token);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for PReceiver<T> {
|
||||
fn drop(&mut self) {
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
let inner_token = unsafe { inner.get_unchecked_mut(self.token) };
|
||||
if inner_token.flags.contains(Flags::SENDER) {
|
||||
inner_token.flags.remove(Flags::RECEIVER);
|
||||
} else {
|
||||
inner.remove(self.token);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Future for PReceiver<T> {
|
||||
type Output = Result<T, Canceled>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let this = self.get_mut();
|
||||
let mut inner = this.inner.borrow_mut();
|
||||
let inner = unsafe { inner.get_unchecked_mut(this.token) };
|
||||
|
||||
// If we've got a value, then skip the logic below as we're done.
|
||||
if let Some(val) = inner.value.take() {
|
||||
return Poll::Ready(Ok(val));
|
||||
}
|
||||
|
||||
// Check if sender is dropped and return error if it is.
|
||||
if !inner.flags.contains(Flags::SENDER) {
|
||||
Poll::Ready(Err(Canceled))
|
||||
} else {
|
||||
inner.waker.register(cx.waker());
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use futures_util::future::lazy;
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_oneshot() {
|
||||
let (tx, rx) = channel();
|
||||
tx.send("test").unwrap();
|
||||
assert_eq!(rx.await.unwrap(), "test");
|
||||
|
||||
let (tx, rx) = channel();
|
||||
assert!(!tx.is_canceled());
|
||||
drop(rx);
|
||||
assert!(tx.is_canceled());
|
||||
assert!(tx.send("test").is_err());
|
||||
|
||||
let (tx, rx) = channel::<&'static str>();
|
||||
drop(tx);
|
||||
assert!(rx.await.is_err());
|
||||
|
||||
let (tx, mut rx) = channel::<&'static str>();
|
||||
assert_eq!(lazy(|cx| Pin::new(&mut rx).poll(cx)).await, Poll::Pending);
|
||||
tx.send("test").unwrap();
|
||||
assert_eq!(rx.await.unwrap(), "test");
|
||||
|
||||
let (tx, mut rx) = channel::<&'static str>();
|
||||
assert_eq!(lazy(|cx| Pin::new(&mut rx).poll(cx)).await, Poll::Pending);
|
||||
drop(tx);
|
||||
assert!(rx.await.is_err());
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_pool() {
|
||||
let (tx, rx) = pool().channel();
|
||||
tx.send("test").unwrap();
|
||||
assert_eq!(rx.await.unwrap(), "test");
|
||||
|
||||
let (tx, rx) = pool().channel();
|
||||
assert!(!tx.is_canceled());
|
||||
drop(rx);
|
||||
assert!(tx.is_canceled());
|
||||
assert!(tx.send("test").is_err());
|
||||
|
||||
let (tx, rx) = pool::<&'static str>().channel();
|
||||
drop(tx);
|
||||
assert!(rx.await.is_err());
|
||||
|
||||
let (tx, mut rx) = pool::<&'static str>().channel();
|
||||
assert_eq!(lazy(|cx| Pin::new(&mut rx).poll(cx)).await, Poll::Pending);
|
||||
tx.send("test").unwrap();
|
||||
assert_eq!(rx.await.unwrap(), "test");
|
||||
|
||||
let (tx, mut rx) = pool::<&'static str>().channel();
|
||||
assert_eq!(lazy(|cx| Pin::new(&mut rx).poll(cx)).await, Poll::Pending);
|
||||
drop(tx);
|
||||
assert!(rx.await.is_err());
|
||||
}
|
||||
}
|
@@ -1,278 +0,0 @@
|
||||
use std::collections::VecDeque;
|
||||
use std::convert::Infallible;
|
||||
use std::fmt;
|
||||
use std::future::Future;
|
||||
use std::marker::PhantomData;
|
||||
use std::pin::Pin;
|
||||
use std::rc::Rc;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
use actix_service::{IntoService, Service, Transform};
|
||||
use futures_util::future::{ok, Ready};
|
||||
|
||||
use crate::oneshot;
|
||||
use crate::task::LocalWaker;
|
||||
|
||||
struct Record<I, E> {
|
||||
rx: oneshot::Receiver<Result<I, E>>,
|
||||
tx: oneshot::Sender<Result<I, E>>,
|
||||
}
|
||||
|
||||
/// Timeout error
|
||||
pub enum InOrderError<E> {
|
||||
/// Service error
|
||||
Service(E),
|
||||
/// Service call dropped
|
||||
Disconnected,
|
||||
}
|
||||
|
||||
impl<E> From<E> for InOrderError<E> {
|
||||
fn from(err: E) -> Self {
|
||||
InOrderError::Service(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: fmt::Debug> fmt::Debug for InOrderError<E> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
InOrderError::Service(e) => write!(f, "InOrderError::Service({:?})", e),
|
||||
InOrderError::Disconnected => write!(f, "InOrderError::Disconnected"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: fmt::Display> fmt::Display for InOrderError<E> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
InOrderError::Service(e) => e.fmt(f),
|
||||
InOrderError::Disconnected => write!(f, "InOrder service disconnected"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// InOrder - The service will yield responses as they become available,
|
||||
/// in the order that their originating requests were submitted to the service.
|
||||
pub struct InOrder<S> {
|
||||
_t: PhantomData<S>,
|
||||
}
|
||||
|
||||
impl<S> InOrder<S>
|
||||
where
|
||||
S: Service,
|
||||
S::Response: 'static,
|
||||
S::Future: 'static,
|
||||
S::Error: 'static,
|
||||
{
|
||||
pub fn new() -> Self {
|
||||
Self { _t: PhantomData }
|
||||
}
|
||||
|
||||
pub fn service(service: S) -> InOrderService<S> {
|
||||
InOrderService::new(service)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> Default for InOrder<S>
|
||||
where
|
||||
S: Service,
|
||||
S::Response: 'static,
|
||||
S::Future: 'static,
|
||||
S::Error: 'static,
|
||||
{
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> Transform<S> for InOrder<S>
|
||||
where
|
||||
S: Service,
|
||||
S::Response: 'static,
|
||||
S::Future: 'static,
|
||||
S::Error: 'static,
|
||||
{
|
||||
type Request = S::Request;
|
||||
type Response = S::Response;
|
||||
type Error = InOrderError<S::Error>;
|
||||
type InitError = Infallible;
|
||||
type Transform = InOrderService<S>;
|
||||
type Future = Ready<Result<Self::Transform, Self::InitError>>;
|
||||
|
||||
fn new_transform(&self, service: S) -> Self::Future {
|
||||
ok(InOrderService::new(service))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct InOrderService<S: Service> {
|
||||
service: S,
|
||||
waker: Rc<LocalWaker>,
|
||||
acks: VecDeque<Record<S::Response, S::Error>>,
|
||||
}
|
||||
|
||||
impl<S> InOrderService<S>
|
||||
where
|
||||
S: Service,
|
||||
S::Response: 'static,
|
||||
S::Future: 'static,
|
||||
S::Error: 'static,
|
||||
{
|
||||
pub fn new<U>(service: U) -> Self
|
||||
where
|
||||
U: IntoService<S>,
|
||||
{
|
||||
Self {
|
||||
service: service.into_service(),
|
||||
acks: VecDeque::new(),
|
||||
waker: Rc::new(LocalWaker::new()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> Service for InOrderService<S>
|
||||
where
|
||||
S: Service,
|
||||
S::Response: 'static,
|
||||
S::Future: 'static,
|
||||
S::Error: 'static,
|
||||
{
|
||||
type Request = S::Request;
|
||||
type Response = S::Response;
|
||||
type Error = InOrderError<S::Error>;
|
||||
type Future = InOrderServiceResponse<S>;
|
||||
|
||||
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
// poll_ready could be called from different task
|
||||
self.waker.register(cx.waker());
|
||||
|
||||
// check acks
|
||||
while !self.acks.is_empty() {
|
||||
let rec = self.acks.front_mut().unwrap();
|
||||
match Pin::new(&mut rec.rx).poll(cx) {
|
||||
Poll::Ready(Ok(res)) => {
|
||||
let rec = self.acks.pop_front().unwrap();
|
||||
let _ = rec.tx.send(res);
|
||||
}
|
||||
Poll::Pending => break,
|
||||
Poll::Ready(Err(oneshot::Canceled)) => {
|
||||
return Poll::Ready(Err(InOrderError::Disconnected))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check nested service
|
||||
if let Poll::Pending = self.service.poll_ready(cx).map_err(InOrderError::Service)? {
|
||||
Poll::Pending
|
||||
} else {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
}
|
||||
|
||||
fn call(&mut self, request: S::Request) -> Self::Future {
|
||||
let (tx1, rx1) = oneshot::channel();
|
||||
let (tx2, rx2) = oneshot::channel();
|
||||
self.acks.push_back(Record { rx: rx1, tx: tx2 });
|
||||
|
||||
let waker = self.waker.clone();
|
||||
let fut = self.service.call(request);
|
||||
actix_rt::spawn(async move {
|
||||
let res = fut.await;
|
||||
waker.wake();
|
||||
let _ = tx1.send(res);
|
||||
});
|
||||
|
||||
InOrderServiceResponse { rx: rx2 }
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct InOrderServiceResponse<S: Service> {
|
||||
rx: oneshot::Receiver<Result<S::Response, S::Error>>,
|
||||
}
|
||||
|
||||
impl<S: Service> Future for InOrderServiceResponse<S> {
|
||||
type Output = Result<S::Response, InOrderError<S::Error>>;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
match Pin::new(&mut self.rx).poll(cx) {
|
||||
Poll::Pending => Poll::Pending,
|
||||
Poll::Ready(Ok(Ok(res))) => Poll::Ready(Ok(res)),
|
||||
Poll::Ready(Ok(Err(e))) => Poll::Ready(Err(e.into())),
|
||||
Poll::Ready(Err(_)) => Poll::Ready(Err(InOrderError::Disconnected)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use std::task::{Context, Poll};
|
||||
use std::time::Duration;
|
||||
|
||||
use super::*;
|
||||
use actix_service::Service;
|
||||
use futures_channel::oneshot;
|
||||
use futures_util::future::{lazy, poll_fn, FutureExt, LocalBoxFuture};
|
||||
|
||||
struct Srv;
|
||||
|
||||
impl Service for Srv {
|
||||
type Request = oneshot::Receiver<usize>;
|
||||
type Response = usize;
|
||||
type Error = ();
|
||||
type Future = LocalBoxFuture<'static, Result<usize, ()>>;
|
||||
|
||||
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
fn call(&mut self, req: oneshot::Receiver<usize>) -> Self::Future {
|
||||
req.map(|res| res.map_err(|_| ())).boxed_local()
|
||||
}
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_in_order() {
|
||||
let (tx1, rx1) = oneshot::channel();
|
||||
let (tx2, rx2) = oneshot::channel();
|
||||
let (tx3, rx3) = oneshot::channel();
|
||||
let (tx_stop, rx_stop) = oneshot::channel();
|
||||
|
||||
let h = std::thread::spawn(move || {
|
||||
let rx1 = rx1;
|
||||
let rx2 = rx2;
|
||||
let rx3 = rx3;
|
||||
let tx_stop = tx_stop;
|
||||
actix_rt::System::new("test").block_on(async {
|
||||
let mut srv = InOrderService::new(Srv);
|
||||
|
||||
let _ = lazy(|cx| srv.poll_ready(cx)).await;
|
||||
let res1 = srv.call(rx1);
|
||||
let res2 = srv.call(rx2);
|
||||
let res3 = srv.call(rx3);
|
||||
|
||||
actix_rt::spawn(async move {
|
||||
poll_fn(|cx| {
|
||||
let _ = srv.poll_ready(cx);
|
||||
Poll::<()>::Pending
|
||||
})
|
||||
.await;
|
||||
});
|
||||
|
||||
assert_eq!(res1.await.unwrap(), 1);
|
||||
assert_eq!(res2.await.unwrap(), 2);
|
||||
assert_eq!(res3.await.unwrap(), 3);
|
||||
|
||||
let _ = tx_stop.send(());
|
||||
actix_rt::System::current().stop();
|
||||
});
|
||||
});
|
||||
|
||||
let _ = tx3.send(3);
|
||||
std::thread::sleep(Duration::from_millis(50));
|
||||
let _ = tx2.send(2);
|
||||
let _ = tx1.send(1);
|
||||
|
||||
let _ = rx_stop.await;
|
||||
let _ = h.join();
|
||||
}
|
||||
}
|
@@ -1,76 +0,0 @@
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
use actix_service::{IntoService, Service};
|
||||
use futures_util::{stream::Stream, FutureExt};
|
||||
|
||||
use crate::mpsc;
|
||||
|
||||
#[pin_project::pin_project]
|
||||
pub struct Dispatcher<S, T>
|
||||
where
|
||||
S: Stream,
|
||||
T: Service<Request = S::Item, Response = ()> + 'static,
|
||||
{
|
||||
#[pin]
|
||||
stream: S,
|
||||
service: T,
|
||||
err_rx: mpsc::Receiver<T::Error>,
|
||||
err_tx: mpsc::Sender<T::Error>,
|
||||
}
|
||||
|
||||
impl<S, T> Dispatcher<S, T>
|
||||
where
|
||||
S: Stream,
|
||||
T: Service<Request = S::Item, Response = ()> + 'static,
|
||||
{
|
||||
pub fn new<F>(stream: S, service: F) -> Self
|
||||
where
|
||||
F: IntoService<T>,
|
||||
{
|
||||
let (err_tx, err_rx) = mpsc::channel();
|
||||
Dispatcher {
|
||||
err_rx,
|
||||
err_tx,
|
||||
stream,
|
||||
service: service.into_service(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, T> Future for Dispatcher<S, T>
|
||||
where
|
||||
S: Stream,
|
||||
T: Service<Request = S::Item, Response = ()> + 'static,
|
||||
{
|
||||
type Output = Result<(), T::Error>;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let mut this = self.as_mut().project();
|
||||
|
||||
if let Poll::Ready(Some(e)) = Pin::new(&mut this.err_rx).poll_next(cx) {
|
||||
return Poll::Ready(Err(e));
|
||||
}
|
||||
|
||||
loop {
|
||||
return match this.service.poll_ready(cx)? {
|
||||
Poll::Ready(_) => match this.stream.poll_next(cx) {
|
||||
Poll::Ready(Some(item)) => {
|
||||
let stop = this.err_tx.clone();
|
||||
actix_rt::spawn(this.service.call(item).map(move |res| {
|
||||
if let Err(e) = res {
|
||||
let _ = stop.send(e);
|
||||
}
|
||||
}));
|
||||
this = self.as_mut().project();
|
||||
continue;
|
||||
}
|
||||
Poll::Pending => Poll::Pending,
|
||||
Poll::Ready(None) => Poll::Ready(Ok(())),
|
||||
},
|
||||
Poll::Pending => Poll::Pending,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,7 +1,7 @@
|
||||
use std::cell::UnsafeCell;
|
||||
use std::marker::PhantomData;
|
||||
use std::task::Waker;
|
||||
use std::{fmt, rc};
|
||||
use core::cell::UnsafeCell;
|
||||
use core::fmt;
|
||||
use core::marker::PhantomData;
|
||||
use core::task::Waker;
|
||||
|
||||
/// A synchronization primitive for task wakeup.
|
||||
///
|
||||
@@ -19,10 +19,12 @@ use std::{fmt, rc};
|
||||
///
|
||||
/// 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<Option<Waker>>,
|
||||
_t: PhantomData<rc::Rc<()>>,
|
||||
// mark LocalWaker as a !Send type.
|
||||
_t: PhantomData<*const ()>,
|
||||
}
|
||||
|
||||
impl LocalWaker {
|
||||
@@ -34,6 +36,10 @@ impl LocalWaker {
|
||||
}
|
||||
}
|
||||
|
||||
#[deprecated(
|
||||
since = "2.1.0",
|
||||
note = "In favor of `wake`. State of the register doesn't matter at `wake` up"
|
||||
)]
|
||||
#[inline]
|
||||
/// Check if waker has been registered.
|
||||
pub fn is_registered(&self) -> bool {
|
||||
@@ -47,9 +53,8 @@ impl LocalWaker {
|
||||
pub fn register(&self, waker: &Waker) -> bool {
|
||||
unsafe {
|
||||
let w = self.waker.get();
|
||||
let is_registered = (*w).is_some();
|
||||
*w = Some(waker.clone());
|
||||
is_registered
|
||||
let last_waker = w.replace(Some(waker.clone()));
|
||||
last_waker.is_some()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,6 +68,7 @@ impl LocalWaker {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Returns the last `Waker` passed to `register`, so that the user can wake it.
|
||||
///
|
||||
/// If a waker has not been registered, this returns `None`.
|
||||
|
@@ -1,225 +0,0 @@
|
||||
use std::cell::RefCell;
|
||||
use std::convert::Infallible;
|
||||
use std::rc::Rc;
|
||||
use std::task::{Context, Poll};
|
||||
use std::time::{self, Duration, Instant};
|
||||
|
||||
use actix_rt::time::delay_for;
|
||||
use actix_service::{Service, ServiceFactory};
|
||||
use futures_util::future::{ok, ready, FutureExt, Ready};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct LowResTime(Rc<RefCell<Inner>>);
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Inner {
|
||||
resolution: Duration,
|
||||
current: Option<Instant>,
|
||||
}
|
||||
|
||||
impl Inner {
|
||||
fn new(resolution: Duration) -> Self {
|
||||
Inner {
|
||||
resolution,
|
||||
current: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LowResTime {
|
||||
pub fn with(resolution: Duration) -> LowResTime {
|
||||
LowResTime(Rc::new(RefCell::new(Inner::new(resolution))))
|
||||
}
|
||||
|
||||
pub fn timer(&self) -> LowResTimeService {
|
||||
LowResTimeService(self.0.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for LowResTime {
|
||||
fn default() -> Self {
|
||||
LowResTime(Rc::new(RefCell::new(Inner::new(Duration::from_secs(1)))))
|
||||
}
|
||||
}
|
||||
|
||||
impl ServiceFactory for LowResTime {
|
||||
type Request = ();
|
||||
type Response = Instant;
|
||||
type Error = Infallible;
|
||||
type InitError = Infallible;
|
||||
type Config = ();
|
||||
type Service = LowResTimeService;
|
||||
type Future = Ready<Result<Self::Service, Self::InitError>>;
|
||||
|
||||
fn new_service(&self, _: ()) -> Self::Future {
|
||||
ok(self.timer())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct LowResTimeService(Rc<RefCell<Inner>>);
|
||||
|
||||
impl LowResTimeService {
|
||||
pub fn with(resolution: Duration) -> LowResTimeService {
|
||||
LowResTimeService(Rc::new(RefCell::new(Inner::new(resolution))))
|
||||
}
|
||||
|
||||
/// Get current time. This function has to be called from
|
||||
/// future's poll method, otherwise it panics.
|
||||
pub fn now(&self) -> Instant {
|
||||
let cur = self.0.borrow().current;
|
||||
if let Some(cur) = cur {
|
||||
cur
|
||||
} else {
|
||||
let now = Instant::now();
|
||||
let inner = self.0.clone();
|
||||
let interval = {
|
||||
let mut b = inner.borrow_mut();
|
||||
b.current = Some(now);
|
||||
b.resolution
|
||||
};
|
||||
|
||||
actix_rt::spawn(delay_for(interval).then(move |_| {
|
||||
inner.borrow_mut().current.take();
|
||||
ready(())
|
||||
}));
|
||||
now
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Service for LowResTimeService {
|
||||
type Request = ();
|
||||
type Response = Instant;
|
||||
type Error = Infallible;
|
||||
type Future = Ready<Result<Self::Response, Self::Error>>;
|
||||
|
||||
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
fn call(&mut self, _: ()) -> Self::Future {
|
||||
ok(self.now())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SystemTime(Rc<RefCell<SystemTimeInner>>);
|
||||
|
||||
#[derive(Debug)]
|
||||
struct SystemTimeInner {
|
||||
resolution: Duration,
|
||||
current: Option<time::SystemTime>,
|
||||
}
|
||||
|
||||
impl SystemTimeInner {
|
||||
fn new(resolution: Duration) -> Self {
|
||||
SystemTimeInner {
|
||||
resolution,
|
||||
current: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SystemTimeService(Rc<RefCell<SystemTimeInner>>);
|
||||
|
||||
impl SystemTimeService {
|
||||
pub fn with(resolution: Duration) -> SystemTimeService {
|
||||
SystemTimeService(Rc::new(RefCell::new(SystemTimeInner::new(resolution))))
|
||||
}
|
||||
|
||||
/// Get current time. This function has to be called from
|
||||
/// future's poll method, otherwise it panics.
|
||||
pub fn now(&self) -> time::SystemTime {
|
||||
let cur = self.0.borrow().current;
|
||||
if let Some(cur) = cur {
|
||||
cur
|
||||
} else {
|
||||
let now = time::SystemTime::now();
|
||||
let inner = self.0.clone();
|
||||
let interval = {
|
||||
let mut b = inner.borrow_mut();
|
||||
b.current = Some(now);
|
||||
b.resolution
|
||||
};
|
||||
|
||||
actix_rt::spawn(delay_for(interval).then(move |_| {
|
||||
inner.borrow_mut().current.take();
|
||||
ready(())
|
||||
}));
|
||||
now
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::time::{Duration, SystemTime};
|
||||
|
||||
/// State Under Test: Two calls of `SystemTimeService::now()` return the same value if they are done within resolution interval of `SystemTimeService`.
|
||||
///
|
||||
/// Expected Behavior: Two back-to-back calls of `SystemTimeService::now()` return the same value.
|
||||
#[actix_rt::test]
|
||||
async fn system_time_service_time_does_not_immediately_change() {
|
||||
let resolution = Duration::from_millis(50);
|
||||
|
||||
let time_service = SystemTimeService::with(resolution);
|
||||
assert_eq!(time_service.now(), time_service.now());
|
||||
}
|
||||
|
||||
/// State Under Test: Two calls of `LowResTimeService::now()` return the same value if they are done within resolution interval of `SystemTimeService`.
|
||||
///
|
||||
/// Expected Behavior: Two back-to-back calls of `LowResTimeService::now()` return the same value.
|
||||
#[actix_rt::test]
|
||||
async fn low_res_time_service_time_does_not_immediately_change() {
|
||||
let resolution = Duration::from_millis(50);
|
||||
let time_service = LowResTimeService::with(resolution);
|
||||
assert_eq!(time_service.now(), time_service.now());
|
||||
}
|
||||
|
||||
/// State Under Test: `SystemTimeService::now()` updates returned value every resolution period.
|
||||
///
|
||||
/// Expected Behavior: Two calls of `LowResTimeService::now()` made in subsequent resolution interval return different values
|
||||
/// and second value is greater than the first one at least by a resolution interval.
|
||||
#[actix_rt::test]
|
||||
async fn system_time_service_time_updates_after_resolution_interval() {
|
||||
let resolution = Duration::from_millis(100);
|
||||
let wait_time = Duration::from_millis(300);
|
||||
|
||||
let time_service = SystemTimeService::with(resolution);
|
||||
|
||||
let first_time = time_service
|
||||
.now()
|
||||
.duration_since(SystemTime::UNIX_EPOCH)
|
||||
.unwrap();
|
||||
|
||||
delay_for(wait_time).await;
|
||||
|
||||
let second_time = time_service
|
||||
.now()
|
||||
.duration_since(SystemTime::UNIX_EPOCH)
|
||||
.unwrap();
|
||||
|
||||
assert!(second_time - first_time >= wait_time);
|
||||
}
|
||||
|
||||
/// State Under Test: `LowResTimeService::now()` updates returned value every resolution period.
|
||||
///
|
||||
/// Expected Behavior: Two calls of `LowResTimeService::now()` made in subsequent resolution interval return different values
|
||||
/// and second value is greater than the first one at least by a resolution interval.
|
||||
#[actix_rt::test]
|
||||
async fn low_res_time_service_time_updates_after_resolution_interval() {
|
||||
let resolution = Duration::from_millis(100);
|
||||
let wait_time = Duration::from_millis(300);
|
||||
let time_service = LowResTimeService::with(resolution);
|
||||
|
||||
let first_time = time_service.now();
|
||||
|
||||
delay_for(wait_time).await;
|
||||
|
||||
let second_time = time_service.now();
|
||||
assert!(second_time - first_time >= wait_time);
|
||||
}
|
||||
}
|
@@ -1,16 +1,16 @@
|
||||
//! Service that applies a timeout to requests.
|
||||
//!
|
||||
//! If the response does not complete within the specified timeout, the response
|
||||
//! will be aborted.
|
||||
use std::future::Future;
|
||||
use std::marker::PhantomData;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
use std::{fmt, time};
|
||||
//! If the response does not complete within the specified timeout, the response will be aborted.
|
||||
|
||||
use actix_rt::time::{delay_for, Delay};
|
||||
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 futures_util::future::{ok, Ready};
|
||||
use pin_project_lite::pin_project;
|
||||
|
||||
/// Applies a timeout to requests.
|
||||
#[derive(Debug)]
|
||||
@@ -78,126 +78,149 @@ impl<E> Clone for Timeout<E> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, E> Transform<S> for Timeout<E>
|
||||
impl<S, E, Req> Transform<S, Req> for Timeout<E>
|
||||
where
|
||||
S: Service,
|
||||
S: Service<Req>,
|
||||
{
|
||||
type Request = S::Request;
|
||||
type Response = S::Response;
|
||||
type Error = TimeoutError<S::Error>;
|
||||
type Transform = TimeoutService<S, Req>;
|
||||
type InitError = E;
|
||||
type Transform = TimeoutService<S>;
|
||||
type Future = Ready<Result<Self::Transform, Self::InitError>>;
|
||||
type Future = TimeoutFuture<Self::Transform, Self::InitError>;
|
||||
|
||||
fn new_transform(&self, service: S) -> Self::Future {
|
||||
ok(TimeoutService {
|
||||
let service = TimeoutService {
|
||||
service,
|
||||
timeout: self.timeout,
|
||||
})
|
||||
_phantom: PhantomData,
|
||||
};
|
||||
|
||||
TimeoutFuture {
|
||||
service: Some(service),
|
||||
_err: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TimeoutFuture<T, E> {
|
||||
service: Option<T>,
|
||||
_err: PhantomData<E>,
|
||||
}
|
||||
|
||||
impl<T, E> Unpin for TimeoutFuture<T, E> {}
|
||||
|
||||
impl<T, E> Future for TimeoutFuture<T, E> {
|
||||
type Output = Result<T, E>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
Poll::Ready(Ok(self.get_mut().service.take().unwrap()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Applies a timeout to requests.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TimeoutService<S> {
|
||||
pub struct TimeoutService<S, Req> {
|
||||
service: S,
|
||||
timeout: time::Duration,
|
||||
_phantom: PhantomData<Req>,
|
||||
}
|
||||
|
||||
impl<S> TimeoutService<S>
|
||||
impl<S, Req> TimeoutService<S, Req>
|
||||
where
|
||||
S: Service,
|
||||
S: Service<Req>,
|
||||
{
|
||||
pub fn new<U>(timeout: time::Duration, service: U) -> Self
|
||||
where
|
||||
U: IntoService<S>,
|
||||
U: IntoService<S, Req>,
|
||||
{
|
||||
TimeoutService {
|
||||
timeout,
|
||||
service: service.into_service(),
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> Service for TimeoutService<S>
|
||||
impl<S, Req> Service<Req> for TimeoutService<S, Req>
|
||||
where
|
||||
S: Service,
|
||||
S: Service<Req>,
|
||||
{
|
||||
type Request = S::Request;
|
||||
type Response = S::Response;
|
||||
type Error = TimeoutError<S::Error>;
|
||||
type Future = TimeoutServiceResponse<S>;
|
||||
type Future = TimeoutServiceResponse<S, Req>;
|
||||
|
||||
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
self.service.poll_ready(cx).map_err(TimeoutError::Service)
|
||||
}
|
||||
|
||||
fn call(&mut self, request: S::Request) -> Self::Future {
|
||||
fn call(&mut self, request: Req) -> Self::Future {
|
||||
TimeoutServiceResponse {
|
||||
fut: self.service.call(request),
|
||||
sleep: delay_for(self.timeout),
|
||||
sleep: sleep(self.timeout),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// `TimeoutService` response future
|
||||
#[pin_project::pin_project]
|
||||
#[derive(Debug)]
|
||||
pub struct TimeoutServiceResponse<T: Service> {
|
||||
#[pin]
|
||||
fut: T::Future,
|
||||
sleep: Delay,
|
||||
pin_project! {
|
||||
/// `TimeoutService` response future
|
||||
#[derive(Debug)]
|
||||
pub struct TimeoutServiceResponse<S, Req>
|
||||
where
|
||||
S: Service<Req>
|
||||
{
|
||||
#[pin]
|
||||
fut: S::Future,
|
||||
#[pin]
|
||||
sleep: Sleep,
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Future for TimeoutServiceResponse<T>
|
||||
impl<S, Req> Future for TimeoutServiceResponse<S, Req>
|
||||
where
|
||||
T: Service,
|
||||
S: Service<Req>,
|
||||
{
|
||||
type Output = Result<T::Response, TimeoutError<T::Error>>;
|
||||
type Output = Result<S::Response, TimeoutError<S::Error>>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let mut this = self.project();
|
||||
let this = self.project();
|
||||
|
||||
// First, try polling the future
|
||||
match this.fut.poll(cx) {
|
||||
Poll::Ready(Ok(v)) => return Poll::Ready(Ok(v)),
|
||||
Poll::Ready(Err(e)) => return Poll::Ready(Err(TimeoutError::Service(e))),
|
||||
Poll::Pending => {}
|
||||
if let Poll::Ready(res) = this.fut.poll(cx) {
|
||||
return match res {
|
||||
Ok(v) => Poll::Ready(Ok(v)),
|
||||
Err(e) => Poll::Ready(Err(TimeoutError::Service(e))),
|
||||
};
|
||||
}
|
||||
|
||||
// Now check the sleep
|
||||
match Pin::new(&mut this.sleep).poll(cx) {
|
||||
Poll::Pending => Poll::Pending,
|
||||
Poll::Ready(_) => Poll::Ready(Err(TimeoutError::Timeout)),
|
||||
}
|
||||
this.sleep.poll(cx).map(|_| Err(TimeoutError::Timeout))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::task::{Context, Poll};
|
||||
use std::time::Duration;
|
||||
use core::task::Poll;
|
||||
use core::time::Duration;
|
||||
|
||||
use super::*;
|
||||
use actix_service::{apply, fn_factory, Service, ServiceFactory};
|
||||
use futures_util::future::{ok, FutureExt, LocalBoxFuture};
|
||||
use futures_core::future::LocalBoxFuture;
|
||||
|
||||
struct SleepService(Duration);
|
||||
|
||||
impl Service for SleepService {
|
||||
type Request = ();
|
||||
impl Service<()> for SleepService {
|
||||
type Response = ();
|
||||
type Error = ();
|
||||
type Future = LocalBoxFuture<'static, Result<(), ()>>;
|
||||
|
||||
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
actix_service::always_ready!();
|
||||
|
||||
fn call(&mut self, _: ()) -> Self::Future {
|
||||
actix_rt::time::delay_for(self.0)
|
||||
.then(|_| ok::<_, ()>(()))
|
||||
.boxed_local()
|
||||
let sleep = actix_rt::time::sleep(self.0);
|
||||
Box::pin(async move {
|
||||
sleep.await;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,7 +249,7 @@ mod tests {
|
||||
|
||||
let timeout = apply(
|
||||
Timeout::new(resolution),
|
||||
fn_factory(|| ok::<_, ()>(SleepService(wait_time))),
|
||||
fn_factory(|| async { Ok::<_, ()>(SleepService(wait_time)) }),
|
||||
);
|
||||
let mut srv = timeout.new_service(&()).await.unwrap();
|
||||
|
||||
|
@@ -1,3 +1,5 @@
|
||||
comment: false
|
||||
|
||||
coverage:
|
||||
status:
|
||||
project:
|
||||
|
@@ -42,7 +42,7 @@ macro_rules! parse_single_value {
|
||||
};
|
||||
}
|
||||
|
||||
pub struct PathDeserializer<'de, T: ResourcePath + 'de> {
|
||||
pub struct PathDeserializer<'de, T: ResourcePath> {
|
||||
path: &'de Path<T>,
|
||||
}
|
||||
|
||||
|
@@ -1,5 +1,9 @@
|
||||
//! Resource path matching library.
|
||||
|
||||
#![deny(rust_2018_idioms, nonstandard_style)]
|
||||
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
||||
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
||||
|
||||
mod de;
|
||||
mod path;
|
||||
mod resource;
|
||||
|
@@ -158,7 +158,7 @@ impl<T: ResourcePath> Path<T> {
|
||||
}
|
||||
|
||||
/// Return iterator to items in parameter container
|
||||
pub fn iter(&self) -> PathIter<T> {
|
||||
pub fn iter(&self) -> PathIter<'_, T> {
|
||||
PathIter {
|
||||
idx: 0,
|
||||
params: self,
|
||||
|
@@ -1,5 +1,9 @@
|
||||
//! A UTF-8 encoded read-only string using Bytes as storage.
|
||||
|
||||
#![deny(rust_2018_idioms, nonstandard_style)]
|
||||
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
||||
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
||||
|
||||
use std::convert::TryFrom;
|
||||
use std::{borrow, fmt, hash, ops, str};
|
||||
|
||||
@@ -7,7 +11,7 @@ use bytes::Bytes;
|
||||
|
||||
/// A UTF-8 encoded string with [`Bytes`] as a storage.
|
||||
///
|
||||
/// [`Bytes`]: https://docs.rs/bytes/0.5.3/bytes/struct.Bytes.html
|
||||
/// [`Bytes`]: bytes::Bytes
|
||||
#[derive(Clone, Eq, Ord, PartialOrd, Default)]
|
||||
pub struct ByteString(Bytes);
|
||||
|
||||
@@ -156,13 +160,13 @@ macro_rules! array_impls {
|
||||
array_impls!(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16);
|
||||
|
||||
impl fmt::Debug for ByteString {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(**self).fmt(fmt)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ByteString {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(**self).fmt(fmt)
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user