1
0
mirror of https://github.com/fafhrd91/actix-net synced 2025-08-13 01:37:05 +02:00

Compare commits

..

15 Commits

Author SHA1 Message Date
Rob Ede
d4c46b7da9 fix macros code 2021-01-09 14:58:15 +00:00
Rob Ede
b0a8f8411b prepare macros 0.2.0-beta.1 release 2021-01-09 14:56:07 +00:00
Rob Ede
46bfe5de36 prepare service 2.0.0-beta.3 release 2021-01-09 14:28:33 +00:00
Rob Ede
a95afe2800 prepare router release 0.2.6 2021-01-09 14:18:20 +00:00
Rob Ede
f751cf5acb use convert err on forward_ready! (#246) 2021-01-09 14:13:16 +00:00
fakeshadow
a1982bdbad add actix-rt::task (#245) 2021-01-03 18:16:57 +00:00
Rob Ede
147c4f4f2c test bytestring with ahash 2021-01-03 04:42:08 +00:00
Rob Ede
5285656bdc prepare next beta releases 2021-01-03 04:39:37 +00:00
Rob Ede
296294061f update readme 2020-12-31 02:52:55 +00:00
Rob Ede
93865de848 move router to actix-router 2020-12-31 02:29:27 +00:00
Rob Ede
6bcf6d8160 use bytestring crate name as dir name 2020-12-31 02:21:50 +00:00
Rob Ede
14ff379150 prepare bytestring release 1.0.0 (#243) 2020-12-31 02:20:49 +00:00
fakeshadow
647817ef14 tokio 1.0 and mio 0.7 (#204) 2020-12-30 22:11:50 +00:00
fakeshadow
b5eefb4d42 merge actix-testing into actix-server (#242) 2020-12-29 21:20:24 +00:00
fakeshadow
03eb96d6d4 fix actix-tls tests (#241) 2020-12-29 11:36:17 +00:00
61 changed files with 550 additions and 535 deletions

View File

@@ -1,29 +1,27 @@
[workspace] [workspace]
members = [ members = [
"actix-codec", "actix-codec",
"actix-rt",
"actix-macros", "actix-macros",
"actix-service", "actix-router",
"actix-rt",
"actix-server", "actix-server",
"actix-testing", "actix-service",
"actix-threadpool", "actix-threadpool",
"actix-tls", "actix-tls",
"actix-tracing", "actix-tracing",
"actix-utils", "actix-utils",
"router", "bytestring",
"string",
] ]
[patch.crates-io] [patch.crates-io]
actix-codec = { path = "actix-codec" } actix-codec = { path = "actix-codec" }
actix-rt = { path = "actix-rt" }
actix-macros = { path = "actix-macros" } actix-macros = { path = "actix-macros" }
actix-router = { path = "actix-router" }
actix-rt = { path = "actix-rt" }
actix-server = { path = "actix-server" } actix-server = { path = "actix-server" }
actix-service = { path = "actix-service" } actix-service = { path = "actix-service" }
actix-testing = { path = "actix-testing" }
actix-threadpool = { path = "actix-threadpool" } actix-threadpool = { path = "actix-threadpool" }
actix-tls = { path = "actix-tls" } actix-tls = { path = "actix-tls" }
actix-tracing = { path = "actix-tracing" } actix-tracing = { path = "actix-tracing" }
actix-utils = { path = "actix-utils" } actix-utils = { path = "actix-utils" }
actix-router = { path = "router" } bytestring = { path = "bytestring" }
bytestring = { path = "string" }

View File

@@ -1,9 +1,12 @@
# Actix net [![codecov](https://codecov.io/gh/actix/actix-net/branch/master/graph/badge.svg)](https://codecov.io/gh/actix/actix-net) [![Join the chat at https://gitter.im/actix/actix](https://badges.gitter.im/actix/actix.svg)](https://gitter.im/actix/actix?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) # Actix Net
Actix net - framework for composable network services > A collection of lower-level libraries for composable network services.
![Apache 2.0 or MIT licensed](https://img.shields.io/crates/l/actix-server)
[![codecov](https://codecov.io/gh/actix/actix-net/branch/master/graph/badge.svg)](https://codecov.io/gh/actix/actix-net)
[![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x)
## Build statuses ## Build statuses
| Platform | Build Status | | Platform | Build Status |
| ---------------- | ------------ | | ---------------- | ------------ |
| Linux | [![build status](https://github.com/actix/actix-net/workflows/CI%20%28Linux%29/badge.svg?branch=master&event=push)](https://github.com/actix/actix-net/actions?query=workflow%3A"CI+(Linux)") | | Linux | [![build status](https://github.com/actix/actix-net/workflows/CI%20%28Linux%29/badge.svg?branch=master&event=push)](https://github.com/actix/actix-net/actions?query=workflow%3A"CI+(Linux)") |
@@ -11,59 +14,13 @@ Actix net - framework for composable network services
| Windows | [![build status](https://github.com/actix/actix-net/workflows/CI%20%28Windows%29/badge.svg?branch=master&event=push)](https://github.com/actix/actix-net/actions?query=workflow%3A"CI+(Windows)") | | Windows | [![build status](https://github.com/actix/actix-net/workflows/CI%20%28Windows%29/badge.svg?branch=master&event=push)](https://github.com/actix/actix-net/actions?query=workflow%3A"CI+(Windows)") |
| Windows (MinGW) | [![build status](https://github.com/actix/actix-net/workflows/CI%20%28Windows-mingw%29/badge.svg?branch=master&event=push)](https://github.com/actix/actix-net/actions?query=workflow%3A"CI+(Windows-mingw)") | | Windows (MinGW) | [![build status](https://github.com/actix/actix-net/workflows/CI%20%28Windows-mingw%29/badge.svg?branch=master&event=push)](https://github.com/actix/actix-net/actions?query=workflow%3A"CI+(Windows-mingw)") |
## Documentation & community resources
* [Chat on Gitter](https://gitter.im/actix/actix)
* Minimum supported Rust version: 1.46 or later
## Example ## Example
See `actix-server/examples` and `actix-tls/examples` for some basic examples.
```rust ### MSRV
fn main() -> io::Result<()> { This repo's Minimum Supported Rust Version (MSRV) is 1.46.0.
// load ssl keys
let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap();
builder.set_private_key_file("./examples/key.pem", SslFiletype::PEM).unwrap();
builder.set_certificate_chain_file("./examples/cert.pem").unwrap();
let acceptor = builder.build();
let num = Arc::new(AtomicUsize::new(0));
// bind socket address and start workers. By default server uses number of
// available logical cpu as threads count. actix net start separate
// instances of service pipeline in each worker.
Server::build()
.bind(
// configure service pipeline
"basic", "0.0.0.0:8443",
move || {
let num = num.clone();
let acceptor = acceptor.clone();
// construct transformation pipeline
pipeline(
// service for converting incoming TcpStream to a SslStream<TcpStream>
fn_service(move |stream: actix_rt::net::TcpStream| async move {
SslAcceptorExt::accept_async(&acceptor, stream.into_parts().0).await
.map_err(|e| println!("Openssl error: {}", e))
}))
// .and_then() combinator chains result of previos service call to argument
/// for next service calll. in this case, on success we chain
/// ssl stream to the `logger` service.
.and_then(fn_service(logger))
// Next service counts number of connections
.and_then(move |_| {
let num = num.fetch_add(1, Ordering::Relaxed);
println!("got ssl connection {:?}", num);
future::ok(())
})
},
)?
.run()
}
```
## License ## License
This project is licensed under either of This project is licensed under either of
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0)) * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0))
@@ -73,6 +30,5 @@ at your option.
## Code of Conduct ## Code of Conduct
Contribution to the actix-net crate is organized under the terms of the Contribution to the actix-net repo is organized under the terms of the Contributor Covenant.
Contributor Covenant, the maintainer of actix-net, @fafhrd91, promises to The Actix team promises to intervene to uphold that code of conduct.
intervene to uphold that code of conduct.

View File

@@ -1,6 +1,6 @@
# Changes # Changes
## Unreleased - 2020-xx-xx ## Unreleased - 2021-xx-xx
## 0.4.0-beta.1 - 2020-12-28 ## 0.4.0-beta.1 - 2020-12-28

View File

@@ -1,13 +1,19 @@
# CHANGES # Changes
## 0.1.3 - 2020-12-3 ## Unreleased - 2021-xx-xx
## 0.2.0-beta.1 - 2021-01-09
* Remove `actix-reexport` feature.
## 0.1.3 - 2020-12-03
* Add `actix-reexport` feature. [#218]
[#218]: https://github.com/actix/actix-net/pull/218
* Add `actix-reexport` feature
## 0.1.2 - 2020-05-18 ## 0.1.2 - 2020-05-18
### Changed
* Forward actix_rt::test arguments to test function [#127] * Forward actix_rt::test arguments to test function [#127]
[#127]: https://github.com/actix/actix-net/pull/127 [#127]: https://github.com/actix/actix-net/pull/127

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "actix-macros" name = "actix-macros"
version = "0.1.3" version = "0.2.0-beta.1"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"] authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Actix runtime macros" description = "Actix runtime macros"
repository = "https://github.com/actix/actix-net" repository = "https://github.com/actix/actix-net"

View File

@@ -11,7 +11,7 @@ use quote::quote;
/// ///
/// ## Usage /// ## Usage
/// ///
/// ```rust /// ```
/// #[actix_rt::main] /// #[actix_rt::main]
/// async fn main() { /// async fn main() {
/// println!("Hello world"); /// println!("Hello world");
@@ -36,25 +36,14 @@ pub fn main(_: TokenStream, item: TokenStream) -> TokenStream {
sig.asyncness = None; sig.asyncness = None;
if cfg!(feature = "actix-reexport") { (quote! {
(quote! { #(#attrs)*
#(#attrs)* #vis #sig {
#vis #sig { actix_rt::System::new(stringify!(#name))
actix::System::new(stringify!(#name)) .block_on(async move { #body })
.block_on(async move { #body }) }
} })
}) .into()
.into()
} else {
(quote! {
#(#attrs)*
#vis #sig {
actix_rt::System::new(stringify!(#name))
.block_on(async move { #body })
}
})
.into()
}
} }
/// Marks async test function to be executed by actix runtime. /// Marks async test function to be executed by actix runtime.

View File

@@ -1,6 +1,12 @@
# Changes # Changes
## Unreleased - 2020-xx-xx ## Unreleased - 2021-xx-xx
## 0.2.6 - 2021-01-09
* Use `bytestring` version range compatible with Bytes v1.0. [#246]
[#246]: https://github.com/actix/actix-net/pull/246
## 0.2.5 - 2020-09-20 ## 0.2.5 - 2020-09-20

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "actix-router" name = "actix-router"
version = "0.2.5" version = "0.2.6"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"] authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Resource path matching library" description = "Resource path matching library"
keywords = ["actix"] keywords = ["actix"]
@@ -20,10 +20,10 @@ default = ["http"]
[dependencies] [dependencies]
regex = "1.3.1" regex = "1.3.1"
serde = "1.0.104" serde = "1.0.104"
bytestring = "0.1.2" bytestring = ">=0.1.5, <2"
log = "0.4.8" log = "0.4.8"
http = { version = "0.2.0", optional = true } http = { version = "0.2.2", optional = true }
[dev-dependencies] [dev-dependencies]
http = "0.2.0" http = "0.2.2"
serde_derive = "1.0" serde_derive = "1.0"

View File

@@ -1,7 +1,9 @@
# Changes # Changes
## Unreleased - 2020-xx-xx ## Unreleased - 2021-xx-xx
* Add `task` mod with re-export of `tokio::task::{spawn_blocking, yield_now, JoinHandle}` [#245]
[#245]: https://github.com/actix/actix-net/pull/245
## 2.0.0-beta.1 - 2020-12-28 ## 2.0.0-beta.1 - 2020-12-28
### Added ### Added

View File

@@ -16,6 +16,6 @@ name = "actix_rt"
path = "src/lib.rs" path = "src/lib.rs"
[dependencies] [dependencies]
actix-macros = "0.1.0" actix-macros = "0.2.0-beta.1"
tokio = { version = "1", features = ["rt", "net", "signal", "sync", "time"] } tokio = { version = "1", features = ["rt", "net", "parking_lot", "signal", "sync", "time"] }

View File

@@ -63,3 +63,8 @@ pub mod time {
pub use tokio::time::{sleep, sleep_until, Sleep}; pub use tokio::time::{sleep, sleep_until, Sleep};
pub use tokio::time::{timeout, Timeout}; pub use tokio::time::{timeout, Timeout};
} }
/// task management.
pub mod task {
pub use tokio::task::{spawn_blocking, yield_now, JoinHandle};
}

View File

@@ -41,7 +41,7 @@ impl Runtime {
/// ///
/// # Examples /// # Examples
/// ///
/// ```rust,ignore /// ```ignore
/// # use futures::{future, Future, Stream}; /// # use futures::{future, Future, Stream};
/// use actix_rt::Runtime; /// use actix_rt::Runtime;
/// ///

View File

@@ -70,7 +70,7 @@ impl System {
/// ///
/// # Examples /// # Examples
/// ///
/// ```rust,ignore /// ```ignore
/// use tokio::{runtime::Runtime, task::LocalSet}; /// use tokio::{runtime::Runtime, task::LocalSet};
/// use actix_rt::System; /// use actix_rt::System;
/// use futures_util::future::try_join_all; /// use futures_util::future::try_join_all;
@@ -139,7 +139,7 @@ impl System {
/// ///
/// # Examples /// # Examples
/// ///
/// ```rust,ignore /// ```ignore
/// use tokio::runtime::Runtime; /// use tokio::runtime::Runtime;
/// use actix_rt::System; /// use actix_rt::System;
/// use futures_util::future::try_join_all; /// use futures_util::future::try_join_all;

View File

@@ -1,6 +1,15 @@
# Changes # Changes
## Unreleased - 2020-xx-xx ## Unreleased - 2021-xx-xx
* Hidden `ServerBuilder::start` method has been removed. Use `ServerBuilder::run`. [#246]
[#246]: https://github.com/actix/actix-net/pull/246
## 2.0.0-beta.2 - 2021-01-03
* Merge `actix-testing` to `actix-server` as `test_server` mod. [#242]
[#242]: https://github.com/actix/actix-net/pull/242
## 2.0.0-beta.1 - 2020-12-28 ## 2.0.0-beta.1 - 2020-12-28

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "actix-server" name = "actix-server"
version = "2.0.0-beta.1" version = "2.0.0-beta.2"
authors = [ authors = [
"Nikolay Kim <fafhrd91@gmail.com>", "Nikolay Kim <fafhrd91@gmail.com>",
"fakeshadow <24548779@qq.com>", "fakeshadow <24548779@qq.com>",
@@ -25,7 +25,7 @@ default = []
[dependencies] [dependencies]
actix-codec = "0.4.0-beta.1" actix-codec = "0.4.0-beta.1"
actix-rt = "2.0.0-beta.1" actix-rt = "2.0.0-beta.1"
actix-service = "2.0.0-beta.1" actix-service = "2.0.0-beta.2"
actix-utils = "3.0.0-beta.1" actix-utils = "3.0.0-beta.1"
futures-core = { version = "0.3.7", default-features = false } futures-core = { version = "0.3.7", default-features = false }
@@ -36,7 +36,6 @@ slab = "0.4"
tokio = { version = "1", features = ["sync"] } tokio = { version = "1", features = ["sync"] }
[dev-dependencies] [dev-dependencies]
actix-testing = "2.0.0-beta.1"
bytes = "1" bytes = "1"
env_logger = "0.8" env_logger = "0.8"
futures-util = { version = "0.3.7", default-features = false, features = ["sink"] } futures-util = { version = "0.3.7", default-features = false, features = ["sink"] }

View File

@@ -252,11 +252,6 @@ impl ServerBuilder {
Ok(self) Ok(self)
} }
#[doc(hidden)]
pub fn start(self) -> Server {
self.run()
}
/// Starts processing incoming connections and return server controller. /// Starts processing incoming connections and return server controller.
pub fn run(mut self) -> Server { pub fn run(mut self) -> Server {
if self.sockets.is_empty() { if self.sockets.is_empty() {

View File

@@ -11,6 +11,7 @@ mod server;
mod service; mod service;
mod signals; mod signals;
mod socket; mod socket;
mod test_server;
mod waker_queue; mod waker_queue;
mod worker; mod worker;
@@ -18,6 +19,7 @@ pub use self::builder::ServerBuilder;
pub use self::config::{ServiceConfig, ServiceRuntime}; pub use self::config::{ServiceConfig, ServiceRuntime};
pub use self::server::Server; pub use self::server::Server;
pub use self::service::ServiceFactory; pub use self::service::ServiceFactory;
pub use self::test_server::TestServer;
#[doc(hidden)] #[doc(hidden)]
pub use self::socket::FromStream; pub use self::socket::FromStream;

View File

@@ -1,19 +1,9 @@
//! Various helpers for Actix applications to use during testing.
#![deny(rust_2018_idioms, nonstandard_style)]
#![allow(clippy::type_complexity, clippy::needless_doctest_main)]
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
use std::sync::mpsc; use std::sync::mpsc;
use std::{net, thread}; use std::{net, thread};
use actix_rt::{net::TcpStream, System}; use actix_rt::{net::TcpStream, System};
use actix_server::{Server, ServerBuilder, ServiceFactory};
use socket2::{Domain, Protocol, Socket, Type};
#[cfg(not(test))] // Work around for rust-lang/rust#62127 use crate::{Server, ServerBuilder, ServiceFactory};
pub use actix_macros::test;
/// The `TestServer` type. /// The `TestServer` type.
/// ///
@@ -22,9 +12,9 @@ pub use actix_macros::test;
/// ///
/// # Examples /// # Examples
/// ///
/// ```rust /// ```
/// use actix_service::fn_service; /// use actix_service::fn_service;
/// use actix_testing::TestServer; /// use actix_server::TestServer;
/// ///
/// #[actix_rt::main] /// #[actix_rt::main]
/// async fn main() { /// async fn main() {
@@ -59,10 +49,7 @@ impl TestServer {
// run server in separate thread // run server in separate thread
thread::spawn(move || { thread::spawn(move || {
let sys = System::new("actix-test-server"); let sys = System::new("actix-test-server");
factory(Server::build()) factory(Server::build()).workers(1).disable_signals().run();
.workers(1)
.disable_signals()
.start();
tx.send(System::current()).unwrap(); tx.send(System::current()).unwrap();
sys.run() sys.run()
@@ -93,7 +80,7 @@ impl TestServer {
.unwrap() .unwrap()
.workers(1) .workers(1)
.disable_signals() .disable_signals()
.start(); .run();
tx.send((System::current(), local_addr)).unwrap(); tx.send((System::current(), local_addr)).unwrap();
}); });
sys.run() sys.run()
@@ -115,11 +102,10 @@ impl TestServer {
/// Get first available unused local address /// Get first available unused local address
pub fn unused_addr() -> net::SocketAddr { pub fn unused_addr() -> net::SocketAddr {
let addr: net::SocketAddr = "127.0.0.1:0".parse().unwrap(); let addr: net::SocketAddr = "127.0.0.1:0".parse().unwrap();
let socket = let socket = mio::net::TcpSocket::new_v4().unwrap();
Socket::new(Domain::ipv4(), Type::stream(), Some(Protocol::tcp())).unwrap(); socket.bind(addr).unwrap();
socket.bind(&addr.into()).unwrap(); socket.set_reuseaddr(true).unwrap();
socket.set_reuse_address(true).unwrap(); let tcp = socket.listen(1024).unwrap();
let tcp = socket.into_tcp_listener();
tcp.local_addr().unwrap() tcp.local_addr().unwrap()
} }
} }

View File

@@ -28,7 +28,7 @@ fn test_bind() {
.disable_signals() .disable_signals()
.bind("test", addr, move || fn_service(|_| ok::<_, ()>(()))) .bind("test", addr, move || fn_service(|_| ok::<_, ()>(())))
.unwrap() .unwrap()
.start() .run()
})); }));
let _ = tx.send((srv, actix_rt::System::current())); let _ = tx.send((srv, actix_rt::System::current()));
let _ = sys.run(); let _ = sys.run();
@@ -55,7 +55,7 @@ fn test_listen() {
.workers(1) .workers(1)
.listen("test", lst, move || fn_service(|_| ok::<_, ()>(()))) .listen("test", lst, move || fn_service(|_| ok::<_, ()>(())))
.unwrap() .unwrap()
.start(); .run();
let _ = tx.send(actix_rt::System::current()); let _ = tx.send(actix_rt::System::current());
}); });
let _ = sys.run(); let _ = sys.run();
@@ -94,7 +94,7 @@ fn test_start() {
}) })
}) })
.unwrap() .unwrap()
.start() .run()
})); }));
let _ = tx.send((srv, actix_rt::System::current())); let _ = tx.send((srv, actix_rt::System::current()));
@@ -173,7 +173,7 @@ fn test_configure() {
}) })
.unwrap() .unwrap()
.workers(1) .workers(1)
.start() .run()
})); }));
let _ = tx.send((srv, actix_rt::System::current())); let _ = tx.send((srv, actix_rt::System::current()));
let _ = sys.run(); let _ = sys.run();

View File

@@ -1,6 +1,16 @@
# Changes # Changes
## Unreleased - 2020-xx-xx ## Unreleased - 2021-xx-xx
## 2.0.0-beta.3 - 2021-01-09
* The `forward_ready!` macro converts errors. [#246]
[#246]: https://github.com/actix/actix-net/pull/246
## 2.0.0-beta.2 - 2021-01-03
* Remove redundant type parameter from `map_config`.
## 2.0.0-beta.1 - 2020-12-28 ## 2.0.0-beta.1 - 2020-12-28

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "actix-service" name = "actix-service"
version = "2.0.0-beta.1" version = "2.0.0-beta.3"
authors = [ authors = [
"Nikolay Kim <fafhrd91@gmail.com>", "Nikolay Kim <fafhrd91@gmail.com>",
"Rob Ede <robjtede@icloud.com>", "Rob Ede <robjtede@icloud.com>",

View File

@@ -17,7 +17,7 @@ where
/// ///
/// # Example /// # Example
/// ///
/// ```rust /// ```
/// use std::io; /// use std::io;
/// use actix_service::{fn_factory, fn_service, Service, ServiceFactory}; /// use actix_service::{fn_factory, fn_service, Service, ServiceFactory};
/// use futures_util::future::ok; /// use futures_util::future::ok;
@@ -67,7 +67,7 @@ where
/// ///
/// # Example /// # Example
/// ///
/// ```rust /// ```
/// use std::io; /// use std::io;
/// use actix_service::{fn_factory_with_config, fn_service, Service, ServiceFactory}; /// use actix_service::{fn_factory_with_config, fn_service, Service, ServiceFactory};
/// use futures_util::future::ok; /// use futures_util::future::ok;

View File

@@ -48,7 +48,7 @@ use self::ready::{err, ok, ready, Ready};
/// replies. You can think about a service as a function with one argument that returns some result /// replies. You can think about a service as a function with one argument that returns some result
/// asynchronously. Conceptually, the operation looks like this: /// asynchronously. Conceptually, the operation looks like this:
/// ///
/// ```rust,ignore /// ```ignore
/// async fn(Request) -> Result<Response, Err> /// async fn(Request) -> Result<Response, Err>
/// ``` /// ```
/// ///
@@ -60,7 +60,7 @@ use self::ready::{err, ok, ready, Ready};
/// simple API surfaces. This leads to simpler design of each service, improves test-ability and /// simple API surfaces. This leads to simpler design of each service, improves test-ability and
/// makes composition easier. /// makes composition easier.
/// ///
/// ```rust,ignore /// ```ignore
/// struct MyService; /// struct MyService;
/// ///
/// impl Service for MyService { /// impl Service for MyService {
@@ -78,7 +78,7 @@ use self::ready::{err, ok, ready, Ready};
/// Sometimes it is not necessary to implement the Service trait. For example, the above service /// Sometimes it is not necessary to implement the Service trait. For example, the above service
/// could be rewritten as a simple function and passed to [fn_service](fn_service()). /// could be rewritten as a simple function and passed to [fn_service](fn_service()).
/// ///
/// ```rust,ignore /// ```ignore
/// async fn my_service(req: u8) -> Result<u64, MyError>; /// async fn my_service(req: u8) -> Result<u64, MyError>;
/// ``` /// ```
pub trait Service<Req> { pub trait Service<Req> {
@@ -327,7 +327,9 @@ macro_rules! forward_ready {
&mut self, &mut self,
cx: &mut ::core::task::Context<'_>, cx: &mut ::core::task::Context<'_>,
) -> ::core::task::Poll<Result<(), Self::Error>> { ) -> ::core::task::Poll<Result<(), Self::Error>> {
self.$field.poll_ready(cx) self.$field
.poll_ready(cx)
.map_err(::core::convert::Into::into)
} }
}; };
} }

View File

@@ -6,7 +6,7 @@ use super::{IntoServiceFactory, ServiceFactory};
/// ///
/// Note that this function consumes the receiving service factory and returns /// Note that this function consumes the receiving service factory and returns
/// a wrapped version of it. /// a wrapped version of it.
pub fn map_config<I, SF, S, Req, F, Cfg>(factory: I, f: F) -> MapConfig<SF, Req, F, Cfg> pub fn map_config<I, SF, Req, F, Cfg>(factory: I, f: F) -> MapConfig<SF, Req, F, Cfg>
where where
I: IntoServiceFactory<SF, Req>, I: IntoServiceFactory<SF, Req>,
SF: ServiceFactory<Req>, SF: ServiceFactory<Req>,

View File

@@ -30,7 +30,7 @@ where
/// ///
/// For example, timeout transform: /// For example, timeout transform:
/// ///
/// ```rust,ignore /// ```ignore
/// pub struct Timeout<S> { /// pub struct Timeout<S> {
/// service: S, /// service: S,
/// timeout: Duration, /// timeout: Duration,
@@ -45,9 +45,7 @@ where
/// type Error = TimeoutError<S::Error>; /// type Error = TimeoutError<S::Error>;
/// type Future = TimeoutServiceResponse<S>; /// type Future = TimeoutServiceResponse<S>;
/// ///
/// fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> { /// actix_service::forward_ready!(service);
/// ready!(self.service.poll_ready(cx)).map_err(TimeoutError::Service)
/// }
/// ///
/// fn call(&mut self, req: S::Request) -> Self::Future { /// fn call(&mut self, req: S::Request) -> Self::Future {
/// TimeoutServiceResponse { /// TimeoutServiceResponse {
@@ -69,7 +67,7 @@ where
/// ///
/// Factory for `Timeout` middleware from the above example could look like this: /// Factory for `Timeout` middleware from the above example could look like this:
/// ///
/// ```rust,,ignore /// ```ignore
/// pub struct TimeoutTransform { /// pub struct TimeoutTransform {
/// timeout: Duration, /// timeout: Duration,
/// } /// }

View File

@@ -1,37 +0,0 @@
# Changes
## Unreleased - 2021-xx-xx
## 2.0.0-beta.1 - 2020-12-28
* Update `actix-server` to v2.0.0-beta.1.
## 1.0.1 - 2020-05-19
* Replace deprecated `net2` crate with `socket2`
* Remove unused `futures` dependency
## 1.0.0 - 2019-12-11
* Update actix-server to 1.0.0
## 1.0.0-alpha.3 - 2019-12-07
* Migrate to tokio 0.2
## 1.0.0-alpha.2 - 2019-12-02
* Re-export `test` attribute macros
## 0.3.0-alpha.1 - 2019-11-22
* Migrate to std::future
## 0.2.0 - 2019-10-14
* Upgrade actix-server and actix-server-config deps
## 0.1.0 - 2019-09-25
* Initial impl

View File

@@ -1,26 +0,0 @@
[package]
name = "actix-testing"
version = "2.0.0-beta.1"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Various helpers for Actix applications to use during testing"
keywords = ["network", "framework", "async", "futures"]
homepage = "https://actix.rs"
repository = "https://github.com/actix/actix-net.git"
documentation = "https://docs.rs/actix-testing/"
categories = ["network-programming", "asynchronous"]
license = "MIT OR Apache-2.0"
edition = "2018"
readme = "README.md"
[lib]
name = "actix_testing"
path = "src/lib.rs"
[dependencies]
actix-rt = "2.0.0-beta.1"
actix-macros = "0.1.0"
actix-server = "2.0.0-beta.1"
actix-service = "2.0.0-beta.1"
log = "0.4"
socket2 = "0.3"

View File

@@ -1,9 +0,0 @@
# Actix test utilities [![crates.io](https://meritbadge.herokuapp.com/actix-testing)](https://crates.io/crates/actix-testint) [![Join the chat at https://gitter.im/actix/actix](https://badges.gitter.im/actix/actix.svg)](https://gitter.im/actix/actix?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
## Documentation & community resources
* [User Guide](https://actix.rs/docs/)
* [API Documentation](https://docs.rs/actix-testing/)
* [Chat on gitter](https://gitter.im/actix/actix)
* Cargo package: [actix-http-test](https://crates.io/crates/actix-testing)
* Minimum supported Rust version: 1.46 or later

View File

@@ -19,7 +19,7 @@ path = "src/lib.rs"
[dependencies] [dependencies]
derive_more = "0.99.2" derive_more = "0.99.2"
futures-channel = "0.3.1" futures-channel = "0.3.7"
parking_lot = "0.11" parking_lot = "0.11"
lazy_static = "1.3" lazy_static = "1.3"
log = "0.4" log = "0.4"

View File

@@ -3,6 +3,12 @@
## Unreleased - 2021-xx-xx ## Unreleased - 2021-xx-xx
## 3.0.0-beta.2 - 2021-xx-xx
* Depend on stable trust-dns packages. [#204]
[#204]: https://github.com/actix/actix-net/pull/204
## 3.0.0-beta.1 - 2020-12-29 ## 3.0.0-beta.1 - 2020-12-29
* Move acceptors under `accept` module. [#238] * Move acceptors under `accept` module. [#238]
* Merge `actix-connect` crate under `connect` module. [#238] * Merge `actix-connect` crate under `connect` module. [#238]

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "actix-tls" name = "actix-tls"
version = "3.0.0-beta.1" version = "3.0.0-beta.2"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"] authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "TLS acceptor and connector services for Actix ecosystem" description = "TLS acceptor and connector services for Actix ecosystem"
keywords = ["network", "tls", "ssl", "async", "transport"] keywords = ["network", "tls", "ssl", "async", "transport"]
@@ -12,7 +12,7 @@ license = "MIT OR Apache-2.0"
edition = "2018" edition = "2018"
[package.metadata.docs.rs] [package.metadata.docs.rs]
features = ["openssl", "rustls", "native-tls", "accept", "connect", "http"] features = ["openssl", "rustls", "native-tls", "accept", "connect", "uri"]
[lib] [lib]
name = "actix_tls" name = "actix_tls"
@@ -23,13 +23,13 @@ name = "basic"
required-features = ["accept", "rustls"] required-features = ["accept", "rustls"]
[features] [features]
default = ["accept", "connect", "http"] default = ["accept", "connect", "uri"]
# enable acceptor services # enable acceptor services
accept = [] accept = []
# enable connector services # enable connector services
connect = [] connect = ["trust-dns-proto/tokio-runtime", "trust-dns-resolver/tokio-runtime", "trust-dns-resolver/system-config"]
# use openssl impls # use openssl impls
openssl = ["tls-openssl", "tokio-openssl"] openssl = ["tls-openssl", "tokio-openssl"]
@@ -40,19 +40,24 @@ rustls = ["tls-rustls", "webpki", "webpki-roots", "tokio-rustls"]
# use native-tls impls # use native-tls impls
native-tls = ["tls-native-tls", "tokio-native-tls"] native-tls = ["tls-native-tls", "tokio-native-tls"]
# support http::Uri as connect address
uri = ["http"]
[dependencies] [dependencies]
actix-codec = "0.4.0-beta.1" actix-codec = "0.4.0-beta.1"
actix-rt = "2.0.0-beta.1" actix-rt = "2.0.0-beta.1"
actix-service = "2.0.0-beta.1" actix-service = "2.0.0-beta.2"
actix-utils = "3.0.0-beta.1" actix-utils = "3.0.0-beta.1"
derive_more = "0.99.5" derive_more = "0.99.5"
either = "1.6" either = "1.6"
futures-util = { version = "0.3.7", default-features = false } futures-util = { version = "0.3.7", default-features = false }
http = { version = "0.2.0", optional = true } http = { version = "0.2.2", optional = true }
log = "0.4" log = "0.4"
trust-dns-proto = { version = "0.19", default-features = false, features = ["tokio-runtime"] }
trust-dns-resolver = { version = "0.19", default-features = false, features = ["tokio-runtime", "system-config"] } # resolver
trust-dns-proto = { version = "0.20.0", default-features = false, optional = true }
trust-dns-resolver = { version = "0.20.0", default-features = false, optional = true }
# openssl # openssl
tls-openssl = { package = "openssl", version = "0.10", optional = true } tls-openssl = { package = "openssl", version = "0.10", optional = true }
@@ -71,8 +76,8 @@ tls-native-tls = { package = "native-tls", version = "0.2", optional = true }
tokio-native-tls = { version = "0.3", optional = true } tokio-native-tls = { version = "0.3", optional = true }
[dev-dependencies] [dev-dependencies]
actix-server = "2.0.0-beta.1" actix-server = "2.0.0-beta.2"
actix-testing = "2.0.0-beta.1"
bytes = "1" bytes = "1"
log = "0.4"
env_logger = "0.8" env_logger = "0.8"
futures-util = { version = "0.3.7", default-features = false, features = ["sink"] }
log = "0.4"

View File

@@ -1,10 +1,9 @@
use std::marker::PhantomData;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
use actix_codec::{AsyncRead, AsyncWrite}; use actix_codec::{AsyncRead, AsyncWrite};
use actix_service::{Service, ServiceFactory}; use actix_service::{Service, ServiceFactory};
use actix_utils::counter::Counter; use actix_utils::counter::Counter;
use futures_util::future::{self, FutureExt, LocalBoxFuture, TryFutureExt}; use futures_util::future::{ready, LocalBoxFuture, Ready};
pub use native_tls::Error; pub use native_tls::Error;
pub use tokio_native_tls::{TlsAcceptor, TlsStream}; pub use tokio_native_tls::{TlsAcceptor, TlsStream};
@@ -14,75 +13,64 @@ use super::MAX_CONN_COUNTER;
/// Accept TLS connections via `native-tls` package. /// Accept TLS connections via `native-tls` package.
/// ///
/// `native-tls` feature enables this `Acceptor` type. /// `native-tls` feature enables this `Acceptor` type.
pub struct Acceptor<T> { pub struct Acceptor {
acceptor: TlsAcceptor, acceptor: TlsAcceptor,
io: PhantomData<T>,
} }
impl<T> Acceptor<T> impl Acceptor {
where
T: AsyncRead + AsyncWrite + Unpin,
{
/// Create `native-tls` based `Acceptor` service factory. /// Create `native-tls` based `Acceptor` service factory.
#[inline] #[inline]
pub fn new(acceptor: TlsAcceptor) -> Self { pub fn new(acceptor: TlsAcceptor) -> Self {
Acceptor { Acceptor { acceptor }
acceptor,
io: PhantomData,
}
} }
} }
impl<T> Clone for Acceptor<T> { impl Clone for Acceptor {
#[inline] #[inline]
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self { Self {
acceptor: self.acceptor.clone(), acceptor: self.acceptor.clone(),
io: PhantomData,
} }
} }
} }
impl<T> ServiceFactory<T> for Acceptor<T> impl<T> ServiceFactory<T> for Acceptor
where where
T: AsyncRead + AsyncWrite + Unpin + 'static, T: AsyncRead + AsyncWrite + Unpin + 'static,
{ {
type Response = TlsStream<T>; type Response = TlsStream<T>;
type Error = Error; type Error = Error;
type Service = NativeTlsAcceptorService<T>;
type Config = (); type Config = ();
type Service = NativeTlsAcceptorService;
type InitError = (); type InitError = ();
type Future = future::Ready<Result<Self::Service, Self::InitError>>; type Future = Ready<Result<Self::Service, Self::InitError>>;
fn new_service(&self, _: ()) -> Self::Future { fn new_service(&self, _: ()) -> Self::Future {
MAX_CONN_COUNTER.with(|conns| { MAX_CONN_COUNTER.with(|conns| {
future::ok(NativeTlsAcceptorService { ready(Ok(NativeTlsAcceptorService {
acceptor: self.acceptor.clone(), acceptor: self.acceptor.clone(),
conns: conns.clone(), conns: conns.clone(),
io: PhantomData, }))
})
}) })
} }
} }
pub struct NativeTlsAcceptorService<T> { pub struct NativeTlsAcceptorService {
acceptor: TlsAcceptor, acceptor: TlsAcceptor,
io: PhantomData<T>,
conns: Counter, conns: Counter,
} }
impl<T> Clone for NativeTlsAcceptorService<T> { impl Clone for NativeTlsAcceptorService {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self { Self {
acceptor: self.acceptor.clone(), acceptor: self.acceptor.clone(),
io: PhantomData,
conns: self.conns.clone(), conns: self.conns.clone(),
} }
} }
} }
impl<T> Service<T> for NativeTlsAcceptorService<T> impl<T> Service<T> for NativeTlsAcceptorService
where where
T: AsyncRead + AsyncWrite + Unpin + 'static, T: AsyncRead + AsyncWrite + Unpin + 'static,
{ {
@@ -101,12 +89,10 @@ where
fn call(&mut self, io: T) -> Self::Future { fn call(&mut self, io: T) -> Self::Future {
let guard = self.conns.get(); let guard = self.conns.get();
let this = self.clone(); let this = self.clone();
async move { this.acceptor.accept(io).await } Box::pin(async move {
.map_ok(move |io| { let io = this.acceptor.accept(io).await;
// Required to preserve `CounterGuard` until `Self::Future` is completely resolved. drop(guard);
let _ = guard; io
io })
})
.boxed_local()
} }
} }

View File

@@ -1,5 +1,4 @@
use std::future::Future; use std::future::Future;
use std::marker::PhantomData;
use std::pin::Pin; use std::pin::Pin;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
@@ -7,7 +6,7 @@ use actix_codec::{AsyncRead, AsyncWrite};
use actix_service::{Service, ServiceFactory}; use actix_service::{Service, ServiceFactory};
use actix_utils::counter::{Counter, CounterGuard}; use actix_utils::counter::{Counter, CounterGuard};
use futures_util::{ use futures_util::{
future::{ok, Ready}, future::{ready, Ready},
ready, ready,
}; };
@@ -21,61 +20,54 @@ use super::MAX_CONN_COUNTER;
/// Accept TLS connections via `openssl` package. /// Accept TLS connections via `openssl` package.
/// ///
/// `openssl` feature enables this `Acceptor` type. /// `openssl` feature enables this `Acceptor` type.
pub struct Acceptor<T: AsyncRead + AsyncWrite> { pub struct Acceptor {
acceptor: SslAcceptor, acceptor: SslAcceptor,
io: PhantomData<T>,
} }
impl<T: AsyncRead + AsyncWrite> Acceptor<T> { impl Acceptor {
/// Create OpenSSL based `Acceptor` service factory. /// Create OpenSSL based `Acceptor` service factory.
#[inline] #[inline]
pub fn new(acceptor: SslAcceptor) -> Self { pub fn new(acceptor: SslAcceptor) -> Self {
Acceptor { Acceptor { acceptor }
acceptor,
io: PhantomData,
}
} }
} }
impl<T: AsyncRead + AsyncWrite> Clone for Acceptor<T> { impl Clone for Acceptor {
#[inline] #[inline]
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self { Self {
acceptor: self.acceptor.clone(), acceptor: self.acceptor.clone(),
io: PhantomData,
} }
} }
} }
impl<T> ServiceFactory<T> for Acceptor<T> impl<T> ServiceFactory<T> for Acceptor
where where
T: AsyncRead + AsyncWrite + Unpin + 'static, T: AsyncRead + AsyncWrite + Unpin + 'static,
{ {
type Response = SslStream<T>; type Response = SslStream<T>;
type Error = SslError; type Error = SslError;
type Config = (); type Config = ();
type Service = AcceptorService<T>; type Service = AcceptorService;
type InitError = (); type InitError = ();
type Future = Ready<Result<Self::Service, Self::InitError>>; type Future = Ready<Result<Self::Service, Self::InitError>>;
fn new_service(&self, _: ()) -> Self::Future { fn new_service(&self, _: ()) -> Self::Future {
MAX_CONN_COUNTER.with(|conns| { MAX_CONN_COUNTER.with(|conns| {
ok(AcceptorService { ready(Ok(AcceptorService {
acceptor: self.acceptor.clone(), acceptor: self.acceptor.clone(),
conns: conns.clone(), conns: conns.clone(),
io: PhantomData, }))
})
}) })
} }
} }
pub struct AcceptorService<T> { pub struct AcceptorService {
acceptor: SslAcceptor, acceptor: SslAcceptor,
conns: Counter, conns: Counter,
io: PhantomData<T>,
} }
impl<T> Service<T> for AcceptorService<T> impl<T> Service<T> for AcceptorService
where where
T: AsyncRead + AsyncWrite + Unpin + 'static, T: AsyncRead + AsyncWrite + Unpin + 'static,
{ {
@@ -92,10 +84,8 @@ where
} }
fn call(&mut self, io: T) -> Self::Future { fn call(&mut self, io: T) -> Self::Future {
let acc = self.acceptor.clone(); let ssl_ctx = self.acceptor.context();
let ssl_ctx = acc.into_context(); let ssl = Ssl::new(ssl_ctx).expect("Provided SSL acceptor was invalid.");
let ssl = Ssl::new(&ssl_ctx).expect("Provided SSL acceptor was invalid.");
AcceptorServiceResponse { AcceptorServiceResponse {
_guard: self.conns.get(), _guard: self.conns.get(),
stream: Some(SslStream::new(ssl, io).unwrap()), stream: Some(SslStream::new(ssl, io).unwrap()),

View File

@@ -1,6 +1,5 @@
use std::future::Future; use std::future::Future;
use std::io; use std::io;
use std::marker::PhantomData;
use std::pin::Pin; use std::pin::Pin;
use std::sync::Arc; use std::sync::Arc;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
@@ -8,7 +7,7 @@ use std::task::{Context, Poll};
use actix_codec::{AsyncRead, AsyncWrite}; use actix_codec::{AsyncRead, AsyncWrite};
use actix_service::{Service, ServiceFactory}; use actix_service::{Service, ServiceFactory};
use actix_utils::counter::{Counter, CounterGuard}; use actix_utils::counter::{Counter, CounterGuard};
use futures_util::future::{ok, Ready}; use futures_util::future::{ready, Ready};
use tokio_rustls::{Accept, TlsAcceptor}; use tokio_rustls::{Accept, TlsAcceptor};
pub use rustls::{ServerConfig, Session}; pub use rustls::{ServerConfig, Session};
@@ -20,66 +19,58 @@ use super::MAX_CONN_COUNTER;
/// Accept TLS connections via `rustls` package. /// Accept TLS connections via `rustls` package.
/// ///
/// `rustls` feature enables this `Acceptor` type. /// `rustls` feature enables this `Acceptor` type.
pub struct Acceptor<T> { pub struct Acceptor {
config: Arc<ServerConfig>, config: Arc<ServerConfig>,
io: PhantomData<T>,
} }
impl<T> Acceptor<T> impl Acceptor {
where
T: AsyncRead + AsyncWrite,
{
/// Create Rustls based `Acceptor` service factory. /// Create Rustls based `Acceptor` service factory.
#[inline] #[inline]
pub fn new(config: ServerConfig) -> Self { pub fn new(config: ServerConfig) -> Self {
Acceptor { Acceptor {
config: Arc::new(config), config: Arc::new(config),
io: PhantomData,
} }
} }
} }
impl<T> Clone for Acceptor<T> { impl Clone for Acceptor {
#[inline] #[inline]
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self { Self {
config: self.config.clone(), config: self.config.clone(),
io: PhantomData,
} }
} }
} }
impl<T> ServiceFactory<T> for Acceptor<T> impl<T> ServiceFactory<T> for Acceptor
where where
T: AsyncRead + AsyncWrite + Unpin, T: AsyncRead + AsyncWrite + Unpin,
{ {
type Response = TlsStream<T>; type Response = TlsStream<T>;
type Error = io::Error; type Error = io::Error;
type Service = AcceptorService<T>;
type Config = (); type Config = ();
type Service = AcceptorService;
type InitError = (); type InitError = ();
type Future = Ready<Result<Self::Service, Self::InitError>>; type Future = Ready<Result<Self::Service, Self::InitError>>;
fn new_service(&self, _: ()) -> Self::Future { fn new_service(&self, _: ()) -> Self::Future {
MAX_CONN_COUNTER.with(|conns| { MAX_CONN_COUNTER.with(|conns| {
ok(AcceptorService { ready(Ok(AcceptorService {
acceptor: self.config.clone().into(), acceptor: self.config.clone().into(),
conns: conns.clone(), conns: conns.clone(),
io: PhantomData, }))
})
}) })
} }
} }
/// Rustls based `Acceptor` service /// Rustls based `Acceptor` service
pub struct AcceptorService<T> { pub struct AcceptorService {
acceptor: TlsAcceptor, acceptor: TlsAcceptor,
io: PhantomData<T>,
conns: Counter, conns: Counter,
} }
impl<T> Service<T> for AcceptorService<T> impl<T> Service<T> for AcceptorService
where where
T: AsyncRead + AsyncWrite + Unpin, T: AsyncRead + AsyncWrite + Unpin,
{ {
@@ -119,11 +110,6 @@ where
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.get_mut(); let this = self.get_mut();
Pin::new(&mut this.fut).poll(cx)
let res = futures_util::ready!(Pin::new(&mut this.fut).poll(cx));
match res {
Ok(io) => Poll::Ready(Ok(io)),
Err(e) => Poll::Ready(Err(e)),
}
} }
} }

View File

@@ -8,7 +8,7 @@ use std::task::{Context, Poll};
use actix_rt::net::TcpStream; use actix_rt::net::TcpStream;
use actix_service::{Service, ServiceFactory}; use actix_service::{Service, ServiceFactory};
use futures_util::future::{err, ok, BoxFuture, Either, FutureExt, Ready}; use futures_util::future::{ready, Ready};
use log::{error, trace}; use log::{error, trace};
use super::connect::{Address, Connect, Connection}; use super::connect::{Address, Connect, Connection};
@@ -50,7 +50,7 @@ impl<T: Address> ServiceFactory<Connect<T>> for TcpConnectorFactory<T> {
type Future = Ready<Result<Self::Service, Self::InitError>>; type Future = Ready<Result<Self::Service, Self::InitError>>;
fn new_service(&self, _: ()) -> Self::Future { fn new_service(&self, _: ()) -> Self::Future {
ok(self.service()) ready(Ok(self.service()))
} }
} }
@@ -73,8 +73,7 @@ impl<T> Clone for TcpConnector<T> {
impl<T: Address> Service<Connect<T>> for TcpConnector<T> { impl<T: Address> Service<Connect<T>> for TcpConnector<T> {
type Response = Connection<T, TcpStream>; type Response = Connection<T, TcpStream>;
type Error = ConnectError; type Error = ConnectError;
#[allow(clippy::type_complexity)] type Future = TcpConnectorResponse<T>;
type Future = Either<TcpConnectorResponse<T>, Ready<Result<Self::Response, Self::Error>>>;
actix_service::always_ready!(); actix_service::always_ready!();
@@ -83,21 +82,26 @@ impl<T: Address> Service<Connect<T>> for TcpConnector<T> {
let Connect { req, addr, .. } = req; let Connect { req, addr, .. } = req;
if let Some(addr) = addr { if let Some(addr) = addr {
Either::Left(TcpConnectorResponse::new(req, port, addr)) TcpConnectorResponse::new(req, port, addr)
} else { } else {
error!("TCP connector: got unresolved address"); error!("TCP connector: got unresolved address");
Either::Right(err(ConnectError::Unresolved)) TcpConnectorResponse::Error(Some(ConnectError::Unresolved))
} }
} }
} }
type LocalBoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + 'a>>;
#[doc(hidden)] #[doc(hidden)]
/// TCP stream connector response future /// TCP stream connector response future
pub struct TcpConnectorResponse<T> { pub enum TcpConnectorResponse<T> {
req: Option<T>, Response {
port: u16, req: Option<T>,
addrs: Option<VecDeque<SocketAddr>>, port: u16,
stream: Option<BoxFuture<'static, Result<TcpStream, io::Error>>>, addrs: Option<VecDeque<SocketAddr>>,
stream: Option<LocalBoxFuture<'static, Result<TcpStream, io::Error>>>,
},
Error(Option<ConnectError>),
} }
impl<T: Address> TcpConnectorResponse<T> { impl<T: Address> TcpConnectorResponse<T> {
@@ -113,13 +117,13 @@ impl<T: Address> TcpConnectorResponse<T> {
); );
match addr { match addr {
either::Either::Left(addr) => TcpConnectorResponse { either::Either::Left(addr) => TcpConnectorResponse::Response {
req: Some(req), req: Some(req),
port, port,
addrs: None, addrs: None,
stream: Some(TcpStream::connect(addr).boxed()), stream: Some(Box::pin(TcpStream::connect(addr))),
}, },
either::Either::Right(addrs) => TcpConnectorResponse { either::Either::Right(addrs) => TcpConnectorResponse::Response {
req: Some(req), req: Some(req),
port, port,
addrs: Some(addrs), addrs: Some(addrs),
@@ -134,36 +138,43 @@ impl<T: Address> Future for TcpConnectorResponse<T> {
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.get_mut(); let this = self.get_mut();
match this {
// connect TcpConnectorResponse::Error(e) => Poll::Ready(Err(e.take().unwrap())),
loop { // connect
if let Some(new) = this.stream.as_mut() { TcpConnectorResponse::Response {
match new.as_mut().poll(cx) { req,
Poll::Ready(Ok(sock)) => { port,
let req = this.req.take().unwrap(); addrs,
trace!( stream,
"TCP connector - successfully connected to connecting to {:?} - {:?}", } => loop {
req.host(), sock.peer_addr() if let Some(new) = stream.as_mut() {
); match new.as_mut().poll(cx) {
return Poll::Ready(Ok(Connection::new(sock, req))); Poll::Ready(Ok(sock)) => {
} let req = req.take().unwrap();
Poll::Pending => return Poll::Pending, trace!(
Poll::Ready(Err(err)) => { "TCP connector - successfully connected to connecting to {:?} - {:?}",
trace!( req.host(), sock.peer_addr()
"TCP connector - failed to connect to connecting to {:?} port: {}", );
this.req.as_ref().unwrap().host(), return Poll::Ready(Ok(Connection::new(sock, req)));
this.port, }
); Poll::Pending => return Poll::Pending,
if this.addrs.is_none() || this.addrs.as_ref().unwrap().is_empty() { Poll::Ready(Err(err)) => {
return Poll::Ready(Err(err.into())); trace!(
"TCP connector - failed to connect to connecting to {:?} port: {}",
req.as_ref().unwrap().host(),
port,
);
if addrs.is_none() || addrs.as_ref().unwrap().is_empty() {
return Poll::Ready(Err(err.into()));
}
} }
} }
} }
}
// try to connect // try to connect
let addr = this.addrs.as_mut().unwrap().pop_front().unwrap(); let addr = addrs.as_mut().unwrap().pop_front().unwrap();
this.stream = Some(TcpStream::connect(addr).boxed()); *stream = Some(Box::pin(TcpStream::connect(addr)));
},
} }
} }
} }

View File

@@ -11,6 +11,7 @@ mod error;
mod resolve; mod resolve;
mod service; mod service;
pub mod ssl; pub mod ssl;
#[cfg(feature = "uri")]
mod uri; mod uri;
use actix_rt::{net::TcpStream, Arbiter}; use actix_rt::{net::TcpStream, Arbiter};
@@ -35,7 +36,7 @@ pub async fn start_resolver(
cfg: ResolverConfig, cfg: ResolverConfig,
opts: ResolverOpts, opts: ResolverOpts,
) -> Result<AsyncResolver, ConnectError> { ) -> Result<AsyncResolver, ConnectError> {
Ok(AsyncResolver::tokio(cfg, opts).await?) Ok(AsyncResolver::tokio(cfg, opts)?)
} }
struct DefaultResolver(AsyncResolver); struct DefaultResolver(AsyncResolver);
@@ -52,7 +53,7 @@ pub(crate) async fn get_default_resolver() -> Result<AsyncResolver, ConnectError
} }
}; };
let resolver = AsyncResolver::tokio(cfg, opts).await?; let resolver = AsyncResolver::tokio(cfg, opts)?;
Arbiter::set_item(DefaultResolver(resolver.clone())); Arbiter::set_item(DefaultResolver(resolver.clone()));
Ok(resolver) Ok(resolver)

View File

@@ -8,7 +8,7 @@ use actix_codec::{AsyncRead, AsyncWrite};
use actix_rt::net::TcpStream; use actix_rt::net::TcpStream;
use actix_service::{Service, ServiceFactory}; use actix_service::{Service, ServiceFactory};
use futures_util::{ use futures_util::{
future::{err, ok, Either, Ready}, future::{ready, Either, Ready},
ready, ready,
}; };
use log::trace; use log::trace;
@@ -21,43 +21,31 @@ use crate::connect::{
}; };
/// OpenSSL connector factory /// OpenSSL connector factory
pub struct OpensslConnector<T, U> { pub struct OpensslConnector {
connector: SslConnector, connector: SslConnector,
_t: PhantomData<(T, U)>,
} }
impl<T, U> OpensslConnector<T, U> { impl OpensslConnector {
pub fn new(connector: SslConnector) -> Self { pub fn new(connector: SslConnector) -> Self {
OpensslConnector { OpensslConnector { connector }
connector,
_t: PhantomData,
}
} }
} }
impl<T, U> OpensslConnector<T, U> impl OpensslConnector {
where pub fn service(connector: SslConnector) -> OpensslConnectorService {
T: Address + 'static, OpensslConnectorService { connector }
U: AsyncRead + AsyncWrite + Unpin + fmt::Debug + 'static,
{
pub fn service(connector: SslConnector) -> OpensslConnectorService<T, U> {
OpensslConnectorService {
connector,
_t: PhantomData,
}
} }
} }
impl<T, U> Clone for OpensslConnector<T, U> { impl Clone for OpensslConnector {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self { Self {
connector: self.connector.clone(), connector: self.connector.clone(),
_t: PhantomData,
} }
} }
} }
impl<T, U> ServiceFactory<Connection<T, U>> for OpensslConnector<T, U> impl<T, U> ServiceFactory<Connection<T, U>> for OpensslConnector
where where
T: Address + 'static, T: Address + 'static,
U: AsyncRead + AsyncWrite + Unpin + fmt::Debug + 'static, U: AsyncRead + AsyncWrite + Unpin + fmt::Debug + 'static,
@@ -65,33 +53,30 @@ where
type Response = Connection<T, SslStream<U>>; type Response = Connection<T, SslStream<U>>;
type Error = io::Error; type Error = io::Error;
type Config = (); type Config = ();
type Service = OpensslConnectorService<T, U>; type Service = OpensslConnectorService;
type InitError = (); type InitError = ();
type Future = Ready<Result<Self::Service, Self::InitError>>; type Future = Ready<Result<Self::Service, Self::InitError>>;
fn new_service(&self, _: ()) -> Self::Future { fn new_service(&self, _: ()) -> Self::Future {
ok(OpensslConnectorService { ready(Ok(OpensslConnectorService {
connector: self.connector.clone(), connector: self.connector.clone(),
_t: PhantomData, }))
})
} }
} }
pub struct OpensslConnectorService<T, U> { pub struct OpensslConnectorService {
connector: SslConnector, connector: SslConnector,
_t: PhantomData<(T, U)>,
} }
impl<T, U> Clone for OpensslConnectorService<T, U> { impl Clone for OpensslConnectorService {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self { Self {
connector: self.connector.clone(), connector: self.connector.clone(),
_t: PhantomData,
} }
} }
} }
impl<T, U> Service<Connection<T, U>> for OpensslConnectorService<T, U> impl<T, U> Service<Connection<T, U>> for OpensslConnectorService
where where
T: Address + 'static, T: Address + 'static,
U: AsyncRead + AsyncWrite + Unpin + fmt::Debug + 'static, U: AsyncRead + AsyncWrite + Unpin + fmt::Debug + 'static,
@@ -109,7 +94,7 @@ where
let host = stream.host().to_string(); let host = stream.host().to_string();
match self.connector.configure() { match self.connector.configure() {
Err(e) => Either::Right(err(io::Error::new(io::ErrorKind::Other, e))), Err(e) => Either::Right(ready(Err(io::Error::new(io::ErrorKind::Other, e)))),
Ok(config) => { Ok(config) => {
let ssl = config let ssl = config
.into_ssl(&host) .into_ssl(&host)
@@ -156,7 +141,7 @@ where
pub struct OpensslConnectServiceFactory<T> { pub struct OpensslConnectServiceFactory<T> {
tcp: ConnectServiceFactory<T>, tcp: ConnectServiceFactory<T>,
openssl: OpensslConnector<T, TcpStream>, openssl: OpensslConnector,
} }
impl<T> OpensslConnectServiceFactory<T> { impl<T> OpensslConnectServiceFactory<T> {
@@ -182,7 +167,6 @@ impl<T> OpensslConnectServiceFactory<T> {
tcp: self.tcp.service(), tcp: self.tcp.service(),
openssl: OpensslConnectorService { openssl: OpensslConnectorService {
connector: self.openssl.connector.clone(), connector: self.openssl.connector.clone(),
_t: PhantomData,
}, },
} }
} }
@@ -206,14 +190,14 @@ impl<T: Address + 'static> ServiceFactory<Connect<T>> for OpensslConnectServiceF
type Future = Ready<Result<Self::Service, Self::InitError>>; type Future = Ready<Result<Self::Service, Self::InitError>>;
fn new_service(&self, _: ()) -> Self::Future { fn new_service(&self, _: ()) -> Self::Future {
ok(self.service()) ready(Ok(self.service()))
} }
} }
#[derive(Clone)] #[derive(Clone)]
pub struct OpensslConnectService<T> { pub struct OpensslConnectService<T> {
tcp: ConnectService<T>, tcp: ConnectService<T>,
openssl: OpensslConnectorService<T, TcpStream>, openssl: OpensslConnectorService,
} }
impl<T: Address + 'static> Service<Connect<T>> for OpensslConnectService<T> { impl<T: Address + 'static> Service<Connect<T>> for OpensslConnectService<T> {
@@ -234,10 +218,8 @@ impl<T: Address + 'static> Service<Connect<T>> for OpensslConnectService<T> {
pub struct OpensslConnectServiceResponse<T: Address + 'static> { pub struct OpensslConnectServiceResponse<T: Address + 'static> {
fut1: Option<<ConnectService<T> as Service<Connect<T>>>::Future>, fut1: Option<<ConnectService<T> as Service<Connect<T>>>::Future>,
fut2: Option< fut2: Option<<OpensslConnectorService as Service<Connection<T, TcpStream>>>::Future>,
<OpensslConnectorService<T, TcpStream> as Service<Connection<T, TcpStream>>>::Future, openssl: OpensslConnectorService,
>,
openssl: OpensslConnectorService<T, TcpStream>,
} }
impl<T: Address> Future for OpensslConnectServiceResponse<T> { impl<T: Address> Future for OpensslConnectServiceResponse<T> {

View File

@@ -1,6 +1,5 @@
use std::fmt; use std::fmt;
use std::future::Future; use std::future::Future;
use std::marker::PhantomData;
use std::pin::Pin; use std::pin::Pin;
use std::sync::Arc; use std::sync::Arc;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
@@ -10,7 +9,10 @@ pub use tokio_rustls::{client::TlsStream, rustls::ClientConfig};
use actix_codec::{AsyncRead, AsyncWrite}; use actix_codec::{AsyncRead, AsyncWrite};
use actix_service::{Service, ServiceFactory}; use actix_service::{Service, ServiceFactory};
use futures_util::future::{ok, Ready}; use futures_util::{
future::{ready, Ready},
ready,
};
use log::trace; use log::trace;
use tokio_rustls::{Connect, TlsConnector}; use tokio_rustls::{Connect, TlsConnector};
use webpki::DNSNameRef; use webpki::DNSNameRef;
@@ -18,77 +20,63 @@ use webpki::DNSNameRef;
use crate::connect::{Address, Connection}; use crate::connect::{Address, Connection};
/// Rustls connector factory /// Rustls connector factory
pub struct RustlsConnector<T, U> { pub struct RustlsConnector {
connector: Arc<ClientConfig>, connector: Arc<ClientConfig>,
_t: PhantomData<(T, U)>,
} }
impl<T, U> RustlsConnector<T, U> { impl RustlsConnector {
pub fn new(connector: Arc<ClientConfig>) -> Self { pub fn new(connector: Arc<ClientConfig>) -> Self {
RustlsConnector { RustlsConnector { connector }
connector,
_t: PhantomData,
}
} }
} }
impl<T, U> RustlsConnector<T, U> impl RustlsConnector {
where pub fn service(connector: Arc<ClientConfig>) -> RustlsConnectorService {
T: Address, RustlsConnectorService { connector }
U: AsyncRead + AsyncWrite + Unpin + fmt::Debug,
{
pub fn service(connector: Arc<ClientConfig>) -> RustlsConnectorService<T, U> {
RustlsConnectorService {
connector,
_t: PhantomData,
}
} }
} }
impl<T, U> Clone for RustlsConnector<T, U> { impl Clone for RustlsConnector {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self { Self {
connector: self.connector.clone(), connector: self.connector.clone(),
_t: PhantomData,
} }
} }
} }
impl<T: Address, U> ServiceFactory<Connection<T, U>> for RustlsConnector<T, U> impl<T: Address, U> ServiceFactory<Connection<T, U>> for RustlsConnector
where where
U: AsyncRead + AsyncWrite + Unpin + fmt::Debug, U: AsyncRead + AsyncWrite + Unpin + fmt::Debug,
{ {
type Response = Connection<T, TlsStream<U>>; type Response = Connection<T, TlsStream<U>>;
type Error = std::io::Error; type Error = std::io::Error;
type Config = (); type Config = ();
type Service = RustlsConnectorService<T, U>; type Service = RustlsConnectorService;
type InitError = (); type InitError = ();
type Future = Ready<Result<Self::Service, Self::InitError>>; type Future = Ready<Result<Self::Service, Self::InitError>>;
fn new_service(&self, _: ()) -> Self::Future { fn new_service(&self, _: ()) -> Self::Future {
ok(RustlsConnectorService { ready(Ok(RustlsConnectorService {
connector: self.connector.clone(), connector: self.connector.clone(),
_t: PhantomData, }))
})
} }
} }
pub struct RustlsConnectorService<T, U> { pub struct RustlsConnectorService {
connector: Arc<ClientConfig>, connector: Arc<ClientConfig>,
_t: PhantomData<(T, U)>,
} }
impl<T, U> Clone for RustlsConnectorService<T, U> { impl Clone for RustlsConnectorService {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self { Self {
connector: self.connector.clone(), connector: self.connector.clone(),
_t: PhantomData,
} }
} }
} }
impl<T: Address, U> Service<Connection<T, U>> for RustlsConnectorService<T, U> impl<T, U> Service<Connection<T, U>> for RustlsConnectorService
where where
T: Address,
U: AsyncRead + AsyncWrite + Unpin + fmt::Debug, U: AsyncRead + AsyncWrite + Unpin + fmt::Debug,
{ {
type Response = Connection<T, TlsStream<U>>; type Response = Connection<T, TlsStream<U>>;
@@ -114,20 +102,18 @@ pub struct ConnectAsyncExt<T, U> {
stream: Option<Connection<T, ()>>, stream: Option<Connection<T, ()>>,
} }
impl<T: Address, U> Future for ConnectAsyncExt<T, U> impl<T, U> Future for ConnectAsyncExt<T, U>
where where
T: Address,
U: AsyncRead + AsyncWrite + Unpin + fmt::Debug, U: AsyncRead + AsyncWrite + Unpin + fmt::Debug,
{ {
type Output = Result<Connection<T, TlsStream<U>>, std::io::Error>; type Output = Result<Connection<T, TlsStream<U>>, std::io::Error>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.get_mut(); let this = self.get_mut();
Poll::Ready( let stream = ready!(Pin::new(&mut this.fut).poll(cx))?;
futures_util::ready!(Pin::new(&mut this.fut).poll(cx)).map(|stream| { let s = this.stream.take().unwrap();
let s = this.stream.take().unwrap(); trace!("SSL Handshake success: {:?}", s.host());
trace!("SSL Handshake success: {:?}", s.host()); Poll::Ready(Ok(s.replace(stream).1))
s.replace(stream).1
}),
)
} }
} }

