1
0
mirror of https://github.com/fafhrd91/actix-net synced 2025-08-14 11:10:32 +02:00

Compare commits

..

9 Commits

Author SHA1 Message Date
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
Rob Ede
0934078947 prepare tls beta release 2020-12-29 01:04:21 +00:00
Rob Ede
5759c9e144 merge -connect and -tls and upgrade to rt v2 (#238) 2020-12-29 00:38:41 +00:00
Rob Ede
3c6de3a81b use correct service version for tracing 2020-12-29 00:08:59 +00:00
52 changed files with 487 additions and 742 deletions

View File

@@ -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" }

View File

@@ -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

View File

@@ -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"

View File

@@ -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"

View File

@@ -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]

View File

@@ -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

@@ -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.
/// ///
@@ -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()
} }
} }

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

@@ -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

@@ -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

View File

@@ -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"

View File

@@ -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,
}; };

View 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),
}

View File

@@ -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()
} }
} }

View File

@@ -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.")))
} }
} }

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,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)),
}
} }
} }

View File

@@ -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)));
},
} }
} }
} }

View File

@@ -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)

View File

@@ -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> {

View File

@@ -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>,

View File

@@ -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,

View File

@@ -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
}),
)
} }
} }

View File

@@ -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 {

View File

@@ -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),
}

View File

@@ -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(|| {

View File

@@ -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
View 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

View File

@@ -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"

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 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();
} }

View File

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

View File

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

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 +0,0 @@
../LICENSE-APACHE

View File

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