mirror of
https://github.com/fafhrd91/actix-net
synced 2025-08-14 11:10:32 +02:00
Compare commits
9 Commits
server-v2.
...
bytestring
Author | SHA1 | Date | |
---|---|---|---|
|
93865de848 | ||
|
6bcf6d8160 | ||
|
14ff379150 | ||
|
647817ef14 | ||
|
b5eefb4d42 | ||
|
03eb96d6d4 | ||
|
0934078947 | ||
|
5759c9e144 | ||
|
3c6de3a81b |
24
Cargo.toml
24
Cargo.toml
@@ -1,31 +1,27 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = [
|
||||||
"actix-codec",
|
"actix-codec",
|
||||||
"actix-connect",
|
|
||||||
"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 = { git = "https://github.com/actix/actix-net.git", rev = "ba44ea7d0bafaf5fccb9a34003d503e1910943ee" }
|
actix-codec = { path = "actix-codec" }
|
||||||
actix-connect = { path = "actix-connect" }
|
|
||||||
actix-rt = { git = "https://github.com/actix/actix-net.git", rev = "ba44ea7d0bafaf5fccb9a34003d503e1910943ee" }
|
|
||||||
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 = { git = "https://github.com/actix/actix-net.git", rev = "ba44ea7d0bafaf5fccb9a34003d503e1910943ee" }
|
actix-service = { path = "actix-service" }
|
||||||
actix-testing = { path = "actix-testing" }
|
|
||||||
actix-threadpool = { path = "actix-threadpool" }
|
actix-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 = { git = "https://github.com/actix/actix-net.git", rev = "ba44ea7d0bafaf5fccb9a34003d503e1910943ee" }
|
actix-utils = { path = "actix-utils" }
|
||||||
actix-router = { path = "router" }
|
bytestring = { path = "bytestring" }
|
||||||
bytestring = { path = "string" }
|
|
||||||
|
@@ -1,154 +0,0 @@
|
|||||||
# Changes
|
|
||||||
|
|
||||||
## Unreleased - 2020-xx-xx
|
|
||||||
|
|
||||||
|
|
||||||
## 2.0.0 - 2020-09-02
|
|
||||||
- No significant changes from `2.0.0-alpha.4`.
|
|
||||||
|
|
||||||
## 2.0.0-alpha.4 - 2020-08-17
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
* Update `rustls` dependency to 0.18
|
|
||||||
* Update `tokio-rustls` dependency to 0.14
|
|
||||||
|
|
||||||
|
|
||||||
## [2.0.0-alpha.3] - 2020-05-08
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
* Corrected spelling of `ConnectError::Unresolverd` to `ConnectError::Unresolved`
|
|
||||||
|
|
||||||
## [2.0.0-alpha.2] - 2020-03-08
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
* Update `trust-dns-proto` dependency to 0.19. [#116]
|
|
||||||
* Update `trust-dns-resolver` dependency to 0.19. [#116]
|
|
||||||
* `Address` trait is now required to have static lifetime. [#116]
|
|
||||||
* `start_resolver` and `start_default_resolver` are now `async` and may return a `ConnectError`. [#116]
|
|
||||||
|
|
||||||
[#116]: https://github.com/actix/actix-net/pull/116
|
|
||||||
|
|
||||||
## [2.0.0-alpha.1] - 2020-03-03
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
* Update `rustls` dependency to 0.17
|
|
||||||
* Update `tokio-rustls` dependency to 0.13
|
|
||||||
|
|
||||||
## [1.0.2] - 2020-01-15
|
|
||||||
|
|
||||||
* Fix actix-service 1.0.3 compatibility
|
|
||||||
|
|
||||||
## [1.0.1] - 2019-12-15
|
|
||||||
|
|
||||||
* Fix trust-dns-resolver compilation
|
|
||||||
|
|
||||||
## [1.0.0] - 2019-12-11
|
|
||||||
|
|
||||||
* Release
|
|
||||||
|
|
||||||
## [1.0.0-alpha.3] - 2019-12-07
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
* Migrate to tokio 0.2
|
|
||||||
|
|
||||||
|
|
||||||
## [1.0.0-alpha.2] - 2019-12-02
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
* Migrated to `std::future`
|
|
||||||
|
|
||||||
|
|
||||||
## [0.3.0] - 2019-10-03
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
* Update `rustls` to 0.16
|
|
||||||
* Minimum required Rust version upped to 1.37.0
|
|
||||||
|
|
||||||
## [0.2.5] - 2019-09-05
|
|
||||||
|
|
||||||
* Add `TcpConnectService`
|
|
||||||
|
|
||||||
## [0.2.4] - 2019-09-02
|
|
||||||
|
|
||||||
* Use arbiter's storage for default async resolver
|
|
||||||
|
|
||||||
## [0.2.3] - 2019-08-05
|
|
||||||
|
|
||||||
* Add `ConnectService` and `OpensslConnectService`
|
|
||||||
|
|
||||||
## [0.2.2] - 2019-07-24
|
|
||||||
|
|
||||||
* Add `rustls` support
|
|
||||||
|
|
||||||
## [0.2.1] - 2019-07-17
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
* Expose Connect addrs #30
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
* Update `derive_more` to 0.15
|
|
||||||
|
|
||||||
|
|
||||||
## [0.2.0] - 2019-05-12
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
* Upgrade to actix-service 0.4
|
|
||||||
|
|
||||||
|
|
||||||
## [0.1.5] - 2019-04-19
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
* `Connect::set_addr()`
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
* Use trust-dns-resolver 0.11.0
|
|
||||||
|
|
||||||
|
|
||||||
## [0.1.4] - 2019-04-12
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
* Do not start default resolver immediately for default connector.
|
|
||||||
|
|
||||||
|
|
||||||
## [0.1.3] - 2019-04-11
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
* Start trust-dns default resolver on first use
|
|
||||||
|
|
||||||
## [0.1.2] - 2019-04-04
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
* Log error if dns system config could not be loaded.
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
* Rename connect Connector to TcpConnector #10
|
|
||||||
|
|
||||||
|
|
||||||
## [0.1.1] - 2019-03-15
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
* Fix error handling for single address
|
|
||||||
|
|
||||||
|
|
||||||
## [0.1.0] - 2019-03-14
|
|
||||||
|
|
||||||
* Refactor resolver and connector services
|
|
||||||
|
|
||||||
* Rename crate
|
|
@@ -1,58 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "actix-connect"
|
|
||||||
version = "2.0.0"
|
|
||||||
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
|
||||||
description = "TCP connector service for Actix ecosystem."
|
|
||||||
keywords = ["network", "framework", "async", "futures"]
|
|
||||||
homepage = "https://actix.rs"
|
|
||||||
repository = "https://github.com/actix/actix-net.git"
|
|
||||||
documentation = "https://docs.rs/actix-connect/"
|
|
||||||
categories = ["network-programming", "asynchronous"]
|
|
||||||
license = "MIT OR Apache-2.0"
|
|
||||||
edition = "2018"
|
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
|
||||||
features = ["openssl", "rustls", "uri"]
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
name = "actix_connect"
|
|
||||||
path = "src/lib.rs"
|
|
||||||
|
|
||||||
[features]
|
|
||||||
default = ["uri"]
|
|
||||||
|
|
||||||
# openssl
|
|
||||||
openssl = ["open-ssl", "tokio-openssl"]
|
|
||||||
|
|
||||||
# rustls
|
|
||||||
rustls = ["rust-tls", "tokio-rustls", "webpki"]
|
|
||||||
|
|
||||||
# support http::Uri as connect address
|
|
||||||
uri = ["http"]
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
actix-service = "1.0.6"
|
|
||||||
actix-codec = "0.3.0"
|
|
||||||
actix-utils = "2.0.0"
|
|
||||||
actix-rt = "1.1.1"
|
|
||||||
|
|
||||||
derive_more = "0.99.2"
|
|
||||||
either = "1.5.3"
|
|
||||||
futures-util = { version = "0.3.4", default-features = false }
|
|
||||||
http = { version = "0.2.0", optional = true }
|
|
||||||
log = "0.4"
|
|
||||||
trust-dns-proto = { version = "0.19", default-features = false, features = ["tokio-runtime"] }
|
|
||||||
trust-dns-resolver = { version = "0.19", default-features = false, features = ["tokio-runtime", "system-config"] }
|
|
||||||
|
|
||||||
# openssl
|
|
||||||
open-ssl = { package = "openssl", version = "0.10", optional = true }
|
|
||||||
tokio-openssl = { version = "0.4.0", optional = true }
|
|
||||||
|
|
||||||
# rustls
|
|
||||||
rust-tls = { package = "rustls", version = "0.18.0", optional = true }
|
|
||||||
tokio-rustls = { version = "0.14.0", optional = true }
|
|
||||||
webpki = { version = "0.21", optional = true }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
bytes = "0.5.3"
|
|
||||||
actix-testing = "1.0.0"
|
|
@@ -22,8 +22,8 @@ regex = "1.3.1"
|
|||||||
serde = "1.0.104"
|
serde = "1.0.104"
|
||||||
bytestring = "0.1.2"
|
bytestring = "0.1.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"
|
@@ -1,7 +1,7 @@
|
|||||||
# Changes
|
# Changes
|
||||||
|
|
||||||
## Unreleased - 2020-xx-xx
|
## Unreleased - 2020-xx-xx
|
||||||
|
* Merge `actix-testing` to `actix-server` as `test_server` mod.
|
||||||
|
|
||||||
## 2.0.0-beta.1 - 2020-12-28
|
## 2.0.0-beta.1 - 2020-12-28
|
||||||
* Added explicit info log message on accept queue pause. [#215]
|
* Added explicit info log message on accept queue pause. [#215]
|
||||||
|
@@ -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"] }
|
||||||
|
@@ -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;
|
||||||
|
@@ -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.
|
||||||
///
|
///
|
||||||
@@ -24,7 +14,7 @@ pub use actix_macros::test;
|
|||||||
///
|
///
|
||||||
/// ```rust
|
/// ```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() {
|
||||||
@@ -115,11 +105,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()
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -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>,
|
||||||
|
@@ -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
|
|
@@ -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"
|
|
@@ -1,9 +0,0 @@
|
|||||||
# Actix test utilities [](https://crates.io/crates/actix-testint) [](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
|
|
@@ -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"
|
||||||
|
@@ -1,6 +1,14 @@
|
|||||||
# Changes
|
# Changes
|
||||||
|
|
||||||
## Unreleased - 2020-xx-xx
|
## Unreleased - 2021-xx-xx
|
||||||
|
|
||||||
|
|
||||||
|
## 3.0.0-beta.1 - 2020-12-29
|
||||||
|
* Move acceptors under `accept` module. [#238]
|
||||||
|
* Merge `actix-connect` crate under `connect` module. [#238]
|
||||||
|
* Add feature flags to enable acceptors and/or connectors individually. [#238]
|
||||||
|
|
||||||
|
[#238]: https://github.com/actix/actix-net/pull/238
|
||||||
|
|
||||||
|
|
||||||
## 2.0.0 - 2020-09-03
|
## 2.0.0 - 2020-09-03
|
||||||
|
@@ -1,9 +1,9 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "actix-tls"
|
name = "actix-tls"
|
||||||
version = "2.0.0"
|
version = "3.0.0-beta.1"
|
||||||
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
||||||
description = "TLS acceptor services for Actix ecosystem."
|
description = "TLS acceptor and connector services for Actix ecosystem"
|
||||||
keywords = ["network", "framework", "async", "tls", "ssl"]
|
keywords = ["network", "tls", "ssl", "async", "transport"]
|
||||||
homepage = "https://actix.rs"
|
homepage = "https://actix.rs"
|
||||||
repository = "https://github.com/actix/actix-net.git"
|
repository = "https://github.com/actix/actix-net.git"
|
||||||
documentation = "https://docs.rs/actix-tls/"
|
documentation = "https://docs.rs/actix-tls/"
|
||||||
@@ -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", "nativetls"]
|
features = ["openssl", "rustls", "native-tls", "accept", "connect", "uri"]
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "actix_tls"
|
name = "actix_tls"
|
||||||
@@ -20,45 +20,64 @@ path = "src/lib.rs"
|
|||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "basic"
|
name = "basic"
|
||||||
required-features = ["rustls"]
|
required-features = ["accept", "rustls"]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = ["accept", "connect", "uri"]
|
||||||
|
|
||||||
# openssl
|
# enable acceptor services
|
||||||
openssl = ["open-ssl", "tokio-openssl"]
|
accept = []
|
||||||
|
|
||||||
# rustls
|
# enable connector services
|
||||||
rustls = ["rust-tls", "webpki", "webpki-roots", "tokio-rustls"]
|
connect = ["trust-dns-proto/tokio-runtime", "trust-dns-resolver/tokio-runtime", "trust-dns-resolver/system-config"]
|
||||||
|
|
||||||
# nativetls
|
# use openssl impls
|
||||||
nativetls = ["native-tls", "tokio-tls"]
|
openssl = ["tls-openssl", "tokio-openssl"]
|
||||||
|
|
||||||
|
# use rustls impls
|
||||||
|
rustls = ["tls-rustls", "webpki", "webpki-roots", "tokio-rustls"]
|
||||||
|
|
||||||
|
# use native-tls impls
|
||||||
|
native-tls = ["tls-native-tls", "tokio-native-tls"]
|
||||||
|
|
||||||
|
# support http::Uri as connect address
|
||||||
|
uri = ["http"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix-service = "1.0.0"
|
actix-codec = "0.4.0-beta.1"
|
||||||
actix-codec = "0.3.0"
|
actix-rt = "2.0.0-beta.1"
|
||||||
actix-utils = "2.0.0"
|
actix-service = "2.0.0-beta.1"
|
||||||
|
actix-utils = "3.0.0-beta.1"
|
||||||
|
|
||||||
futures-util = { version = "0.3.4", default-features = false }
|
derive_more = "0.99.5"
|
||||||
|
either = "1.6"
|
||||||
|
futures-util = { version = "0.3.7", default-features = false }
|
||||||
|
http = { version = "0.2.2", optional = true }
|
||||||
|
log = "0.4"
|
||||||
|
|
||||||
|
# resolver
|
||||||
|
trust-dns-proto = { version = "0.20.0", default-features = false, optional = true }
|
||||||
|
trust-dns-resolver = { version = "0.20.0", default-features = false, optional = true }
|
||||||
|
|
||||||
# openssl
|
# openssl
|
||||||
open-ssl = { package = "openssl", version = "0.10", optional = true }
|
tls-openssl = { package = "openssl", version = "0.10", optional = true }
|
||||||
tokio-openssl = { version = "0.4.0", optional = true }
|
tokio-openssl = { version = "0.6", optional = true }
|
||||||
|
|
||||||
|
# TODO: Reduce dependencies where tokio wrappers re-export base crate.
|
||||||
|
|
||||||
# rustls
|
# rustls
|
||||||
rust-tls = { package = "rustls", version = "0.18.0", optional = true }
|
tls-rustls = { package = "rustls", version = "0.19", optional = true }
|
||||||
|
tokio-rustls = { version = "0.22", optional = true }
|
||||||
webpki = { version = "0.21", optional = true }
|
webpki = { version = "0.21", optional = true }
|
||||||
webpki-roots = { version = "0.20", optional = true }
|
webpki-roots = { version = "0.21", optional = true }
|
||||||
tokio-rustls = { version = "0.14.0", optional = true }
|
|
||||||
|
|
||||||
# native-tls
|
# native-tls
|
||||||
native-tls = { version = "0.2", optional = true }
|
tls-native-tls = { package = "native-tls", version = "0.2", optional = true }
|
||||||
tokio-tls = { version = "0.3", optional = true }
|
tokio-native-tls = { version = "0.3", optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
bytes = "0.5"
|
|
||||||
log = "0.4"
|
|
||||||
env_logger = "0.7"
|
|
||||||
actix-testing = "2.0.0-beta.1"
|
|
||||||
actix-server = "2.0.0-beta.1"
|
actix-server = "2.0.0-beta.1"
|
||||||
actix-rt = "1"
|
bytes = "1"
|
||||||
|
env_logger = "0.8"
|
||||||
|
futures-util = { version = "0.3.7", default-features = false, features = ["sink"] }
|
||||||
|
log = "0.4"
|
||||||
|
@@ -15,6 +15,10 @@
|
|||||||
//! http --verify=false https://127.0.0.1:8443
|
//! http --verify=false https://127.0.0.1:8443
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
|
// this rename only exists because of how we have organised the crate's feature flags
|
||||||
|
// it is not necessary for your actual code
|
||||||
|
extern crate tls_rustls as rustls;
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
env,
|
env,
|
||||||
fs::File,
|
fs::File,
|
||||||
@@ -27,10 +31,10 @@ use std::{
|
|||||||
|
|
||||||
use actix_server::Server;
|
use actix_server::Server;
|
||||||
use actix_service::pipeline_factory;
|
use actix_service::pipeline_factory;
|
||||||
use actix_tls::rustls::Acceptor as RustlsAcceptor;
|
use actix_tls::accept::rustls::Acceptor as RustlsAcceptor;
|
||||||
use futures_util::future::ok;
|
use futures_util::future::ok;
|
||||||
use log::info;
|
use log::info;
|
||||||
use rust_tls::{
|
use rustls::{
|
||||||
internal::pemfile::certs, internal::pemfile::rsa_private_keys, NoClientAuth, ServerConfig,
|
internal::pemfile::certs, internal::pemfile::rsa_private_keys, NoClientAuth, ServerConfig,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
42
actix-tls/src/accept/mod.rs
Normal file
42
actix-tls/src/accept/mod.rs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
//! TLS acceptor services for Actix ecosystem.
|
||||||
|
//!
|
||||||
|
//! ## Crate Features
|
||||||
|
//! * `openssl` - TLS acceptor using the `openssl` crate.
|
||||||
|
//! * `rustls` - TLS acceptor using the `rustls` crate.
|
||||||
|
//! * `native-tls` - TLS acceptor using the `native-tls` crate.
|
||||||
|
|
||||||
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
|
|
||||||
|
use actix_utils::counter::Counter;
|
||||||
|
|
||||||
|
#[cfg(feature = "openssl")]
|
||||||
|
pub mod openssl;
|
||||||
|
|
||||||
|
#[cfg(feature = "rustls")]
|
||||||
|
pub mod rustls;
|
||||||
|
|
||||||
|
#[cfg(feature = "native-tls")]
|
||||||
|
pub mod nativetls;
|
||||||
|
|
||||||
|
pub(crate) static MAX_CONN: AtomicUsize = AtomicUsize::new(256);
|
||||||
|
|
||||||
|
thread_local! {
|
||||||
|
static MAX_CONN_COUNTER: Counter = Counter::new(MAX_CONN.load(Ordering::Relaxed));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the maximum per-worker concurrent TLS connection limit.
|
||||||
|
///
|
||||||
|
/// All listeners will stop accepting connections when this limit is reached.
|
||||||
|
/// It can be used to regulate the global TLS CPU usage.
|
||||||
|
///
|
||||||
|
/// By default, the connection limit is 256.
|
||||||
|
pub fn max_concurrent_tls_connect(num: usize) {
|
||||||
|
MAX_CONN.store(num, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// TLS error combined with service error.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum TlsError<E1, E2> {
|
||||||
|
Tls(E1),
|
||||||
|
Service(E2),
|
||||||
|
}
|
@@ -1,93 +1,79 @@
|
|||||||
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_tls::{TlsAcceptor, TlsStream};
|
pub use tokio_native_tls::{TlsAcceptor, TlsStream};
|
||||||
|
|
||||||
use crate::MAX_CONN_COUNTER;
|
use super::MAX_CONN_COUNTER;
|
||||||
|
|
||||||
/// Accept TLS connections via `native-tls` package.
|
/// Accept TLS connections via `native-tls` package.
|
||||||
///
|
///
|
||||||
/// `nativetls` 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 for Acceptor<T>
|
impl<T> ServiceFactory<T> for Acceptor
|
||||||
where
|
where
|
||||||
T: AsyncRead + AsyncWrite + Unpin + 'static,
|
T: AsyncRead + AsyncWrite + Unpin + 'static,
|
||||||
{
|
{
|
||||||
type Request = T;
|
|
||||||
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 for NativeTlsAcceptorService<T>
|
impl<T> Service<T> for NativeTlsAcceptorService
|
||||||
where
|
where
|
||||||
T: AsyncRead + AsyncWrite + Unpin + 'static,
|
T: AsyncRead + AsyncWrite + Unpin + 'static,
|
||||||
{
|
{
|
||||||
type Request = T;
|
|
||||||
type Response = TlsStream<T>;
|
type Response = TlsStream<T>;
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Future = LocalBoxFuture<'static, Result<TlsStream<T>, Error>>;
|
type Future = LocalBoxFuture<'static, Result<TlsStream<T>, Error>>;
|
||||||
@@ -100,15 +86,13 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call(&mut self, req: Self::Request) -> 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(req).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()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,77 +1,78 @@
|
|||||||
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};
|
||||||
|
|
||||||
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, FutureExt, LocalBoxFuture, Ready};
|
use futures_util::{
|
||||||
|
future::{ready, Ready},
|
||||||
|
ready,
|
||||||
|
};
|
||||||
|
|
||||||
pub use open_ssl::ssl::{AlpnError, SslAcceptor, SslAcceptorBuilder};
|
pub use openssl::ssl::{
|
||||||
pub use tokio_openssl::{HandshakeError, SslStream};
|
AlpnError, Error as SslError, HandshakeError, Ssl, SslAcceptor, SslAcceptorBuilder,
|
||||||
|
};
|
||||||
|
pub use tokio_openssl::SslStream;
|
||||||
|
|
||||||
use crate::MAX_CONN_COUNTER;
|
use super::MAX_CONN_COUNTER;
|
||||||
|
|
||||||
/// Accept TLS connections via `openssl` package.
|
/// 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: AsyncRead + AsyncWrite + Unpin + 'static> ServiceFactory for Acceptor<T> {
|
impl<T> ServiceFactory<T> for Acceptor
|
||||||
type Request = T;
|
where
|
||||||
|
T: AsyncRead + AsyncWrite + Unpin + 'static,
|
||||||
|
{
|
||||||
type Response = SslStream<T>;
|
type Response = SslStream<T>;
|
||||||
type Error = HandshakeError<T>;
|
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: AsyncRead + AsyncWrite + Unpin + 'static> Service for AcceptorService<T> {
|
impl<T> Service<T> for AcceptorService
|
||||||
type Request = T;
|
where
|
||||||
|
T: AsyncRead + AsyncWrite + Unpin + 'static,
|
||||||
|
{
|
||||||
type Response = SslStream<T>;
|
type Response = SslStream<T>;
|
||||||
type Error = HandshakeError<T>;
|
type Error = SslError;
|
||||||
type Future = AcceptorServiceResponse<T>;
|
type Future = AcceptorServiceResponse<T>;
|
||||||
|
|
||||||
fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||||
@@ -82,15 +83,12 @@ impl<T: AsyncRead + AsyncWrite + Unpin + 'static> Service for AcceptorService<T>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call(&mut self, req: Self::Request) -> Self::Future {
|
fn call(&mut self, io: T) -> Self::Future {
|
||||||
let acc = self.acceptor.clone();
|
let ssl_ctx = self.acceptor.context();
|
||||||
|
let ssl = Ssl::new(ssl_ctx).expect("Provided SSL acceptor was invalid.");
|
||||||
AcceptorServiceResponse {
|
AcceptorServiceResponse {
|
||||||
_guard: self.conns.get(),
|
_guard: self.conns.get(),
|
||||||
fut: async move {
|
stream: Some(SslStream::new(ssl, io).unwrap()),
|
||||||
let acc = acc;
|
|
||||||
tokio_openssl::accept(&acc, req).await
|
|
||||||
}
|
|
||||||
.boxed_local(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -99,15 +97,15 @@ pub struct AcceptorServiceResponse<T>
|
|||||||
where
|
where
|
||||||
T: AsyncRead + AsyncWrite,
|
T: AsyncRead + AsyncWrite,
|
||||||
{
|
{
|
||||||
fut: LocalBoxFuture<'static, Result<SslStream<T>, HandshakeError<T>>>,
|
stream: Option<SslStream<T>>,
|
||||||
_guard: CounterGuard,
|
_guard: CounterGuard,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: AsyncRead + AsyncWrite + Unpin> Future for AcceptorServiceResponse<T> {
|
impl<T: AsyncRead + AsyncWrite + Unpin> Future for AcceptorServiceResponse<T> {
|
||||||
type Output = Result<SslStream<T>, HandshakeError<T>>;
|
type Output = Result<SslStream<T>, SslError>;
|
||||||
|
|
||||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
let io = futures_util::ready!(Pin::new(&mut self.fut).poll(cx))?;
|
ready!(Pin::new(self.stream.as_mut().unwrap()).poll_accept(cx))?;
|
||||||
Poll::Ready(Ok(io))
|
Poll::Ready(Ok(self.stream.take().expect("SSL connect has resolved.")))
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -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,74 +7,73 @@ 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 rust_tls::{ServerConfig, Session};
|
pub use rustls::{ServerConfig, Session};
|
||||||
pub use tokio_rustls::server::TlsStream;
|
pub use tokio_rustls::server::TlsStream;
|
||||||
pub use webpki_roots::TLS_SERVER_ROOTS;
|
pub use webpki_roots::TLS_SERVER_ROOTS;
|
||||||
|
|
||||||
use crate::MAX_CONN_COUNTER;
|
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: AsyncRead + AsyncWrite> Acceptor<T> {
|
impl Acceptor {
|
||||||
/// 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: AsyncRead + AsyncWrite + Unpin> ServiceFactory for Acceptor<T> {
|
impl<T> ServiceFactory<T> for Acceptor
|
||||||
type Request = T;
|
where
|
||||||
|
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: AsyncRead + AsyncWrite + Unpin> Service for AcceptorService<T> {
|
impl<T> Service<T> for AcceptorService
|
||||||
type Request = T;
|
where
|
||||||
|
T: AsyncRead + AsyncWrite + Unpin,
|
||||||
|
{
|
||||||
type Response = TlsStream<T>;
|
type Response = TlsStream<T>;
|
||||||
type Error = io::Error;
|
type Error = io::Error;
|
||||||
type Future = AcceptorServiceFut<T>;
|
type Future = AcceptorServiceFut<T>;
|
||||||
@@ -88,7 +86,7 @@ impl<T: AsyncRead + AsyncWrite + Unpin> Service for AcceptorService<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call(&mut self, req: Self::Request) -> Self::Future {
|
fn call(&mut self, req: T) -> Self::Future {
|
||||||
AcceptorServiceFut {
|
AcceptorServiceFut {
|
||||||
_guard: self.conns.get(),
|
_guard: self.conns.get(),
|
||||||
fut: self.acceptor.accept(req),
|
fut: self.acceptor.accept(req),
|
||||||
@@ -104,16 +102,14 @@ where
|
|||||||
_guard: CounterGuard,
|
_guard: CounterGuard,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: AsyncRead + AsyncWrite + Unpin> Future for AcceptorServiceFut<T> {
|
impl<T> Future for AcceptorServiceFut<T>
|
||||||
|
where
|
||||||
|
T: AsyncRead + AsyncWrite + Unpin,
|
||||||
|
{
|
||||||
type Output = Result<TlsStream<T>, io::Error>;
|
type Output = Result<TlsStream<T>, 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();
|
||||||
|
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)),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -8,7 +8,8 @@ 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 super::connect::{Address, Connect, Connection};
|
use super::connect::{Address, Connect, Connection};
|
||||||
use super::error::ConnectError;
|
use super::error::ConnectError;
|
||||||
@@ -49,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()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,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!();
|
||||||
|
|
||||||
@@ -82,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> {
|
||||||
@@ -112,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),
|
||||||
@@ -133,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)));
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -5,21 +5,12 @@
|
|||||||
//! * `openssl` - enables TLS support via `openssl` crate
|
//! * `openssl` - enables TLS support via `openssl` crate
|
||||||
//! * `rustls` - enables TLS support via `rustls` crate
|
//! * `rustls` - enables TLS support via `rustls` crate
|
||||||
|
|
||||||
#![deny(rust_2018_idioms, nonstandard_style)]
|
|
||||||
#![recursion_limit = "128"]
|
|
||||||
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
|
||||||
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
extern crate log;
|
|
||||||
|
|
||||||
mod connect;
|
mod connect;
|
||||||
mod connector;
|
mod connector;
|
||||||
mod error;
|
mod error;
|
||||||
mod resolve;
|
mod resolve;
|
||||||
mod service;
|
mod service;
|
||||||
pub mod ssl;
|
pub mod ssl;
|
||||||
|
|
||||||
#[cfg(feature = "uri")]
|
#[cfg(feature = "uri")]
|
||||||
mod uri;
|
mod uri;
|
||||||
|
|
||||||
@@ -45,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);
|
||||||
@@ -62,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)
|
@@ -6,12 +6,13 @@ use std::task::{Context, Poll};
|
|||||||
|
|
||||||
use actix_service::{Service, ServiceFactory};
|
use actix_service::{Service, ServiceFactory};
|
||||||
use futures_util::future::{ok, Either, Ready};
|
use futures_util::future::{ok, Either, Ready};
|
||||||
|
use log::trace;
|
||||||
use trust_dns_resolver::TokioAsyncResolver as AsyncResolver;
|
use trust_dns_resolver::TokioAsyncResolver as AsyncResolver;
|
||||||
use trust_dns_resolver::{error::ResolveError, lookup_ip::LookupIp};
|
use trust_dns_resolver::{error::ResolveError, lookup_ip::LookupIp};
|
||||||
|
|
||||||
use crate::connect::{Address, Connect};
|
use super::connect::{Address, Connect};
|
||||||
use crate::error::ConnectError;
|
use super::error::ConnectError;
|
||||||
use crate::get_default_resolver;
|
use super::get_default_resolver;
|
||||||
|
|
||||||
/// DNS Resolver Service factory
|
/// DNS Resolver Service factory
|
||||||
pub struct ResolverFactory<T> {
|
pub struct ResolverFactory<T> {
|
@@ -8,10 +8,10 @@ use either::Either;
|
|||||||
use futures_util::future::{ok, Ready};
|
use futures_util::future::{ok, Ready};
|
||||||
use trust_dns_resolver::TokioAsyncResolver as AsyncResolver;
|
use trust_dns_resolver::TokioAsyncResolver as AsyncResolver;
|
||||||
|
|
||||||
use crate::connect::{Address, Connect, Connection};
|
use super::connect::{Address, Connect, Connection};
|
||||||
use crate::connector::{TcpConnector, TcpConnectorFactory};
|
use super::connector::{TcpConnector, TcpConnectorFactory};
|
||||||
use crate::error::ConnectError;
|
use super::error::ConnectError;
|
||||||
use crate::resolve::{Resolver, ResolverFactory};
|
use super::resolve::{Resolver, ResolverFactory};
|
||||||
|
|
||||||
pub struct ConnectServiceFactory<T> {
|
pub struct ConnectServiceFactory<T> {
|
||||||
tcp: TcpConnectorFactory<T>,
|
tcp: TcpConnectorFactory<T>,
|
@@ -4,97 +4,83 @@ use std::pin::Pin;
|
|||||||
use std::task::{Context, Poll};
|
use std::task::{Context, Poll};
|
||||||
use std::{fmt, io};
|
use std::{fmt, io};
|
||||||
|
|
||||||
pub use open_ssl::ssl::{Error as SslError, SslConnector, SslMethod};
|
|
||||||
pub use tokio_openssl::{HandshakeError, SslStream};
|
|
||||||
|
|
||||||
use actix_codec::{AsyncRead, AsyncWrite};
|
use actix_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::future::{err, ok, Either, FutureExt, LocalBoxFuture, Ready};
|
use futures_util::{
|
||||||
|
future::{ready, Either, Ready},
|
||||||
|
ready,
|
||||||
|
};
|
||||||
|
use log::trace;
|
||||||
|
pub use openssl::ssl::{Error as SslError, HandshakeError, SslConnector, SslMethod};
|
||||||
|
pub use tokio_openssl::SslStream;
|
||||||
use trust_dns_resolver::TokioAsyncResolver as AsyncResolver;
|
use trust_dns_resolver::TokioAsyncResolver as AsyncResolver;
|
||||||
|
|
||||||
use crate::{
|
use crate::connect::{
|
||||||
Address, Connect, ConnectError, ConnectService, ConnectServiceFactory, Connection,
|
Address, Connect, ConnectError, ConnectService, ConnectServiceFactory, Connection,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// 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 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,
|
||||||
{
|
{
|
||||||
type Request = Connection<T, U>;
|
|
||||||
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 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,
|
||||||
{
|
{
|
||||||
type Request = Connection<T, U>;
|
|
||||||
type Response = Connection<T, SslStream<U>>;
|
type Response = Connection<T, SslStream<U>>;
|
||||||
type Error = io::Error;
|
type Error = io::Error;
|
||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
@@ -108,19 +94,24 @@ 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) => Either::Left(ConnectAsyncExt {
|
Ok(config) => {
|
||||||
fut: async move { tokio_openssl::connect(config, &host, io).await }
|
let ssl = config
|
||||||
.boxed_local(),
|
.into_ssl(&host)
|
||||||
stream: Some(stream),
|
.expect("SSL connect configuration was invalid.");
|
||||||
_t: PhantomData,
|
|
||||||
}),
|
Either::Left(ConnectAsyncExt {
|
||||||
|
io: Some(SslStream::new(ssl, io).unwrap()),
|
||||||
|
stream: Some(stream),
|
||||||
|
_t: PhantomData,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ConnectAsyncExt<T, U> {
|
pub struct ConnectAsyncExt<T, U> {
|
||||||
fut: LocalBoxFuture<'static, Result<SslStream<U>, HandshakeError<U>>>,
|
io: Option<SslStream<U>>,
|
||||||
stream: Option<Connection<T, ()>>,
|
stream: Option<Connection<T, ()>>,
|
||||||
_t: PhantomData<U>,
|
_t: PhantomData<U>,
|
||||||
}
|
}
|
||||||
@@ -134,24 +125,23 @@ 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();
|
||||||
|
|
||||||
match Pin::new(&mut this.fut).poll(cx) {
|
match ready!(Pin::new(this.io.as_mut().unwrap()).poll_connect(cx)) {
|
||||||
Poll::Ready(Ok(stream)) => {
|
Ok(_) => {
|
||||||
let s = this.stream.take().unwrap();
|
let stream = this.stream.take().unwrap();
|
||||||
trace!("SSL Handshake success: {:?}", s.host());
|
trace!("SSL Handshake success: {:?}", stream.host());
|
||||||
Poll::Ready(Ok(s.replace(stream).1))
|
Poll::Ready(Ok(stream.replace(this.io.take().unwrap()).1))
|
||||||
}
|
}
|
||||||
Poll::Ready(Err(e)) => {
|
Err(e) => {
|
||||||
trace!("SSL Handshake error: {:?}", e);
|
trace!("SSL Handshake error: {:?}", e);
|
||||||
Poll::Ready(Err(io::Error::new(io::ErrorKind::Other, format!("{}", e))))
|
Poll::Ready(Err(io::Error::new(io::ErrorKind::Other, format!("{}", e))))
|
||||||
}
|
}
|
||||||
Poll::Pending => Poll::Pending,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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> {
|
||||||
@@ -177,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,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -192,8 +181,7 @@ impl<T> Clone for OpensslConnectServiceFactory<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Address + 'static> ServiceFactory for OpensslConnectServiceFactory<T> {
|
impl<T: Address + 'static> ServiceFactory<Connect<T>> for OpensslConnectServiceFactory<T> {
|
||||||
type Request = Connect<T>;
|
|
||||||
type Response = SslStream<TcpStream>;
|
type Response = SslStream<TcpStream>;
|
||||||
type Error = ConnectError;
|
type Error = ConnectError;
|
||||||
type Config = ();
|
type Config = ();
|
||||||
@@ -202,18 +190,17 @@ impl<T: Address + 'static> ServiceFactory for OpensslConnectServiceFactory<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()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[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 for OpensslConnectService<T> {
|
impl<T: Address + 'static> Service<Connect<T>> for OpensslConnectService<T> {
|
||||||
type Request = Connect<T>;
|
|
||||||
type Response = SslStream<TcpStream>;
|
type Response = SslStream<TcpStream>;
|
||||||
type Error = ConnectError;
|
type Error = ConnectError;
|
||||||
type Future = OpensslConnectServiceResponse<T>;
|
type Future = OpensslConnectServiceResponse<T>;
|
||||||
@@ -230,9 +217,9 @@ impl<T: Address + 'static> Service for OpensslConnectService<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct OpensslConnectServiceResponse<T: Address + 'static> {
|
pub struct OpensslConnectServiceResponse<T: Address + 'static> {
|
||||||
fut1: Option<<ConnectService<T> as Service>::Future>,
|
fut1: Option<<ConnectService<T> as Service<Connect<T>>>::Future>,
|
||||||
fut2: Option<<OpensslConnectorService<T, TcpStream> as Service>::Future>,
|
fut2: Option<<OpensslConnectorService as Service<Connection<T, TcpStream>>>::Future>,
|
||||||
openssl: OpensslConnectorService<T, TcpStream>,
|
openssl: OpensslConnectorService,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Address> Future for OpensslConnectServiceResponse<T> {
|
impl<T: Address> Future for OpensslConnectServiceResponse<T> {
|
||||||
@@ -240,7 +227,7 @@ impl<T: Address> Future for OpensslConnectServiceResponse<T> {
|
|||||||
|
|
||||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
if let Some(ref mut fut) = self.fut1 {
|
if let Some(ref mut fut) = self.fut1 {
|
||||||
match futures_util::ready!(Pin::new(fut).poll(cx)) {
|
match ready!(Pin::new(fut).poll(cx)) {
|
||||||
Ok(res) => {
|
Ok(res) => {
|
||||||
let _ = self.fut1.take();
|
let _ = self.fut1.take();
|
||||||
self.fut2 = Some(self.openssl.call(res));
|
self.fut2 = Some(self.openssl.call(res));
|
||||||
@@ -250,7 +237,7 @@ impl<T: Address> Future for OpensslConnectServiceResponse<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(ref mut fut) = self.fut2 {
|
if let Some(ref mut fut) = self.fut2 {
|
||||||
match futures_util::ready!(Pin::new(fut).poll(cx)) {
|
match ready!(Pin::new(fut).poll(cx)) {
|
||||||
Ok(connect) => Poll::Ready(Ok(connect.into_parts().0)),
|
Ok(connect) => Poll::Ready(Ok(connect.into_parts().0)),
|
||||||
Err(e) => Poll::Ready(Err(ConnectError::Io(io::Error::new(
|
Err(e) => Poll::Ready(Err(ConnectError::Io(io::Error::new(
|
||||||
io::ErrorKind::Other,
|
io::ErrorKind::Other,
|
@@ -1,97 +1,84 @@
|
|||||||
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};
|
||||||
|
|
||||||
pub use rust_tls::Session;
|
pub use rustls::Session;
|
||||||
pub use tokio_rustls::{client::TlsStream, rustls::ClientConfig};
|
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 tokio_rustls::{Connect, TlsConnector};
|
use tokio_rustls::{Connect, TlsConnector};
|
||||||
use webpki::DNSNameRef;
|
use webpki::DNSNameRef;
|
||||||
|
|
||||||
use crate::{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 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 Request = Connection<T, U>;
|
|
||||||
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 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 Request = Connection<T, U>;
|
|
||||||
type Response = Connection<T, TlsStream<U>>;
|
type Response = Connection<T, TlsStream<U>>;
|
||||||
type Error = std::io::Error;
|
type Error = std::io::Error;
|
||||||
type Future = ConnectAsyncExt<T, U>;
|
type Future = ConnectAsyncExt<T, U>;
|
||||||
@@ -115,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
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,6 +1,6 @@
|
|||||||
use http::Uri;
|
use http::Uri;
|
||||||
|
|
||||||
use crate::Address;
|
use super::Address;
|
||||||
|
|
||||||
impl Address for Uri {
|
impl Address for Uri {
|
||||||
fn host(&self) -> &str {
|
fn host(&self) -> &str {
|
@@ -1,46 +1,17 @@
|
|||||||
//! TLS acceptor services for Actix ecosystem.
|
//! TLS acceptor and connector services for Actix ecosystem
|
||||||
//!
|
|
||||||
//! ## Crate Features
|
|
||||||
//! * `openssl` - TLS acceptor using the `openssl` crate.
|
|
||||||
//! * `rustls` - TLS acceptor using the `rustls` crate.
|
|
||||||
//! * `nativetls` - TLS acceptor using the `native-tls` crate.
|
|
||||||
|
|
||||||
#![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::sync::atomic::{AtomicUsize, Ordering};
|
#[cfg(feature = "native-tls")]
|
||||||
|
extern crate tls_native_tls as native_tls;
|
||||||
use actix_utils::counter::Counter;
|
|
||||||
|
|
||||||
#[cfg(feature = "openssl")]
|
#[cfg(feature = "openssl")]
|
||||||
pub mod openssl;
|
extern crate tls_openssl as openssl;
|
||||||
|
|
||||||
#[cfg(feature = "rustls")]
|
#[cfg(feature = "rustls")]
|
||||||
pub mod rustls;
|
extern crate tls_rustls as rustls;
|
||||||
|
|
||||||
#[cfg(feature = "nativetls")]
|
#[cfg(feature = "accept")]
|
||||||
pub mod nativetls;
|
pub mod accept;
|
||||||
|
#[cfg(feature = "connect")]
|
||||||
pub(crate) static MAX_CONN: AtomicUsize = AtomicUsize::new(256);
|
pub mod connect;
|
||||||
|
|
||||||
thread_local! {
|
|
||||||
static MAX_CONN_COUNTER: Counter = Counter::new(MAX_CONN.load(Ordering::Relaxed));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the maximum per-worker concurrent TLS connection limit.
|
|
||||||
///
|
|
||||||
/// All listeners will stop accepting connections when this limit is reached.
|
|
||||||
/// It can be used to regulate the global TLS CPU usage.
|
|
||||||
///
|
|
||||||
/// By default, the connection limit is 256.
|
|
||||||
pub fn max_concurrent_tls_connect(num: usize) {
|
|
||||||
MAX_CONN.store(num, Ordering::Relaxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// TLS error combined with service error.
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum TlsError<E1, E2> {
|
|
||||||
Tls(E1),
|
|
||||||
Service(E2),
|
|
||||||
}
|
|
||||||
|
@@ -2,15 +2,18 @@ use std::io;
|
|||||||
|
|
||||||
use actix_codec::{BytesCodec, Framed};
|
use actix_codec::{BytesCodec, Framed};
|
||||||
use actix_rt::net::TcpStream;
|
use actix_rt::net::TcpStream;
|
||||||
|
use actix_server::TestServer;
|
||||||
use actix_service::{fn_service, Service, ServiceFactory};
|
use actix_service::{fn_service, Service, ServiceFactory};
|
||||||
use actix_testing::TestServer;
|
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use futures_util::sink::SinkExt;
|
use futures_util::sink::SinkExt;
|
||||||
|
|
||||||
use actix_connect::resolver::{ResolverConfig, ResolverOpts};
|
use actix_tls::connect::{
|
||||||
use actix_connect::Connect;
|
self as actix_connect,
|
||||||
|
resolver::{ResolverConfig, ResolverOpts},
|
||||||
|
Connect,
|
||||||
|
};
|
||||||
|
|
||||||
#[cfg(feature = "openssl")]
|
#[cfg(all(feature = "connect", feature = "openssl"))]
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_string() {
|
async fn test_string() {
|
||||||
let srv = TestServer::with(|| {
|
let srv = TestServer::with(|| {
|
@@ -16,11 +16,12 @@ name = "actix_tracing"
|
|||||||
path = "src/lib.rs"
|
path = "src/lib.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix-service = "1.0.4"
|
actix-service = "2.0.0-beta.1"
|
||||||
|
|
||||||
futures-util = { version = "0.3.4", default-features = false }
|
futures-util = { version = "0.3.4", default-features = false }
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
tracing-futures = "0.2"
|
tracing-futures = "0.2"
|
||||||
|
|
||||||
[dev_dependencies]
|
[dev_dependencies]
|
||||||
actix-rt = "1.0"
|
actix-rt = "1.0"
|
||||||
slab = "0.4"
|
slab = "0.4"
|
||||||
|
36
bytestring/CHANGES.md
Normal file
36
bytestring/CHANGES.md
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
# Changes
|
||||||
|
|
||||||
|
## Unreleased - 2020-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
|
@@ -1,9 +1,13 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "bytestring"
|
name = "bytestring"
|
||||||
version = "0.1.5"
|
version = "1.0.0"
|
||||||
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
authors = [
|
||||||
description = "A UTF-8 encoded string with Bytes as a storage"
|
"Nikolay Kim <fafhrd91@gmail.com>",
|
||||||
keywords = ["actix"]
|
"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"
|
homepage = "https://actix.rs"
|
||||||
repository = "https://github.com/actix/actix-net.git"
|
repository = "https://github.com/actix/actix-net.git"
|
||||||
documentation = "https://docs.rs/bytestring/"
|
documentation = "https://docs.rs/bytestring/"
|
||||||
@@ -15,8 +19,9 @@ name = "bytestring"
|
|||||||
path = "src/lib.rs"
|
path = "src/lib.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bytes = "0.5.3"
|
bytes = "1"
|
||||||
serde = { version = "1.0", optional = true }
|
serde = { version = "1.0", optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
|
siphasher = "0.3"
|
@@ -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 siphasher::sip::SipHasher;
|
||||||
|
|
||||||
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 = SipHasher::default();
|
||||||
"str".hash(&mut hasher1);
|
"str".hash(&mut hasher1);
|
||||||
|
|
||||||
let mut hasher2 = DefaultHasher::default();
|
let mut hasher2 = SipHasher::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();
|
||||||
}
|
}
|
||||||
|
|
@@ -1 +0,0 @@
|
|||||||
../LICENSE-APACHE
|
|
@@ -1 +0,0 @@
|
|||||||
../LICENSE-MIT
|
|
@@ -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
|
|
@@ -1 +0,0 @@
|
|||||||
../LICENSE-APACHE
|
|
@@ -1 +0,0 @@
|
|||||||
../LICENSE-MIT
|
|
Reference in New Issue
Block a user