View File

@@ -0,0 +1,130 @@
use std::io;
use actix_codec::{BytesCodec, Framed};
use actix_rt::net::TcpStream;
use actix_server::TestServer;
use actix_service::{fn_service, Service, ServiceFactory};
use bytes::Bytes;
use futures_util::sink::SinkExt;
use actix_tls::connect::{
self as actix_connect,
resolver::{ResolverConfig, ResolverOpts},
Connect,
};
#[cfg(all(feature = "connect", feature = "openssl"))]
#[actix_rt::test]
async fn test_string() {
let srv = TestServer::with(|| {
fn_service(|io: TcpStream| async {
let mut framed = Framed::new(io, BytesCodec);
framed.send(Bytes::from_static(b"test")).await?;
Ok::<_, io::Error>(())
})
});
let mut conn = actix_connect::default_connector();
let addr = format!("localhost:{}", srv.port());
let con = conn.call(addr.into()).await.unwrap();
assert_eq!(con.peer_addr().unwrap(), srv.addr());
}
#[cfg(feature = "rustls")]
#[actix_rt::test]
async fn test_rustls_string() {
let srv = TestServer::with(|| {
fn_service(|io: TcpStream| async {
let mut framed = Framed::new(io, BytesCodec);
framed.send(Bytes::from_static(b"test")).await?;
Ok::<_, io::Error>(())
})
});
let mut conn = actix_connect::default_connector();
let addr = format!("localhost:{}", srv.port());
let con = conn.call(addr.into()).await.unwrap();
assert_eq!(con.peer_addr().unwrap(), srv.addr());
}
#[actix_rt::test]
async fn test_static_str() {
let srv = TestServer::with(|| {
fn_service(|io: TcpStream| async {
let mut framed = Framed::new(io, BytesCodec);
framed.send(Bytes::from_static(b"test")).await?;
Ok::<_, io::Error>(())
})
});
let resolver = actix_connect::start_default_resolver().await.unwrap();
let mut conn = actix_connect::new_connector(resolver.clone());
let con = conn.call(Connect::with("10", srv.addr())).await.unwrap();
assert_eq!(con.peer_addr().unwrap(), srv.addr());
let connect = Connect::new(srv.host().to_owned());
let mut conn = actix_connect::new_connector(resolver);
let con = conn.call(connect).await;
assert!(con.is_err());
}
#[actix_rt::test]
async fn test_new_service() {
let srv = TestServer::with(|| {
fn_service(|io: TcpStream| async {
let mut framed = Framed::new(io, BytesCodec);
framed.send(Bytes::from_static(b"test")).await?;
Ok::<_, io::Error>(())
})
});
let resolver =
actix_connect::start_resolver(ResolverConfig::default(), ResolverOpts::default())
.await
.unwrap();
let factory = actix_connect::new_connector_factory(resolver);
let mut conn = factory.new_service(()).await.unwrap();
let con = conn.call(Connect::with("10", srv.addr())).await.unwrap();
assert_eq!(con.peer_addr().unwrap(), srv.addr());
}
#[cfg(all(feature = "openssl", feature = "uri"))]
#[actix_rt::test]
async fn test_openssl_uri() {
use std::convert::TryFrom;
let srv = TestServer::with(|| {
fn_service(|io: TcpStream| async {
let mut framed = Framed::new(io, BytesCodec);
framed.send(Bytes::from_static(b"test")).await?;
Ok::<_, io::Error>(())
})
});
let mut conn = actix_connect::default_connector();
let addr = http::Uri::try_from(format!("https://localhost:{}", srv.port())).unwrap();
let con = conn.call(addr.into()).await.unwrap();
assert_eq!(con.peer_addr().unwrap(), srv.addr());
}
#[cfg(all(feature = "rustls", feature = "uri"))]
#[actix_rt::test]
async fn test_rustls_uri() {
use std::convert::TryFrom;
let srv = TestServer::with(|| {
fn_service(|io: TcpStream| async {
let mut framed = Framed::new(io, BytesCodec);
framed.send(Bytes::from_static(b"test")).await?;
Ok::<_, io::Error>(())
})
});
let mut conn = actix_connect::default_connector();
let addr = http::Uri::try_from(format!("https://localhost:{}", srv.port())).unwrap();
let con = conn.call(addr.into()).await.unwrap();
assert_eq!(con.peer_addr().unwrap(), srv.addr());
}

View File

@@ -16,7 +16,7 @@ name = "actix_tracing"
path = "src/lib.rs" path = "src/lib.rs"
[dependencies] [dependencies]
actix-service = "2.0.0-beta.1" actix-service = "2.0.0-beta.2"
futures-util = { version = "0.3.4", default-features = false } futures-util = { version = "0.3.4", default-features = false }
tracing = "0.1" tracing = "0.1"

View File

@@ -94,7 +94,7 @@ where
/// is passed in a reference to the request being handled by the service. /// is passed in a reference to the request being handled by the service.
/// ///
/// For example: /// For example:
/// ```rust,ignore /// ```ignore
/// let traced_service = trace( /// let traced_service = trace(
/// web_service, /// web_service,
/// |req: &Request| Some(span!(Level::INFO, "request", req.id)) /// |req: &Request| Some(span!(Level::INFO, "request", req.id))

View File

@@ -1,6 +1,6 @@
# Changes # Changes
## Unreleased - 2020-xx-xx ## Unreleased - 2021-xx-xx
## 3.0.0-beta.1 - 2020-12-28 ## 3.0.0-beta.1 - 2020-12-28

View File

@@ -18,7 +18,7 @@ path = "src/lib.rs"
[dependencies] [dependencies]
actix-codec = "0.4.0-beta.1" actix-codec = "0.4.0-beta.1"
actix-rt = "2.0.0-beta.1" actix-rt = "2.0.0-beta.1"
actix-service = "2.0.0-beta.1" actix-service = "2.0.0-beta.2"
futures-core = { version = "0.3.7", default-features = false } futures-core = { version = "0.3.7", default-features = false }
futures-sink = { version = "0.3.7", default-features = false } futures-sink = { version = "0.3.7", default-features = false }

View File

@@ -149,9 +149,7 @@ where
type Error = TimeoutError<S::Error>; type Error = TimeoutError<S::Error>;
type Future = TimeoutServiceResponse<S, Req>; type Future = TimeoutServiceResponse<S, Req>;
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> { actix_service::forward_ready!(service);
self.service.poll_ready(cx).map_err(TimeoutError::Service)
}
fn call(&mut self, request: Req) -> Self::Future { fn call(&mut self, request: Req) -> Self::Future {
TimeoutServiceResponse { TimeoutServiceResponse {

36
bytestring/CHANGES.md Normal file
View File

@@ -0,0 +1,36 @@
# Changes
## Unreleased - 2021-xx-xx
## 1.0.0 - 2020-12-31
* Update `bytes` dependency to `1`.
* Add array and slice of `u8` impls of `TryFrom` up to 32 in length.
* Rename `get_ref` to `as_bytes` and rename `into_inner` to `into_bytes`.
* `ByteString::new` is now a `const fn`.
* Crate is now `#[no_std]` compatible.
## 0.1.5 - 2020-03-30
* Serde support
## 0.1.4 - 2020-01-14
* Fix `AsRef<str>` impl
## 0.1.3 - 2020-01-13
* Add `PartialEq<T: AsRef<str>>`, `AsRef<[u8]>` impls
## 0.1.2 - 2019-12-22
* Fix `new()` method
* Make `ByteString::from_static()` and `ByteString::from_bytes_unchecked()` methods const.
## 0.1.1 - 2019-12-07
* Fix hash impl
## 0.1.0 - 2019-12-07
* Initial release

27
bytestring/Cargo.toml Normal file
View File

@@ -0,0 +1,27 @@
[package]
name = "bytestring"
version = "1.0.0"
authors = [
"Nikolay Kim <fafhrd91@gmail.com>",
"Rob Ede <robjtede@icloud.com>",
]
description = "An immutable UTF-8 encoded string using Bytes as storage"
keywords = ["string", "bytes", "utf8", "web", "actix"]
categories = ["no-std", "web-programming"]
homepage = "https://actix.rs"
repository = "https://github.com/actix/actix-net.git"
documentation = "https://docs.rs/bytestring/"
license = "MIT OR Apache-2.0"
edition = "2018"
[lib]
name = "bytestring"
path = "src/lib.rs"
[dependencies]
bytes = "1"
serde = { version = "1.0", optional = true }
[dev-dependencies]
serde_json = "1.0"
ahash = { version = "0.6", default-features = false }

View File

@@ -1,37 +1,38 @@
//! A UTF-8 encoded read-only string using Bytes as storage. //! A UTF-8 encoded read-only string using Bytes as storage.
#![no_std]
#![deny(rust_2018_idioms, nonstandard_style)] #![deny(rust_2018_idioms, nonstandard_style)]
#![doc(html_logo_url = "https://actix.rs/img/logo.png")] #![doc(html_logo_url = "https://actix.rs/img/logo.png")]
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")] #![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
use std::convert::TryFrom; extern crate alloc;
use std::{borrow, fmt, hash, ops, str};
use alloc::{string::String, vec::Vec};
use core::{borrow, convert::TryFrom, fmt, hash, ops, str};
use bytes::Bytes; use bytes::Bytes;
/// A UTF-8 encoded string with [`Bytes`] as a storage. /// An immutable UTF-8 encoded string with [`Bytes`] as a storage.
/// #[derive(Clone, Default, Eq, PartialOrd, Ord)]
/// [`Bytes`]: bytes::Bytes
#[derive(Clone, Eq, Ord, PartialOrd, Default)]
pub struct ByteString(Bytes); pub struct ByteString(Bytes);
impl ByteString { impl ByteString {
/// Creates a new `ByteString`. /// Creates a new empty `ByteString`.
pub fn new() -> Self { pub const fn new() -> Self {
ByteString(Bytes::new()) ByteString(Bytes::new())
} }
/// Get a reference to the underlying bytes object. /// Get a reference to the underlying `Bytes` object.
pub fn get_ref(&self) -> &Bytes { pub fn as_bytes(&self) -> &Bytes {
&self.0 &self.0
} }
/// Unwraps this `ByteString`, returning the underlying bytes object. /// Unwraps this `ByteString` into the underlying `Bytes` object.
pub fn into_inner(self) -> Bytes { pub fn into_bytes(self) -> Bytes {
self.0 self.0
} }
/// Creates a new `ByteString` from a static str. /// Creates a new `ByteString` from a `&'static str`.
pub const fn from_static(src: &'static str) -> ByteString { pub const fn from_static(src: &'static str) -> ByteString {
Self(Bytes::from_static(src.as_bytes())) Self(Bytes::from_static(src.as_bytes()))
} }
@@ -39,11 +40,10 @@ impl ByteString {
/// Creates a new `ByteString` from a Bytes. /// Creates a new `ByteString` from a Bytes.
/// ///
/// # Safety /// # Safety
/// This function is unsafe because it does not check the bytes passed to it /// This function is unsafe because it does not check the bytes passed to it are valid UTF-8.
/// are valid UTF-8. If this constraint is violated, /// If this constraint is violated, it may cause memory unsafety issues with future users of
/// it may cause memory unsafety issues with future users of the `ByteString`, /// the `ByteString`, as we assume that `ByteString`s are valid UTF-8. However, the most likely
/// as we assume that `ByteString`s are valid UTF-8. /// issue is that the data gets corrupted.
/// However, the most likely issue is that the data gets corrupted.
pub const unsafe fn from_bytes_unchecked(src: Bytes) -> ByteString { pub const unsafe fn from_bytes_unchecked(src: Bytes) -> ByteString {
Self(src) Self(src)
} }
@@ -84,8 +84,10 @@ impl ops::Deref for ByteString {
#[inline] #[inline]
fn deref(&self) -> &str { fn deref(&self) -> &str {
let b = self.0.as_ref(); let bytes = self.0.as_ref();
unsafe { str::from_utf8_unchecked(b) } // SAFETY:
// UTF-8 validity is guaranteed at during construction.
unsafe { str::from_utf8_unchecked(bytes) }
} }
} }
@@ -96,21 +98,24 @@ impl borrow::Borrow<str> for ByteString {
} }
impl From<String> for ByteString { impl From<String> for ByteString {
#[inline]
fn from(value: String) -> Self { fn from(value: String) -> Self {
Self(Bytes::from(value)) Self(Bytes::from(value))
} }
} }
impl<'a> From<&'a str> for ByteString { impl From<&str> for ByteString {
fn from(value: &'a str) -> Self { #[inline]
fn from(value: &str) -> Self {
Self(Bytes::copy_from_slice(value.as_ref())) Self(Bytes::copy_from_slice(value.as_ref()))
} }
} }
impl<'a> TryFrom<&'a [u8]> for ByteString { impl TryFrom<&[u8]> for ByteString {
type Error = str::Utf8Error; type Error = str::Utf8Error;
fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> { #[inline]
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
let _ = str::from_utf8(value)?; let _ = str::from_utf8(value)?;
Ok(ByteString(Bytes::copy_from_slice(value))) Ok(ByteString(Bytes::copy_from_slice(value)))
} }
@@ -119,15 +124,17 @@ impl<'a> TryFrom<&'a [u8]> for ByteString {
impl TryFrom<Vec<u8>> for ByteString { impl TryFrom<Vec<u8>> for ByteString {
type Error = str::Utf8Error; type Error = str::Utf8Error;
#[inline]
fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> { fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
let _ = str::from_utf8(value.as_ref())?; let buf = String::from_utf8(value).map_err(|err| err.utf8_error())?;
Ok(ByteString(Bytes::from(value))) Ok(ByteString(Bytes::from(buf)))
} }
} }
impl TryFrom<Bytes> for ByteString { impl TryFrom<Bytes> for ByteString {
type Error = str::Utf8Error; type Error = str::Utf8Error;
#[inline]
fn try_from(value: Bytes) -> Result<Self, Self::Error> { fn try_from(value: Bytes) -> Result<Self, Self::Error> {
let _ = str::from_utf8(value.as_ref())?; let _ = str::from_utf8(value.as_ref())?;
Ok(ByteString(value)) Ok(ByteString(value))
@@ -137,8 +144,9 @@ impl TryFrom<Bytes> for ByteString {
impl TryFrom<bytes::BytesMut> for ByteString { impl TryFrom<bytes::BytesMut> for ByteString {
type Error = str::Utf8Error; type Error = str::Utf8Error;
#[inline]
fn try_from(value: bytes::BytesMut) -> Result<Self, Self::Error> { fn try_from(value: bytes::BytesMut) -> Result<Self, Self::Error> {
let _ = str::from_utf8(value.as_ref())?; let _ = str::from_utf8(&value)?;
Ok(ByteString(value.freeze())) Ok(ByteString(value.freeze()))
} }
} }
@@ -146,10 +154,20 @@ impl TryFrom<bytes::BytesMut> for ByteString {
macro_rules! array_impls { macro_rules! array_impls {
($($len:expr)+) => { ($($len:expr)+) => {
$( $(
impl<'a> TryFrom<&'a [u8; $len]> for ByteString { impl TryFrom<[u8; $len]> for ByteString {
type Error = str::Utf8Error; type Error = str::Utf8Error;
fn try_from(value: &'a [u8; $len]) -> Result<Self, Self::Error> { #[inline]
fn try_from(value: [u8; $len]) -> Result<Self, Self::Error> {
ByteString::try_from(&value[..])
}
}
impl TryFrom<&[u8; $len]> for ByteString {
type Error = str::Utf8Error;
#[inline]
fn try_from(value: &[u8; $len]) -> Result<Self, Self::Error> {
ByteString::try_from(&value[..]) ByteString::try_from(&value[..])
} }
} }
@@ -157,7 +175,7 @@ macro_rules! array_impls {
} }
} }
array_impls!(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16); array_impls!(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32);
impl fmt::Debug for ByteString { impl fmt::Debug for ByteString {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
@@ -173,6 +191,8 @@ impl fmt::Display for ByteString {
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
mod serde { mod serde {
use alloc::string::String;
use serde::de::{Deserialize, Deserializer}; use serde::de::{Deserialize, Deserializer};
use serde::ser::{Serialize, Serializer}; use serde::ser::{Serialize, Serializer};
@@ -201,16 +221,19 @@ mod serde {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use alloc::borrow::ToOwned;
use core::hash::{Hash, Hasher};
use ahash::AHasher;
use super::*; use super::*;
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
#[test] #[test]
fn test_partial_eq() { fn test_partial_eq() {
let s: ByteString = ByteString::from_static("test"); let s: ByteString = ByteString::from_static("test");
assert_eq!(s, "test"); assert_eq!(s, "test");
assert_eq!(s, *"test"); assert_eq!(s, *"test");
assert_eq!(s, "test".to_string()); assert_eq!(s, "test".to_owned());
} }
#[test] #[test]
@@ -220,10 +243,10 @@ mod test {
#[test] #[test]
fn test_hash() { fn test_hash() {
let mut hasher1 = DefaultHasher::default(); let mut hasher1 = AHasher::default();
"str".hash(&mut hasher1); "str".hash(&mut hasher1);
let mut hasher2 = DefaultHasher::default(); let mut hasher2 = AHasher::default();
let s = ByteString::from_static("str"); let s = ByteString::from_static("str");
s.hash(&mut hasher2); s.hash(&mut hasher2);
assert_eq!(hasher1.finish(), hasher2.finish()); assert_eq!(hasher1.finish(), hasher2.finish());
@@ -231,7 +254,7 @@ mod test {
#[test] #[test]
fn test_from_string() { fn test_from_string() {
let s: ByteString = "hello".to_string().into(); let s: ByteString = "hello".to_owned().into();
assert_eq!(&s, "hello"); assert_eq!(&s, "hello");
let t: &str = s.as_ref(); let t: &str = s.as_ref();
assert_eq!(t, "hello"); assert_eq!(t, "hello");
@@ -249,17 +272,25 @@ mod test {
} }
#[test] #[test]
fn test_try_from_rbytes() { fn test_try_from_slice() {
let _ = ByteString::try_from(b"nice bytes").unwrap(); let _ = ByteString::try_from(b"nice bytes").unwrap();
} }
#[test]
fn test_try_from_array() {
assert_eq!(
ByteString::try_from([b'h', b'i']).unwrap(),
ByteString::from_static("hi")
);
}
#[test] #[test]
fn test_try_from_bytes() { fn test_try_from_bytes() {
let _ = ByteString::try_from(Bytes::from_static(b"nice bytes")).unwrap(); let _ = ByteString::try_from(Bytes::from_static(b"nice bytes")).unwrap();
} }
#[test] #[test]
fn test_try_from_bytesmut() { fn test_try_from_bytes_mut() {
let _ = ByteString::try_from(bytes::BytesMut::from(&b"nice bytes"[..])).unwrap(); let _ = ByteString::try_from(bytes::BytesMut::from(&b"nice bytes"[..])).unwrap();
} }

View File

@@ -1,27 +0,0 @@
# Changes
## [0.1.5] - 2020-03-30
* Serde support
## [0.1.4] - 2020-01-14
* Fix `AsRef<str>` impl
## [0.1.3] - 2020-01-13
* Add `PartialEq<T: AsRef<str>>`, `AsRef<[u8]>` impls
## [0.1.2] - 2019-12-22
* Fix `new()` method
* Make `ByteString::from_static()` and `ByteString::from_bytes_unchecked()` methods const.
## [0.1.1] - 2019-12-07
* Fix hash impl
## [0.1.0] - 2019-12-07
* Initial release

View File

@@ -1,22 +0,0 @@
[package]
name = "bytestring"
version = "0.1.5"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "A UTF-8 encoded string with Bytes as a storage"
keywords = ["actix"]
homepage = "https://actix.rs"
repository = "https://github.com/actix/actix-net.git"
documentation = "https://docs.rs/bytestring/"
license = "MIT OR Apache-2.0"
edition = "2018"
[lib]
name = "bytestring"
path = "src/lib.rs"
[dependencies]
bytes = "0.5.3"
serde = { version = "1.0", optional = true }
[dev-dependencies]
serde_json = "1.0"

View File

@@ -1 +0,0 @@
../LICENSE-APACHE

View File

@@ -1 +0,0 @@
../LICENSE-MIT