├── router ├── LICENSE-MIT ├── LICENSE-APACHE ├── Cargo.toml ├── CHANGES.md └── src │ ├── lib.rs │ └── path.rs ├── string ├── LICENSE-MIT ├── LICENSE-APACHE ├── CHANGES.md ├── Cargo.toml └── src │ └── lib.rs ├── actix-codec ├── LICENSE-MIT ├── LICENSE-APACHE ├── src │ ├── lib.rs │ └── bcodec.rs ├── CHANGES.md └── Cargo.toml ├── actix-macros ├── LICENSE-MIT ├── LICENSE-APACHE ├── Cargo.toml └── src │ └── lib.rs ├── actix-rt ├── LICENSE-MIT ├── LICENSE-APACHE ├── Cargo.toml ├── src │ ├── lib.rs │ ├── runtime.rs │ └── system.rs └── CHANGES.md ├── actix-server ├── LICENSE-MIT ├── LICENSE-APACHE ├── src │ ├── lib.rs │ ├── signals.rs │ ├── server.rs │ ├── service.rs │ └── socket.rs ├── Cargo.toml ├── CHANGES.md └── tests │ └── test_server.rs ├── actix-tls ├── LICENSE-MIT ├── LICENSE-APACHE ├── CHANGES.md ├── src │ ├── lib.rs │ ├── nativetls.rs │ ├── rustls.rs │ └── openssl.rs └── Cargo.toml ├── actix-utils ├── LICENSE-MIT ├── LICENSE-APACHE ├── src │ ├── lib.rs │ ├── cell.rs │ ├── counter.rs │ ├── stream.rs │ ├── task.rs │ ├── condition.rs │ ├── keepalive.rs │ ├── either.rs │ └── inflight.rs ├── Cargo.toml └── CHANGES.md ├── actix-connect ├── LICENSE-MIT ├── LICENSE-APACHE ├── src │ ├── ssl │ │ ├── mod.rs │ │ └── rustls.rs │ ├── error.rs │ ├── uri.rs │ ├── lib.rs │ ├── connector.rs │ └── resolve.rs ├── Cargo.toml ├── CHANGES.md └── tests │ └── test_connect.rs ├── actix-ioframe ├── LICENSE-MIT ├── LICENSE-APACHE ├── src │ ├── lib.rs │ ├── error.rs │ └── connect.rs ├── CHANGES.md ├── Cargo.toml └── tests │ └── test_server.rs ├── actix-service ├── LICENSE-MIT ├── LICENSE-APACHE ├── Cargo.toml ├── src │ ├── cell.rs │ ├── map_init_err.rs │ ├── transform_err.rs │ ├── map_config.rs │ └── boxed.rs └── CHANGES.md ├── actix-testing ├── LICENSE-MIT ├── LICENSE-APACHE ├── CHANGES.md ├── README.md ├── Cargo.toml └── src │ └── lib.rs ├── actix-threadpool ├── LICENSE-MIT ├── LICENSE-APACHE ├── CHANGES.md ├── Cargo.toml └── src │ └── lib.rs ├── actix-tracing ├── LICENSE-MIT ├── LICENSE-APACHE ├── CHANGES.md └── Cargo.toml ├── rustfmt.toml ├── .gitignore ├── .github └── workflows │ ├── clippy.yml │ ├── macos.yml │ ├── windows-mingw.yml │ ├── windows.yml │ └── linux.yml ├── Cargo.toml ├── LICENSE-MIT ├── examples ├── ssl.rs ├── cert.pem ├── basic.rs └── key.pem ├── CODE_OF_CONDUCT.md └── README.md /router/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /string/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /actix-codec/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /actix-macros/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /actix-rt/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /actix-server/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /actix-tls/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /actix-utils/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /router/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /string/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /actix-codec/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /actix-connect/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /actix-ioframe/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /actix-rt/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /actix-service/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /actix-testing/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /actix-threadpool/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /actix-tls/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /actix-tracing/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /actix-utils/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /actix-connect/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /actix-ioframe/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /actix-macros/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /actix-server/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /actix-service/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /actix-testing/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /actix-threadpool/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /actix-tracing/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /actix-tracing/CHANGES.md: -------------------------------------------------------------------------------- 1 | # Changes 2 | 3 | ## [0.1.0] - 2020-01-15 4 | 5 | * Initial release 6 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | max_width = 96 2 | reorder_imports = true 3 | #wrap_comments = true 4 | #fn_args_density = "Compressed" 5 | #use_small_heuristics = false 6 | -------------------------------------------------------------------------------- /actix-connect/src/ssl/mod.rs: -------------------------------------------------------------------------------- 1 | //! SSL Services 2 | 3 | #[cfg(feature = "openssl")] 4 | pub mod openssl; 5 | 6 | #[cfg(feature = "rustls")] 7 | pub mod rustls; 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Cargo.lock 2 | target/ 3 | guide/build/ 4 | /gh-pages 5 | 6 | *.so 7 | *.out 8 | *.pyc 9 | *.pid 10 | *.sock 11 | *~ 12 | 13 | # These are backup files generated by rustfmt 14 | **/*.rs.bk 15 | 16 | .idea 17 | -------------------------------------------------------------------------------- /actix-ioframe/src/lib.rs: -------------------------------------------------------------------------------- 1 | // #![deny(rust_2018_idioms, warnings)] 2 | #![allow(clippy::type_complexity, clippy::too_many_arguments)] 3 | 4 | mod connect; 5 | mod dispatcher; 6 | mod error; 7 | mod service; 8 | 9 | pub use self::connect::{Connect, ConnectResult}; 10 | pub use self::error::ServiceError; 11 | pub use self::service::{Builder, FactoryBuilder}; 12 | -------------------------------------------------------------------------------- /actix-tls/CHANGES.md: -------------------------------------------------------------------------------- 1 | # Changes 2 | 3 | ## [1.0.0] - 2019-12-11 4 | 5 | * 1.0.0 release 6 | 7 | ## [1.0.0-alpha.3] - 2019-12-07 8 | 9 | ### Changed 10 | 11 | * Migrate to tokio 0.2 12 | 13 | * Enable rustls acceptor service 14 | 15 | * Enable native-tls acceptor service 16 | 17 | ## [1.0.0-alpha.1] - 2019-12-02 18 | 19 | * Split openssl accetor from actix-server package 20 | -------------------------------------------------------------------------------- /actix-utils/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Actix utils - various helper services 2 | #![deny(rust_2018_idioms, warnings)] 3 | #![allow(clippy::type_complexity)] 4 | 5 | mod cell; 6 | pub mod condition; 7 | pub mod counter; 8 | pub mod either; 9 | pub mod framed; 10 | pub mod inflight; 11 | pub mod keepalive; 12 | pub mod mpsc; 13 | pub mod oneshot; 14 | pub mod order; 15 | pub mod stream; 16 | pub mod task; 17 | pub mod time; 18 | pub mod timeout; 19 | -------------------------------------------------------------------------------- /string/CHANGES.md: -------------------------------------------------------------------------------- 1 | # Changes 2 | 3 | ## [0.1.4] - 2020-01-14 4 | 5 | * Fix `AsRef` impl 6 | 7 | ## [0.1.3] - 2020-01-13 8 | 9 | * Add `PartialEq>`, `AsRef<[u8]>` impls 10 | 11 | ## [0.1.2] - 2019-12-22 12 | 13 | * Fix `new()` method 14 | 15 | * Make `ByteString::from_static()` and `ByteString::from_bytes_unchecked()` methods const. 16 | 17 | ## [0.1.1] - 2019-12-07 18 | 19 | * Fix hash impl 20 | 21 | ## [0.1.0] - 2019-12-07 22 | 23 | * Initial release 24 | -------------------------------------------------------------------------------- /actix-testing/CHANGES.md: -------------------------------------------------------------------------------- 1 | # Changes 2 | 3 | ## [1.0.0] - 2019-12-11 4 | 5 | * Update actix-server to 1.0.0 6 | 7 | ## [1.0.0-alpha.3] - 2019-12-07 8 | 9 | * Migrate to tokio 0.2 10 | 11 | ## [1.0.0-alpha.2] - 2019-12-02 12 | 13 | * Re-export `test` attribute macros 14 | 15 | 16 | ## [0.3.0-alpha.1] - 2019-11-22 17 | 18 | * Migrate to std::future 19 | 20 | ## [0.2.0] - 2019-10-14 21 | 22 | * Upgrade actix-server and actix-server-config deps 23 | 24 | 25 | ## [0.1.0] - 2019-09-25 26 | 27 | * Initial impl 28 | -------------------------------------------------------------------------------- /string/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bytestring" 3 | version = "0.1.4" 4 | authors = ["Nikolay Kim "] 5 | description = "A UTF-8 encoded string with Bytes as a storage" 6 | keywords = ["actix"] 7 | homepage = "https://actix.rs" 8 | repository = "https://github.com/actix/actix-net.git" 9 | documentation = "https://docs.rs/bytestring/" 10 | license = "MIT/Apache-2.0" 11 | edition = "2018" 12 | 13 | [lib] 14 | name = "bytestring" 15 | path = "src/lib.rs" 16 | 17 | [dependencies] 18 | bytes = "0.5.3" 19 | -------------------------------------------------------------------------------- /.github/workflows/clippy.yml: -------------------------------------------------------------------------------- 1 | on: pull_request 2 | 3 | name: Clippy Check 4 | jobs: 5 | clippy_check: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@master 9 | - uses: actions-rs/toolchain@v1 10 | with: 11 | toolchain: nightly 12 | components: clippy 13 | profile: minimal 14 | override: true 15 | - uses: actions-rs/clippy-check@v1 16 | with: 17 | token: ${{ secrets.GITHUB_TOKEN }} 18 | args: --all-features --all --tests 19 | -------------------------------------------------------------------------------- /actix-macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "actix-macros" 3 | version = "0.1.1" 4 | authors = ["Nikolay Kim "] 5 | description = "Actix runtime macros" 6 | repository = "https://github.com/actix/actix-net" 7 | documentation = "https://docs.rs/actix-macros/" 8 | categories = ["network-programming", "asynchronous"] 9 | license = "MIT/Apache-2.0" 10 | edition = "2018" 11 | workspace = ".." 12 | 13 | [lib] 14 | proc-macro = true 15 | 16 | [dependencies] 17 | quote = "^1" 18 | syn = { version = "^1", features = ["full"] } 19 | 20 | [dev-dependencies] 21 | actix-rt = { version = "1.0.0" } 22 | -------------------------------------------------------------------------------- /actix-codec/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Utilities for encoding and decoding frames. 2 | //! 3 | //! Contains adapters to go from streams of bytes, [`AsyncRead`] and 4 | //! [`AsyncWrite`], to framed streams implementing [`Sink`] and [`Stream`]. 5 | //! Framed streams are also known as [transports]. 6 | //! 7 | //! [`AsyncRead`]: # 8 | //! [`AsyncWrite`]: # 9 | #![deny(rust_2018_idioms, warnings)] 10 | 11 | mod bcodec; 12 | mod framed; 13 | 14 | pub use self::bcodec::BytesCodec; 15 | pub use self::framed::{Framed, FramedParts}; 16 | 17 | pub use tokio::io::{AsyncRead, AsyncWrite}; 18 | pub use tokio_util::codec::{Decoder, Encoder}; 19 | -------------------------------------------------------------------------------- /actix-threadpool/CHANGES.md: -------------------------------------------------------------------------------- 1 | # Changes 2 | 3 | ## [0.3.1] - 2019-12-12 4 | 5 | ### Changed 6 | 7 | * Use parking_lot 0.10 8 | 9 | ## [0.3.0] - 2019-12-02 10 | 11 | ### Changed 12 | 13 | * Expect `Result` type as a function return type 14 | 15 | ## [0.2.0] - 2019-11-21 16 | 17 | ### Changed 18 | 19 | * Migrate to `std::future` 20 | 21 | ## [0.1.2] - 2019-08-05 22 | 23 | ### Changed 24 | 25 | * Update `derive_more` to 0.15 26 | 27 | * Update `parking_lot` to 0.9 28 | 29 | ## [0.1.1] - 2019-06-05 30 | 31 | * Update parking_lot 32 | 33 | ## [0.1.0] - 2019-03-28 34 | 35 | * Move threadpool to separate crate 36 | -------------------------------------------------------------------------------- /actix-ioframe/CHANGES.md: -------------------------------------------------------------------------------- 1 | # Changes 2 | 3 | ## [0.5.0] - 2019-12-29 4 | 5 | * Simplify state management 6 | 7 | * Allow to set custom output stream 8 | 9 | * Removed disconnect callback 10 | 11 | ## [0.4.1] - 2019-12-11 12 | 13 | * Disconnect callback accepts owned state 14 | 15 | ## [0.4.0] - 2019-12-11 16 | 17 | * Remove `E` param 18 | 19 | ## [0.3.0-alpha.3] - 2019-12-07 20 | 21 | * Migrate to tokio 0.2 22 | 23 | ## [0.3.0-alpha.2] - 2019-12-02 24 | 25 | * Migrate to `std::future` 26 | 27 | ## [0.1.1] - 2019-10-14 28 | 29 | * Re-register task on every dispatcher poll. 30 | 31 | ## [0.1.0] - 2019-09-25 32 | 33 | * Initial release 34 | -------------------------------------------------------------------------------- /actix-codec/CHANGES.md: -------------------------------------------------------------------------------- 1 | # Changes 2 | 3 | * Use `.advance()` intead of `.split_to()` 4 | 5 | ## [0.2.0] - 2019-12-10 6 | 7 | * Use specific futures dependencies 8 | 9 | ## [0.2.0-alpha.4] 10 | 11 | * Fix buffer remaining capacity calcualtion 12 | 13 | ## [0.2.0-alpha.3] 14 | 15 | * Use tokio 0.2 16 | 17 | * Fix low/high watermark for write/read buffers 18 | 19 | ## [0.2.0-alpha.2] 20 | 21 | * Migrated to `std::future` 22 | 23 | ## [0.1.2] - 2019-03-27 24 | 25 | * Added `Framed::map_io()` method. 26 | 27 | ## [0.1.1] - 2019-03-06 28 | 29 | * Added `FramedParts::with_read_buffer()` method. 30 | 31 | ## [0.1.0] - 2018-12-09 32 | 33 | * Move codec to separate crate 34 | -------------------------------------------------------------------------------- /actix-testing/README.md: -------------------------------------------------------------------------------- 1 | # 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) 2 | 3 | ## Documentation & community resources 4 | 5 | * [User Guide](https://actix.rs/docs/) 6 | * [API Documentation](https://docs.rs/actix-testing/) 7 | * [Chat on gitter](https://gitter.im/actix/actix) 8 | * Cargo package: [actix-http-test](https://crates.io/crates/actix-testing) 9 | * Minimum supported Rust version: 1.37 or later 10 | -------------------------------------------------------------------------------- /actix-service/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "actix-service" 3 | version = "1.0.5" 4 | authors = ["Nikolay Kim "] 5 | description = "Actix service" 6 | keywords = ["network", "framework", "async", "futures"] 7 | homepage = "https://actix.rs" 8 | repository = "https://github.com/actix/actix-net.git" 9 | documentation = "https://docs.rs/actix-service/" 10 | categories = ["network-programming", "asynchronous"] 11 | license = "MIT/Apache-2.0" 12 | edition = "2018" 13 | 14 | [lib] 15 | name = "actix_service" 16 | path = "src/lib.rs" 17 | 18 | [dependencies] 19 | futures-util = "0.3.1" 20 | pin-project = "0.4.6" 21 | 22 | [dev-dependencies] 23 | actix-rt = "1.0.0" 24 | -------------------------------------------------------------------------------- /router/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "actix-router" 3 | version = "0.2.4" 4 | authors = ["Nikolay Kim "] 5 | description = "Path router" 6 | keywords = ["actix"] 7 | homepage = "https://actix.rs" 8 | repository = "https://github.com/actix/actix-net.git" 9 | documentation = "https://docs.rs/actix-router/" 10 | license = "MIT/Apache-2.0" 11 | edition = "2018" 12 | 13 | [lib] 14 | name = "actix_router" 15 | path = "src/lib.rs" 16 | 17 | [features] 18 | default = ["http"] 19 | 20 | [dependencies] 21 | regex = "1.3.1" 22 | serde = "1.0.104" 23 | bytestring = "0.1.2" 24 | log = "0.4.8" 25 | http = { version="0.2.0", optional=true } 26 | 27 | [dev-dependencies] 28 | http = "0.2.0" 29 | serde_derive = "1.0" 30 | -------------------------------------------------------------------------------- /actix-connect/src/error.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | 3 | use derive_more::{Display, From}; 4 | use trust_dns_resolver::error::ResolveError; 5 | 6 | #[derive(Debug, From, Display)] 7 | pub enum ConnectError { 8 | /// Failed to resolve the hostname 9 | #[display(fmt = "Failed resolving hostname: {}", _0)] 10 | Resolver(ResolveError), 11 | 12 | /// No dns records 13 | #[display(fmt = "No dns records found for the input")] 14 | NoRecords, 15 | 16 | /// Invalid input 17 | InvalidInput, 18 | 19 | /// Unresolved host name 20 | #[display(fmt = "Connector received `Connect` method with unresolved host")] 21 | Unresolverd, 22 | 23 | /// Connection io error 24 | #[display(fmt = "{}", _0)] 25 | Io(io::Error), 26 | } 27 | -------------------------------------------------------------------------------- /actix-testing/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "actix-testing" 3 | version = "1.0.0" 4 | authors = ["Nikolay Kim "] 5 | description = "Actix testing utils" 6 | keywords = ["network", "framework", "async", "futures"] 7 | homepage = "https://actix.rs" 8 | repository = "https://github.com/actix/actix-net.git" 9 | documentation = "https://docs.rs/actix-testing/" 10 | categories = ["network-programming", "asynchronous"] 11 | license = "MIT/Apache-2.0" 12 | edition = "2018" 13 | workspace = ".." 14 | 15 | [lib] 16 | name = "actix_testing" 17 | path = "src/lib.rs" 18 | 19 | [dependencies] 20 | actix-rt = "1.0.0" 21 | actix-macros = "0.1.0" 22 | actix-server = "1.0.0" 23 | actix-service = "1.0.0" 24 | 25 | log = "0.4" 26 | net2 = "0.2" 27 | futures = "0.3.1" 28 | -------------------------------------------------------------------------------- /actix-tracing/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "actix-tracing" 3 | version = "0.1.0" 4 | authors = ["Rajasekharan Vengalil "] 5 | description = "Support for tokio tracing with Actix services" 6 | keywords = ["network", "framework", "tracing"] 7 | homepage = "https://actix.rs" 8 | repository = "https://github.com/actix/actix-net.git" 9 | documentation = "https://docs.rs/actix-tracing/" 10 | categories = ["network-programming", "asynchronous"] 11 | license = "MIT/Apache-2.0" 12 | edition = "2018" 13 | 14 | [lib] 15 | name = "actix_tracing" 16 | path = "src/lib.rs" 17 | 18 | [dependencies] 19 | actix-service = "1.0.4" 20 | futures-util = "0.3.1" 21 | tracing = "0.1" 22 | tracing-futures = "0.2" 23 | 24 | [dev_dependencies] 25 | actix-rt = "1.0" 26 | slab = "0.4" -------------------------------------------------------------------------------- /actix-rt/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "actix-rt" 3 | version = "1.0.0" 4 | authors = ["Nikolay Kim "] 5 | description = "Actix runtime" 6 | keywords = ["network", "framework", "async", "futures"] 7 | homepage = "https://actix.rs" 8 | repository = "https://github.com/actix/actix-net.git" 9 | documentation = "https://docs.rs/actix-rt/" 10 | categories = ["network-programming", "asynchronous"] 11 | license = "MIT/Apache-2.0" 12 | edition = "2018" 13 | 14 | [lib] 15 | name = "actix_rt" 16 | path = "src/lib.rs" 17 | 18 | [dependencies] 19 | actix-macros = "0.1.0" 20 | actix-threadpool = "0.3" 21 | futures = "0.3.1" 22 | copyless = "0.1.4" 23 | tokio = { version = "0.2.6", default-features=false, features = ["rt-core", "rt-util", "io-driver", "tcp", "uds", "udp", "time", "signal", "stream"] } 24 | -------------------------------------------------------------------------------- /actix-utils/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "actix-utils" 3 | version = "1.0.6" 4 | authors = ["Nikolay Kim "] 5 | description = "Actix utils - various actix net related services" 6 | keywords = ["network", "framework", "async", "futures"] 7 | homepage = "https://actix.rs" 8 | repository = "https://github.com/actix/actix-net.git" 9 | documentation = "https://docs.rs/actix-utils/" 10 | categories = ["network-programming", "asynchronous"] 11 | license = "MIT/Apache-2.0" 12 | edition = "2018" 13 | 14 | [lib] 15 | name = "actix_utils" 16 | path = "src/lib.rs" 17 | 18 | [dependencies] 19 | actix-service = "1.0.1" 20 | actix-rt = "1.0.0" 21 | actix-codec = "0.2.0" 22 | bitflags = "1.2" 23 | bytes = "0.5.3" 24 | either = "1.5.3" 25 | futures = "0.3.1" 26 | pin-project = "0.4.6" 27 | log = "0.4" 28 | slab = "0.4" 29 | -------------------------------------------------------------------------------- /actix-codec/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "actix-codec" 3 | version = "0.2.0" 4 | authors = ["Nikolay Kim "] 5 | description = "Utilities for encoding and decoding frames" 6 | keywords = ["network", "framework", "async", "futures"] 7 | homepage = "https://actix.rs" 8 | repository = "https://github.com/actix/actix-net.git" 9 | documentation = "https://docs.rs/actix-codec/" 10 | categories = ["network-programming", "asynchronous"] 11 | license = "MIT/Apache-2.0" 12 | edition = "2018" 13 | workspace = ".." 14 | 15 | [lib] 16 | name = "actix_codec" 17 | path = "src/lib.rs" 18 | 19 | [dependencies] 20 | bitflags = "1.2.1" 21 | bytes = "0.5.2" 22 | futures-core = "0.3.1" 23 | futures-sink = "0.3.1" 24 | tokio = { version = "0.2.4", default-features=false } 25 | tokio-util = { version = "0.2.0", default-features=false, features=["codec"] } 26 | log = "0.4" -------------------------------------------------------------------------------- /actix-ioframe/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "actix-ioframe" 3 | version = "0.5.0" 4 | authors = ["Nikolay Kim "] 5 | description = "Actix framed service" 6 | keywords = ["network", "framework", "async", "futures"] 7 | homepage = "https://actix.rs" 8 | repository = "https://github.com/actix/actix-net.git" 9 | documentation = "https://docs.rs/actix-ioframe/" 10 | categories = ["network-programming", "asynchronous"] 11 | license = "MIT/Apache-2.0" 12 | edition = "2018" 13 | 14 | [lib] 15 | name = "actix_ioframe" 16 | path = "src/lib.rs" 17 | 18 | [dependencies] 19 | actix-service = "1.0.1" 20 | actix-codec = "0.2.0" 21 | actix-utils = "1.0.4" 22 | actix-rt = "1.0.0" 23 | bytes = "0.5.3" 24 | either = "1.5.3" 25 | futures = "0.3.1" 26 | pin-project = "0.4.6" 27 | log = "0.4" 28 | 29 | [dev-dependencies] 30 | actix-connect = "1.0.0" 31 | actix-testing = "1.0.0" 32 | -------------------------------------------------------------------------------- /actix-threadpool/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "actix-threadpool" 3 | version = "0.3.1" 4 | authors = ["Nikolay Kim "] 5 | description = "Actix thread pool for sync code" 6 | keywords = ["actix", "network", "framework", "async", "futures"] 7 | homepage = "https://actix.rs" 8 | repository = "https://github.com/actix/actix-net.git" 9 | documentation = "https://docs.rs/actix-threadpool/" 10 | categories = ["network-programming", "asynchronous"] 11 | license = "MIT/Apache-2.0" 12 | exclude = [".gitignore", ".travis.yml", ".cargo/config", "appveyor.yml"] 13 | edition = "2018" 14 | workspace = ".." 15 | 16 | [lib] 17 | name = "actix_threadpool" 18 | path = "src/lib.rs" 19 | 20 | [dependencies] 21 | derive_more = "0.99.2" 22 | futures-channel = "0.3.1" 23 | parking_lot = "0.10" 24 | lazy_static = "1.3" 25 | log = "0.4" 26 | num_cpus = "1.10" 27 | threadpool = "1.7" 28 | -------------------------------------------------------------------------------- /actix-server/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! General purpose tcp server 2 | #![deny(rust_2018_idioms, warnings)] 3 | #![allow(clippy::type_complexity)] 4 | 5 | mod accept; 6 | mod builder; 7 | mod config; 8 | mod server; 9 | mod service; 10 | mod signals; 11 | mod socket; 12 | mod worker; 13 | 14 | pub use self::builder::ServerBuilder; 15 | pub use self::config::{ServiceConfig, ServiceRuntime}; 16 | pub use self::server::Server; 17 | pub use self::service::ServiceFactory; 18 | 19 | #[doc(hidden)] 20 | pub use self::socket::FromStream; 21 | 22 | /// Socket id token 23 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] 24 | pub(crate) struct Token(usize); 25 | 26 | impl Token { 27 | pub(crate) fn next(&mut self) -> Token { 28 | let token = Token(self.0); 29 | self.0 += 1; 30 | token 31 | } 32 | } 33 | 34 | /// Start server building process 35 | pub fn new() -> ServerBuilder { 36 | ServerBuilder::default() 37 | } 38 | -------------------------------------------------------------------------------- /actix-codec/src/bcodec.rs: -------------------------------------------------------------------------------- 1 | use bytes::{BufMut, Bytes, BytesMut}; 2 | use std::io; 3 | 4 | use super::{Decoder, Encoder}; 5 | 6 | /// Bytes codec. 7 | /// 8 | /// Reads/Writes chunks of bytes from a stream. 9 | #[derive(Debug, Copy, Clone)] 10 | pub struct BytesCodec; 11 | 12 | impl Encoder for BytesCodec { 13 | type Item = Bytes; 14 | type Error = io::Error; 15 | 16 | fn encode(&mut self, item: Bytes, dst: &mut BytesMut) -> Result<(), Self::Error> { 17 | dst.reserve(item.len()); 18 | dst.put(item); 19 | Ok(()) 20 | } 21 | } 22 | 23 | impl Decoder for BytesCodec { 24 | type Item = BytesMut; 25 | type Error = io::Error; 26 | 27 | fn decode(&mut self, src: &mut BytesMut) -> Result, Self::Error> { 28 | if src.is_empty() { 29 | Ok(None) 30 | } else { 31 | let len = src.len(); 32 | Ok(Some(src.split_to(len))) 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /actix-connect/src/uri.rs: -------------------------------------------------------------------------------- 1 | use http::Uri; 2 | 3 | use crate::Address; 4 | 5 | impl Address for Uri { 6 | fn host(&self) -> &str { 7 | self.host().unwrap_or("") 8 | } 9 | 10 | fn port(&self) -> Option { 11 | if let Some(port) = self.port_u16() { 12 | Some(port) 13 | } else { 14 | port(self.scheme_str()) 15 | } 16 | } 17 | } 18 | 19 | // TODO: load data from file 20 | fn port(scheme: Option<&str>) -> Option { 21 | if let Some(scheme) = scheme { 22 | match scheme { 23 | "http" => Some(80), 24 | "https" => Some(443), 25 | "ws" => Some(80), 26 | "wss" => Some(443), 27 | "amqp" => Some(5672), 28 | "amqps" => Some(5671), 29 | "sb" => Some(5671), 30 | "mqtt" => Some(1883), 31 | "mqtts" => Some(8883), 32 | _ => None, 33 | } 34 | } else { 35 | None 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "actix-codec", 4 | "actix-connect", 5 | "actix-ioframe", 6 | "actix-rt", 7 | "actix-macros", 8 | "actix-service", 9 | "actix-server", 10 | "actix-testing", 11 | "actix-threadpool", 12 | "actix-tls", 13 | "actix-tracing", 14 | "actix-utils", 15 | "router", 16 | "string", 17 | ] 18 | 19 | [patch.crates-io] 20 | actix-codec = { path = "actix-codec" } 21 | actix-connect = { path = "actix-connect" } 22 | actix-ioframe = { path = "actix-ioframe" } 23 | actix-rt = { path = "actix-rt" } 24 | actix-macros = { path = "actix-macros" } 25 | actix-server = { path = "actix-server" } 26 | actix-service = { path = "actix-service" } 27 | actix-testing = { path = "actix-testing" } 28 | actix-threadpool = { path = "actix-threadpool" } 29 | actix-tls = { path = "actix-tls" } 30 | actix-tracing = { path = "actix-tracing" } 31 | actix-utils = { path = "actix-utils" } 32 | actix-router = { path = "router" } 33 | bytestring = { path = "string" } 34 | -------------------------------------------------------------------------------- /.github/workflows/macos.yml: -------------------------------------------------------------------------------- 1 | name: CI (macOS) 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build_and_test: 7 | strategy: 8 | fail-fast: false 9 | matrix: 10 | version: 11 | - stable 12 | - nightly 13 | 14 | name: ${{ matrix.version }} - x86_64-apple-darwin 15 | runs-on: macos-latest 16 | 17 | steps: 18 | - uses: actions/checkout@master 19 | 20 | - name: Install ${{ matrix.version }} 21 | uses: actions-rs/toolchain@v1 22 | with: 23 | toolchain: ${{ matrix.version }}-x86_64-apple-darwin 24 | profile: minimal 25 | override: true 26 | 27 | - name: check build 28 | uses: actions-rs/cargo@v1 29 | with: 30 | command: check 31 | args: --all --bins --examples --tests 32 | 33 | - name: tests 34 | uses: actions-rs/cargo@v1 35 | with: 36 | command: test 37 | args: --all --all-features --no-fail-fast -- --nocapture 38 | -------------------------------------------------------------------------------- /router/CHANGES.md: -------------------------------------------------------------------------------- 1 | # Changes 2 | 3 | ## [0.2.4] - 2019-12-31 4 | 5 | * Add `ResourceDef::resource_path_named()` path generation method 6 | 7 | ## [0.2.3] - 2019-12-25 8 | 9 | * Add impl `IntoPattern` for `&String` 10 | 11 | ## [0.2.2] - 2019-12-25 12 | 13 | * Use `IntoPattern` for `RouterBuilder::path()` 14 | 15 | ## [0.2.1] - 2019-12-25 16 | 17 | * Add `IntoPattern` trait 18 | 19 | * Add multi-pattern resources 20 | 21 | ## [0.2.0] - 2019-12-07 22 | 23 | * Update http to 0.2 24 | 25 | * Update regex to 1.3 26 | 27 | * Use bytestring instead of string 28 | 29 | ## [0.1.5] - 2019-05-15 30 | 31 | * Remove debug prints 32 | 33 | ## [0.1.4] - 2019-05-15 34 | 35 | * Fix checked resource match 36 | 37 | ## [0.1.3] - 2019-04-22 38 | 39 | * Added support for `remainder match` (i.e "/path/{tail}*") 40 | 41 | ## [0.1.2] - 2019-04-07 42 | 43 | * Export `Quoter` type 44 | 45 | * Allow to reset `Path` instance 46 | 47 | ## [0.1.1] - 2019-04-03 48 | 49 | * Get dynamic segment by name instead of iterator. 50 | 51 | ## [0.1.0] - 2019-03-09 52 | 53 | * Initial release 54 | -------------------------------------------------------------------------------- /actix-server/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "actix-server" 3 | version = "1.0.1" 4 | authors = ["Nikolay Kim "] 5 | description = "Actix server - General purpose tcp server" 6 | keywords = ["network", "framework", "async", "futures"] 7 | homepage = "https://actix.rs" 8 | repository = "https://github.com/actix/actix-net.git" 9 | documentation = "https://docs.rs/actix-server/" 10 | categories = ["network-programming", "asynchronous"] 11 | license = "MIT/Apache-2.0" 12 | exclude = [".gitignore", ".travis.yml", ".cargo/config", "appveyor.yml"] 13 | edition = "2018" 14 | workspace = ".." 15 | 16 | [lib] 17 | name = "actix_server" 18 | path = "src/lib.rs" 19 | 20 | [features] 21 | default = [] 22 | 23 | [dependencies] 24 | actix-service = "1.0.1" 25 | actix-rt = "1.0.0" 26 | actix-codec = "0.2.0" 27 | actix-utils = "1.0.4" 28 | 29 | log = "0.4" 30 | num_cpus = "1.11" 31 | mio = "0.6.19" 32 | net2 = "0.2" 33 | futures = "0.3.1" 34 | slab = "0.4" 35 | 36 | # unix domain sockets 37 | mio-uds = { version = "0.6.7" } 38 | 39 | [dev-dependencies] 40 | bytes = "0.5" 41 | env_logger = "0.7" 42 | actix-testing = "1.0.0" -------------------------------------------------------------------------------- /actix-tls/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! SSL Services 2 | #![deny(rust_2018_idioms, warnings)] 3 | #![allow(clippy::type_complexity)] 4 | 5 | use std::sync::atomic::{AtomicUsize, Ordering}; 6 | 7 | use actix_utils::counter::Counter; 8 | 9 | #[cfg(feature = "openssl")] 10 | pub mod openssl; 11 | 12 | #[cfg(feature = "rustls")] 13 | pub mod rustls; 14 | 15 | #[cfg(feature = "nativetls")] 16 | pub mod nativetls; 17 | 18 | /// Sets the maximum per-worker concurrent ssl connection establish process. 19 | /// 20 | /// All listeners will stop accepting connections when this limit is 21 | /// reached. It can be used to limit the global SSL CPU usage. 22 | /// 23 | /// By default max connections is set to a 256. 24 | pub fn max_concurrent_ssl_connect(num: usize) { 25 | MAX_CONN.store(num, Ordering::Relaxed); 26 | } 27 | 28 | pub(crate) static MAX_CONN: AtomicUsize = AtomicUsize::new(256); 29 | 30 | thread_local! { 31 | static MAX_CONN_COUNTER: Counter = Counter::new(MAX_CONN.load(Ordering::Relaxed)); 32 | } 33 | 34 | /// Ssl error combinded with service error. 35 | #[derive(Debug)] 36 | pub enum SslError { 37 | Ssl(E1), 38 | Service(E2), 39 | } 40 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 Nikolay Kim 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /actix-utils/src/cell.rs: -------------------------------------------------------------------------------- 1 | //! Custom cell impl 2 | 3 | use std::cell::UnsafeCell; 4 | use std::fmt; 5 | use std::rc::Rc; 6 | 7 | pub(crate) struct Cell { 8 | pub(crate) inner: Rc>, 9 | } 10 | 11 | impl Clone for Cell { 12 | fn clone(&self) -> Self { 13 | Self { 14 | inner: self.inner.clone(), 15 | } 16 | } 17 | } 18 | 19 | impl fmt::Debug for Cell { 20 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 21 | self.inner.fmt(f) 22 | } 23 | } 24 | 25 | impl Cell { 26 | pub(crate) fn new(inner: T) -> Self { 27 | Self { 28 | inner: Rc::new(UnsafeCell::new(inner)), 29 | } 30 | } 31 | 32 | pub(crate) fn strong_count(&self) -> usize { 33 | Rc::strong_count(&self.inner) 34 | } 35 | 36 | pub(crate) fn get_ref(&self) -> &T { 37 | unsafe { &*self.inner.as_ref().get() } 38 | } 39 | 40 | pub(crate) fn get_mut(&mut self) -> &mut T { 41 | unsafe { &mut *self.inner.as_ref().get() } 42 | } 43 | 44 | #[allow(clippy::mut_from_ref)] 45 | pub(crate) unsafe fn get_mut_unsafe(&self) -> &mut T { 46 | &mut *self.inner.as_ref().get() 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /examples/ssl.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | use std::sync::{ 3 | atomic::{AtomicUsize, Ordering}, 4 | Arc, 5 | }; 6 | 7 | use actix_rt::System; 8 | use actix_server::{ssl, Server}; 9 | use actix_service::NewService; 10 | use futures::future; 11 | use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod}; 12 | 13 | #[derive(Debug)] 14 | struct ServiceState { 15 | num: Arc, 16 | } 17 | 18 | fn main() -> io::Result<()> { 19 | let sys = System::new("test"); 20 | 21 | // load ssl keys 22 | let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap(); 23 | builder 24 | .set_private_key_file("./examples/key.pem", SslFiletype::PEM) 25 | .unwrap(); 26 | builder 27 | .set_certificate_chain_file("./examples/cert.pem") 28 | .unwrap(); 29 | 30 | let num = Arc::new(AtomicUsize::new(0)); 31 | let openssl = ssl::OpensslAcceptor::new(builder.build()); 32 | 33 | // server start mutiple workers, it runs supplied `Fn` in each worker. 34 | Server::build() 35 | .bind("test-ssl", "0.0.0.0:8443", move || { 36 | let num = num.clone(); 37 | 38 | // configure service 39 | openssl 40 | .clone() 41 | .map_err(|e| println!("Openssl error: {}", e)) 42 | .and_then(move |_| { 43 | let num = num.fetch_add(1, Ordering::Relaxed); 44 | println!("got ssl connection {:?}", num); 45 | future::ok(()) 46 | }) 47 | })? 48 | .start(); 49 | 50 | sys.run() 51 | } 52 | -------------------------------------------------------------------------------- /actix-service/src/cell.rs: -------------------------------------------------------------------------------- 1 | //! Custom cell impl, internal use only 2 | use std::task::{Context, Poll}; 3 | use std::{cell::UnsafeCell, fmt, rc::Rc}; 4 | 5 | pub(crate) struct Cell { 6 | inner: Rc>, 7 | } 8 | 9 | impl Clone for Cell { 10 | fn clone(&self) -> Self { 11 | Self { 12 | inner: self.inner.clone(), 13 | } 14 | } 15 | } 16 | 17 | impl fmt::Debug for Cell { 18 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 19 | self.inner.fmt(f) 20 | } 21 | } 22 | 23 | impl Cell { 24 | pub(crate) fn new(inner: T) -> Self { 25 | Self { 26 | inner: Rc::new(UnsafeCell::new(inner)), 27 | } 28 | } 29 | 30 | pub(crate) fn get_ref(&self) -> &T { 31 | unsafe { &*self.inner.as_ref().get() } 32 | } 33 | 34 | pub(crate) fn get_mut(&mut self) -> &mut T { 35 | unsafe { &mut *self.inner.as_ref().get() } 36 | } 37 | 38 | #[allow(clippy::mut_from_ref)] 39 | pub(crate) unsafe fn get_mut_unsafe(&self) -> &mut T { 40 | &mut *self.inner.as_ref().get() 41 | } 42 | } 43 | 44 | impl crate::Service for Cell { 45 | type Request = T::Request; 46 | type Response = T::Response; 47 | type Error = T::Error; 48 | type Future = T::Future; 49 | 50 | fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { 51 | self.get_mut().poll_ready(cx) 52 | } 53 | 54 | fn call(&mut self, req: Self::Request) -> Self::Future { 55 | self.get_mut().call(req) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /actix-connect/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "actix-connect" 3 | version = "1.0.2" 4 | authors = ["Nikolay Kim "] 5 | description = "Actix connect - tcp connector service" 6 | keywords = ["network", "framework", "async", "futures"] 7 | homepage = "https://actix.rs" 8 | repository = "https://github.com/actix/actix-net.git" 9 | documentation = "https://docs.rs/actix-connect/" 10 | categories = ["network-programming", "asynchronous"] 11 | license = "MIT/Apache-2.0" 12 | edition = "2018" 13 | 14 | [package.metadata.docs.rs] 15 | features = ["openssl", "rustls", "uri"] 16 | 17 | [lib] 18 | name = "actix_connect" 19 | path = "src/lib.rs" 20 | 21 | [features] 22 | default = ["uri"] 23 | 24 | # openssl 25 | openssl = ["open-ssl", "tokio-openssl"] 26 | 27 | # rustls 28 | rustls = ["rust-tls", "tokio-rustls", "webpki"] 29 | 30 | # support http::Uri as connect address 31 | uri = ["http"] 32 | 33 | [dependencies] 34 | actix-service = "1.0.3" 35 | actix-codec = "0.2.0" 36 | actix-utils = "1.0.6" 37 | actix-rt = "1.0.0" 38 | derive_more = "0.99.2" 39 | either = "1.5.3" 40 | futures = "0.3.1" 41 | http = { version = "0.2.0", optional = true } 42 | log = "0.4" 43 | trust-dns-proto = "=0.18.0-alpha.2" 44 | trust-dns-resolver = "=0.18.0-alpha.2" 45 | 46 | # openssl 47 | open-ssl = { version="0.10", package = "openssl", optional = true } 48 | tokio-openssl = { version = "0.4.0", optional = true } 49 | 50 | # rustls 51 | rust-tls = { version = "0.16.0", package = "rustls", optional = true } 52 | tokio-rustls = { version = "0.12.0", optional = true } 53 | webpki = { version = "0.21", optional = true } 54 | 55 | [dev-dependencies] 56 | bytes = "0.5.3" 57 | actix-testing = { version="1.0.0" } 58 | -------------------------------------------------------------------------------- /actix-ioframe/src/error.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | use actix_codec::{Decoder, Encoder}; 4 | 5 | /// Framed service errors 6 | pub enum ServiceError { 7 | /// Inner service error 8 | Service(E), 9 | /// Encoder parse error 10 | Encoder(::Error), 11 | /// Decoder parse error 12 | Decoder(::Error), 13 | } 14 | 15 | impl From for ServiceError { 16 | fn from(err: E) -> Self { 17 | ServiceError::Service(err) 18 | } 19 | } 20 | 21 | impl fmt::Debug for ServiceError 22 | where 23 | E: fmt::Debug, 24 | ::Error: fmt::Debug, 25 | ::Error: fmt::Debug, 26 | { 27 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { 28 | match *self { 29 | ServiceError::Service(ref e) => write!(fmt, "ServiceError::Service({:?})", e), 30 | ServiceError::Encoder(ref e) => write!(fmt, "ServiceError::Encoder({:?})", e), 31 | ServiceError::Decoder(ref e) => write!(fmt, "ServiceError::Encoder({:?})", e), 32 | } 33 | } 34 | } 35 | 36 | impl fmt::Display for ServiceError 37 | where 38 | E: fmt::Display, 39 | ::Error: fmt::Debug, 40 | ::Error: fmt::Debug, 41 | { 42 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { 43 | match *self { 44 | ServiceError::Service(ref e) => write!(fmt, "{}", e), 45 | ServiceError::Encoder(ref e) => write!(fmt, "{:?}", e), 46 | ServiceError::Decoder(ref e) => write!(fmt, "{:?}", e), 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /.github/workflows/windows-mingw.yml: -------------------------------------------------------------------------------- 1 | name: CI (Windows-mingw) 2 | 3 | on: [push, pull_request] 4 | 5 | env: 6 | OPENSSL_DIR: d:\a\_temp\msys\msys64\usr 7 | 8 | jobs: 9 | build_and_test: 10 | strategy: 11 | fail-fast: false 12 | matrix: 13 | version: 14 | - stable 15 | - nightly 16 | 17 | name: ${{ matrix.version }} - x86_64-pc-windows-gnu 18 | runs-on: windows-latest 19 | 20 | steps: 21 | - uses: actions/checkout@master 22 | 23 | - name: Install ${{ matrix.version }} 24 | uses: actions-rs/toolchain@v1 25 | with: 26 | toolchain: ${{ matrix.version }}-x86_64-pc-windows-gnu 27 | profile: minimal 28 | override: true 29 | 30 | - name: Install MSYS2 31 | uses: numworks/setup-msys2@v1 32 | 33 | - name: Install OpenSSL 34 | run: | 35 | msys2do pacman --noconfirm -S openssl-devel pkg-config 36 | 37 | - name: Copy and check libs 38 | run: | 39 | Copy-Item d:\a\_temp\msys\msys64\usr\lib\libssl.dll.a d:\a\_temp\msys\msys64\usr\lib\libssl.dll 40 | Copy-Item d:\a\_temp\msys\msys64\usr\lib\libcrypto.dll.a d:\a\_temp\msys\msys64\usr\lib\libcrypto.dll 41 | Get-ChildItem d:\a\_temp\msys\msys64\usr\lib 42 | Get-ChildItem d:\a\_temp\msys\msys64\usr 43 | 44 | - name: check build 45 | uses: actions-rs/cargo@v1 46 | with: 47 | command: check 48 | args: --all --bins --examples --tests 49 | 50 | - name: tests 51 | uses: actions-rs/cargo@v1 52 | with: 53 | command: test 54 | args: --all --all-features --no-fail-fast -- --nocapture 55 | -------------------------------------------------------------------------------- /actix-tls/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "actix-tls" 3 | version = "1.0.0" 4 | authors = ["Nikolay Kim "] 5 | description = "Actix tls services" 6 | keywords = ["network", "framework", "async", "futures"] 7 | homepage = "https://actix.rs" 8 | repository = "https://github.com/actix/actix-net.git" 9 | documentation = "https://docs.rs/actix-tls/" 10 | categories = ["network-programming", "asynchronous"] 11 | license = "MIT/Apache-2.0" 12 | edition = "2018" 13 | workspace = ".." 14 | 15 | [package.metadata.docs.rs] 16 | features = ["openssl", "rustls", "nativetls"] 17 | 18 | [lib] 19 | name = "actix_tls" 20 | path = "src/lib.rs" 21 | 22 | [features] 23 | default = [] 24 | 25 | # openssl 26 | openssl = ["open-ssl", "tokio-openssl"] 27 | 28 | # rustls 29 | rustls = ["rust-tls", "webpki", "webpki-roots", "tokio-rustls"] 30 | 31 | # nativetls 32 | nativetls = ["native-tls", "tokio-tls"] 33 | 34 | [dependencies] 35 | actix-service = "1.0.0" 36 | actix-codec = "0.2.0" 37 | actix-utils = "1.0.0" 38 | actix-rt = "1.0.0" 39 | derive_more = "0.99.2" 40 | either = "1.5.2" 41 | futures = "0.3.1" 42 | log = "0.4" 43 | 44 | # openssl 45 | open-ssl = { version="0.10", package = "openssl", optional = true } 46 | tokio-openssl = { version = "0.4.0", optional = true } 47 | 48 | # rustls 49 | rust-tls = { version = "0.16.0", package = "rustls", optional = true } 50 | webpki = { version = "0.21", optional = true } 51 | webpki-roots = { version = "0.17", optional = true } 52 | tokio-rustls = { version = "0.12.0", optional = true } 53 | 54 | # native-tls 55 | native-tls = { version="0.2", optional = true } 56 | tokio-tls = { version="0.3", optional = true } 57 | 58 | [dev-dependencies] 59 | bytes = "0.5" 60 | actix-testing = { version="1.0.0" } 61 | -------------------------------------------------------------------------------- /actix-rt/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! A runtime implementation that runs everything on the current thread. 2 | #![deny(rust_2018_idioms, warnings)] 3 | #![allow(clippy::type_complexity)] 4 | 5 | #[cfg(not(test))] // Work around for rust-lang/rust#62127 6 | pub use actix_macros::{main, test}; 7 | 8 | mod arbiter; 9 | mod builder; 10 | mod runtime; 11 | mod system; 12 | 13 | pub use self::arbiter::Arbiter; 14 | pub use self::builder::{Builder, SystemRunner}; 15 | pub use self::runtime::Runtime; 16 | pub use self::system::System; 17 | 18 | #[doc(hidden)] 19 | pub use actix_threadpool as blocking; 20 | 21 | /// Spawns a future on the current arbiter. 22 | /// 23 | /// # Panics 24 | /// 25 | /// This function panics if actix system is not running. 26 | pub fn spawn(f: F) 27 | where 28 | F: futures::Future + 'static, 29 | { 30 | if !System::is_set() { 31 | panic!("System is not running"); 32 | } 33 | 34 | Arbiter::spawn(f); 35 | } 36 | 37 | /// Asynchronous signal handling 38 | pub mod signal { 39 | #[cfg(unix)] 40 | pub mod unix { 41 | pub use tokio::signal::unix::*; 42 | } 43 | pub use tokio::signal::ctrl_c; 44 | } 45 | 46 | /// TCP/UDP/Unix bindings 47 | pub mod net { 48 | pub use tokio::net::UdpSocket; 49 | pub use tokio::net::{TcpListener, TcpStream}; 50 | 51 | #[cfg(unix)] 52 | mod unix { 53 | pub use tokio::net::{UnixDatagram, UnixListener, UnixStream}; 54 | } 55 | 56 | #[cfg(unix)] 57 | pub use self::unix::*; 58 | } 59 | 60 | /// Utilities for tracking time. 61 | pub mod time { 62 | pub use tokio::time::Instant; 63 | pub use tokio::time::{delay_for, delay_until, Delay}; 64 | pub use tokio::time::{interval, interval_at, Interval}; 65 | pub use tokio::time::{timeout, Timeout}; 66 | } 67 | -------------------------------------------------------------------------------- /actix-rt/CHANGES.md: -------------------------------------------------------------------------------- 1 | # Changes 2 | 3 | ## [TBD] - [TBD] 4 | 5 | - Expose `System::is_set` to check if current system is running 6 | 7 | ## [1.0.0] - 2019-12-11 8 | 9 | * Update dependencies 10 | 11 | ## [1.0.0-alpha.3] - 2019-12-07 12 | 13 | ### Fixed 14 | 15 | * Fix compilation on non-unix platforms 16 | 17 | ### Changed 18 | 19 | * Migrate to tokio 0.2 20 | 21 | 22 | ## [1.0.0-alpha.2] - 2019-12-02 23 | 24 | Added 25 | 26 | * Export `main` and `test` attribute macros 27 | 28 | * Export `time` module (re-export of tokio-timer) 29 | 30 | * Export `net` module (re-export of tokio-net) 31 | 32 | 33 | ## [1.0.0-alpha.1] - 2019-11-22 34 | 35 | ### Changed 36 | 37 | * Migrate to std::future and tokio 0.2 38 | 39 | 40 | ## [0.2.6] - 2019-11-14 41 | 42 | ### Fixed 43 | 44 | * Fix arbiter's thread panic message. 45 | 46 | ### Added 47 | 48 | * Allow to join arbiter's thread. #60 49 | 50 | 51 | ## [0.2.5] - 2019-09-02 52 | 53 | ### Added 54 | 55 | * Add arbiter specific storage 56 | 57 | 58 | ## [0.2.4] - 2019-07-17 59 | 60 | ### Changed 61 | 62 | * Avoid a copy of the Future when initializing the Box. #29 63 | 64 | 65 | ## [0.2.3] - 2019-06-22 66 | 67 | ### Added 68 | 69 | * Allow to start System using exsiting CurrentThread Handle #22 70 | 71 | 72 | ## [0.2.2] - 2019-03-28 73 | 74 | ### Changed 75 | 76 | * Moved `blocking` module to `actix-threadpool` crate 77 | 78 | 79 | ## [0.2.1] - 2019-03-11 80 | 81 | ### Added 82 | 83 | * Added `blocking` module 84 | 85 | * Arbiter::exec_fn - execute fn on the arbiter's thread 86 | 87 | * Arbiter::exec - execute fn on the arbiter's thread and wait result 88 | 89 | 90 | ## [0.2.0] - 2019-03-06 91 | 92 | * `run` method returns `io::Result<()>` 93 | 94 | * Removed `Handle` 95 | 96 | 97 | ## [0.1.0] - 2018-12-09 98 | 99 | * Initial release 100 | -------------------------------------------------------------------------------- /actix-ioframe/tests/test_server.rs: -------------------------------------------------------------------------------- 1 | use std::cell::Cell; 2 | use std::rc::Rc; 3 | 4 | use actix_codec::BytesCodec; 5 | use actix_service::{fn_factory_with_config, fn_service, IntoService, Service}; 6 | use actix_testing::TestServer; 7 | use actix_utils::mpsc; 8 | use bytes::{Bytes, BytesMut}; 9 | use futures::future::ok; 10 | 11 | use actix_ioframe::{Builder, Connect, FactoryBuilder}; 12 | 13 | #[derive(Clone)] 14 | struct State(Option>); 15 | 16 | #[actix_rt::test] 17 | async fn test_basic() { 18 | let client_item = Rc::new(Cell::new(false)); 19 | 20 | let srv = TestServer::with(move || { 21 | FactoryBuilder::new(fn_service(|conn: Connect<_, _>| { 22 | ok(conn.codec(BytesCodec).state(State(None))) 23 | })) 24 | // echo 25 | .build(fn_service(|t: BytesMut| ok(Some(t.freeze())))) 26 | }); 27 | 28 | let item = client_item.clone(); 29 | let mut client = Builder::new(fn_service(move |conn: Connect<_, _>| { 30 | async move { 31 | let (tx, rx) = mpsc::channel(); 32 | let _ = tx.send(Bytes::from_static(b"Hello")); 33 | Ok(conn.codec(BytesCodec).out(rx).state(State(Some(tx)))) 34 | } 35 | })) 36 | .build(fn_factory_with_config(move |mut cfg: State| { 37 | let item = item.clone(); 38 | ok((move |t: BytesMut| { 39 | assert_eq!(t.freeze(), Bytes::from_static(b"Hello")); 40 | item.set(true); 41 | // drop Sender, which will close connection 42 | cfg.0.take(); 43 | ok::<_, ()>(None) 44 | }) 45 | .into_service()) 46 | })); 47 | 48 | let conn = actix_connect::default_connector() 49 | .call(actix_connect::Connect::with(String::new(), srv.addr())) 50 | .await 51 | .unwrap(); 52 | 53 | client.call(conn.into_parts().0).await.unwrap(); 54 | assert!(client_item.get()); 55 | } 56 | -------------------------------------------------------------------------------- /examples/cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIFPjCCAyYCCQDvLYiYD+jqeTANBgkqhkiG9w0BAQsFADBhMQswCQYDVQQGEwJV 3 | UzELMAkGA1UECAwCQ0ExCzAJBgNVBAcMAlNGMRAwDgYDVQQKDAdDb21wYW55MQww 4 | CgYDVQQLDANPcmcxGDAWBgNVBAMMD3d3dy5leGFtcGxlLmNvbTAeFw0xODAxMjUx 5 | NzQ2MDFaFw0xOTAxMjUxNzQ2MDFaMGExCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJD 6 | QTELMAkGA1UEBwwCU0YxEDAOBgNVBAoMB0NvbXBhbnkxDDAKBgNVBAsMA09yZzEY 7 | MBYGA1UEAwwPd3d3LmV4YW1wbGUuY29tMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A 8 | MIICCgKCAgEA2WzIA2IpVR9Tb9EFhITlxuhE5rY2a3S6qzYNzQVgSFggxXEPn8k1 9 | sQEcer5BfAP986Sck3H0FvB4Bt/I8PwOtUCmhwcc8KtB5TcGPR4fjXnrpC+MIK5U 10 | NLkwuyBDKziYzTdBj8kUFX1WxmvEHEgqToPOZfBgsS71cJAR/zOWraDLSRM54jXy 11 | voLZN4Ti9rQagQrvTQ44Vz5ycDQy7UxtbUGh1CVv69vNVr7/SOOh/Nw5FNOZWLWr 12 | odGyoec5wh9iqRZgRqiTUc6Lt7V2RWc2X2gjwST2UfI+U46Ip3oaQ7ZD4eAkoqND 13 | xdniBZAykVG3c/99ux4BAESTF8fsNch6UticBxYMuTu+ouvP0psfI9wwwNliJDmA 14 | CRUTB9AgRynbL1AzhqQoDfsb98IZfjfNOpwnwuLwpMAPhbgd5KNdZaIJ4Hb6/stI 15 | yFElOExxd3TAxF2Gshd/lq1JcNHAZ1DSXV5MvOWT/NWgXwbIzUgQ8eIi+HuDYX2U 16 | UuaB6R8tbd52H7rbUv6HrfinuSlKWqjSYLkiKHkwUpoMw8y9UycRSzs1E9nPwPTO 17 | vRXb0mNCQeBCV9FvStNVXdCUTT8LGPv87xSD2pmt7LijlE6mHLG8McfcWkzA69un 18 | CEHIFAFDimTuN7EBljc119xWFTcHMyoZAfFF+oTqwSbBGImruCxnaJECAwEAATAN 19 | BgkqhkiG9w0BAQsFAAOCAgEApavsgsn7SpPHfhDSN5iZs1ILZQRewJg0Bty0xPfk 20 | 3tynSW6bNH3nSaKbpsdmxxomthNSQgD2heOq1By9YzeOoNR+7Pk3s4FkASnf3ToI 21 | JNTUasBFFfaCG96s4Yvs8KiWS/k84yaWuU8c3Wb1jXs5Rv1qE1Uvuwat1DSGXSoD 22 | JNluuIkCsC4kWkyq5pWCGQrabWPRTWsHwC3PTcwSRBaFgYLJaR72SloHB1ot02zL 23 | d2age9dmFRFLLCBzP+D7RojBvL37qS/HR+rQ4SoQwiVc/JzaeqSe7ZbvEH9sZYEu 24 | ALowJzgbwro7oZflwTWunSeSGDSltkqKjvWvZI61pwfHKDahUTmZ5h2y67FuGEaC 25 | CIOUI8dSVSPKITxaq3JL4ze2e9/0Lt7hj19YK2uUmtMAW5Tirz4Yx5lyGH9U8Wur 26 | y/X8VPxTc4A9TMlJgkyz0hqvhbPOT/zSWB10zXh0glKAsSBryAOEDxV1UygmSir7 27 | YV8Qaq+oyKUTMc1MFq5vZ07M51EPaietn85t8V2Y+k/8XYltRp32NxsypxAJuyxh 28 | g/ko6RVTrWa1sMvz/F9LFqAdKiK5eM96lh9IU4xiLg4ob8aS/GRAA8oIFkZFhLrt 29 | tOwjIUPmEPyHWFi8dLpNuQKYalLYhuwZftG/9xV+wqhKGZO9iPrpHSYBRTap8w2y 30 | 1QU= 31 | -----END CERTIFICATE----- 32 | -------------------------------------------------------------------------------- /actix-connect/CHANGES.md: -------------------------------------------------------------------------------- 1 | # Changes 2 | 3 | ## [1.0.2] - 2020-01-15 4 | 5 | * Fix actix-service 1.0.3 compatibility 6 | 7 | ## [1.0.1] - 2019-12-15 8 | 9 | * Fix trust-dns-resolver compilation 10 | 11 | ## [1.0.0] - 2019-12-11 12 | 13 | * Release 14 | 15 | ## [1.0.0-alpha.3] - 2019-12-07 16 | 17 | ### Changed 18 | 19 | * Migrate to tokio 0.2 20 | 21 | 22 | ## [1.0.0-alpha.2] - 2019-12-02 23 | 24 | ### Changed 25 | 26 | * Migrated to `std::future` 27 | 28 | 29 | ## [0.3.0] - 2019-10-03 30 | 31 | ### Changed 32 | 33 | * Update `rustls` to 0.16 34 | * Minimum required Rust version upped to 1.37.0 35 | 36 | ## [0.2.5] - 2019-09-05 37 | 38 | * Add `TcpConnectService` 39 | 40 | ## [0.2.4] - 2019-09-02 41 | 42 | * Use arbiter's storage for default async resolver 43 | 44 | ## [0.2.3] - 2019-08-05 45 | 46 | * Add `ConnectService` and `OpensslConnectService` 47 | 48 | ## [0.2.2] - 2019-07-24 49 | 50 | * Add `rustls` support 51 | 52 | ## [0.2.1] - 2019-07-17 53 | 54 | ### Added 55 | 56 | * Expose Connect addrs #30 57 | 58 | ### Changed 59 | 60 | * Update `derive_more` to 0.15 61 | 62 | 63 | ## [0.2.0] - 2019-05-12 64 | 65 | ### Changed 66 | 67 | * Upgrade to actix-service 0.4 68 | 69 | 70 | ## [0.1.5] - 2019-04-19 71 | 72 | ### Added 73 | 74 | * `Connect::set_addr()` 75 | 76 | ### Changed 77 | 78 | * Use trust-dns-resolver 0.11.0 79 | 80 | 81 | ## [0.1.4] - 2019-04-12 82 | 83 | ### Changed 84 | 85 | * Do not start default resolver immediately for default connector. 86 | 87 | 88 | ## [0.1.3] - 2019-04-11 89 | 90 | ### Changed 91 | 92 | * Start trust-dns default resolver on first use 93 | 94 | ## [0.1.2] - 2019-04-04 95 | 96 | ### Added 97 | 98 | * Log error if dns system config could not be loaded. 99 | 100 | ### Changed 101 | 102 | * Rename connect Connector to TcpConnector #10 103 | 104 | 105 | ## [0.1.1] - 2019-03-15 106 | 107 | ### Fixed 108 | 109 | * Fix error handling for single address 110 | 111 | 112 | ## [0.1.0] - 2019-03-14 113 | 114 | * Refactor resolver and connector services 115 | 116 | * Rename crate 117 | -------------------------------------------------------------------------------- /actix-utils/src/counter.rs: -------------------------------------------------------------------------------- 1 | use std::cell::Cell; 2 | use std::rc::Rc; 3 | use std::task; 4 | 5 | use crate::task::LocalWaker; 6 | 7 | #[derive(Clone)] 8 | /// Simple counter with ability to notify task on reaching specific number 9 | /// 10 | /// Counter could be cloned, total ncount is shared across all clones. 11 | pub struct Counter(Rc); 12 | 13 | struct CounterInner { 14 | count: Cell, 15 | capacity: usize, 16 | task: LocalWaker, 17 | } 18 | 19 | impl Counter { 20 | /// Create `Counter` instance and set max value. 21 | pub fn new(capacity: usize) -> Self { 22 | Counter(Rc::new(CounterInner { 23 | capacity, 24 | count: Cell::new(0), 25 | task: LocalWaker::new(), 26 | })) 27 | } 28 | 29 | /// Get counter guard. 30 | pub fn get(&self) -> CounterGuard { 31 | CounterGuard::new(self.0.clone()) 32 | } 33 | 34 | /// Check if counter is not at capacity. If counter at capacity 35 | /// it registers notification for current task. 36 | pub fn available(&self, cx: &mut task::Context<'_>) -> bool { 37 | self.0.available(cx) 38 | } 39 | 40 | /// Get total number of acquired counts 41 | pub fn total(&self) -> usize { 42 | self.0.count.get() 43 | } 44 | } 45 | 46 | pub struct CounterGuard(Rc); 47 | 48 | impl CounterGuard { 49 | fn new(inner: Rc) -> Self { 50 | inner.inc(); 51 | CounterGuard(inner) 52 | } 53 | } 54 | 55 | impl Unpin for CounterGuard {} 56 | 57 | impl Drop for CounterGuard { 58 | fn drop(&mut self) { 59 | self.0.dec(); 60 | } 61 | } 62 | 63 | impl CounterInner { 64 | fn inc(&self) { 65 | self.count.set(self.count.get() + 1); 66 | } 67 | 68 | fn dec(&self) { 69 | let num = self.count.get(); 70 | self.count.set(num - 1); 71 | if num == self.capacity { 72 | self.task.wake(); 73 | } 74 | } 75 | 76 | fn available(&self, cx: &mut task::Context<'_>) -> bool { 77 | if self.count.get() < self.capacity { 78 | true 79 | } else { 80 | self.task.register(cx.waker()); 81 | false 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /.github/workflows/windows.yml: -------------------------------------------------------------------------------- 1 | name: CI (Windows) 2 | 3 | on: [push, pull_request] 4 | 5 | env: 6 | VCPKGRS_DYNAMIC: 1 7 | 8 | jobs: 9 | build_and_test: 10 | strategy: 11 | fail-fast: false 12 | matrix: 13 | version: 14 | - stable 15 | - nightly 16 | target: 17 | - x86_64-pc-windows-msvc 18 | - i686-pc-windows-msvc 19 | 20 | name: ${{ matrix.version }} - ${{ matrix.target }} 21 | runs-on: windows-latest 22 | 23 | steps: 24 | - uses: actions/checkout@master 25 | 26 | - name: Install ${{ matrix.version }} 27 | uses: actions-rs/toolchain@v1 28 | with: 29 | toolchain: ${{ matrix.version }}-${{ matrix.target }} 30 | profile: minimal 31 | override: true 32 | 33 | - name: Install OpenSSL (x64) 34 | if: matrix.target == 'x86_64-pc-windows-msvc' 35 | run: | 36 | vcpkg integrate install 37 | vcpkg install openssl:x64-windows 38 | Get-ChildItem C:\vcpkg\installed\x64-windows\bin 39 | Get-ChildItem C:\vcpkg\installed\x64-windows\lib 40 | Copy-Item C:\vcpkg\installed\x64-windows\bin\libcrypto-1_1-x64.dll C:\vcpkg\installed\x64-windows\bin\libcrypto.dll 41 | Copy-Item C:\vcpkg\installed\x64-windows\bin\libssl-1_1-x64.dll C:\vcpkg\installed\x64-windows\bin\libssl.dll 42 | 43 | - name: Install OpenSSL (x86) 44 | if: matrix.target == 'i686-pc-windows-msvc' 45 | run: | 46 | vcpkg integrate install 47 | vcpkg install openssl:x86-windows 48 | Get-ChildItem C:\vcpkg\installed\x86-windows\bin 49 | Get-ChildItem C:\vcpkg\installed\x86-windows\lib 50 | Copy-Item C:\vcpkg\installed\x86-windows\bin\libcrypto-1_1.dll C:\vcpkg\installed\x86-windows\bin\libcrypto.dll 51 | Copy-Item C:\vcpkg\installed\x86-windows\bin\libssl-1_1.dll C:\vcpkg\installed\x86-windows\bin\libssl.dll 52 | 53 | - name: check build 54 | uses: actions-rs/cargo@v1 55 | with: 56 | command: check 57 | args: --all --bins --examples --tests 58 | 59 | - name: tests 60 | uses: actions-rs/cargo@v1 61 | with: 62 | command: test 63 | args: --all --all-features --no-fail-fast -- --nocapture 64 | -------------------------------------------------------------------------------- /actix-service/src/map_init_err.rs: -------------------------------------------------------------------------------- 1 | use std::future::Future; 2 | use std::marker::PhantomData; 3 | use std::pin::Pin; 4 | use std::task::{Context, Poll}; 5 | 6 | use super::ServiceFactory; 7 | 8 | /// `MapInitErr` service combinator 9 | pub struct MapInitErr { 10 | a: A, 11 | f: F, 12 | e: PhantomData, 13 | } 14 | 15 | impl MapInitErr 16 | where 17 | A: ServiceFactory, 18 | F: Fn(A::InitError) -> E, 19 | { 20 | /// Create new `MapInitErr` combinator 21 | pub(crate) fn new(a: A, f: F) -> Self { 22 | Self { 23 | a, 24 | f, 25 | e: PhantomData, 26 | } 27 | } 28 | } 29 | 30 | impl Clone for MapInitErr 31 | where 32 | A: Clone, 33 | F: Clone, 34 | { 35 | fn clone(&self) -> Self { 36 | Self { 37 | a: self.a.clone(), 38 | f: self.f.clone(), 39 | e: PhantomData, 40 | } 41 | } 42 | } 43 | 44 | impl ServiceFactory for MapInitErr 45 | where 46 | A: ServiceFactory, 47 | F: Fn(A::InitError) -> E + Clone, 48 | { 49 | type Request = A::Request; 50 | type Response = A::Response; 51 | type Error = A::Error; 52 | 53 | type Config = A::Config; 54 | type Service = A::Service; 55 | type InitError = E; 56 | type Future = MapInitErrFuture; 57 | 58 | fn new_service(&self, cfg: A::Config) -> Self::Future { 59 | MapInitErrFuture::new(self.a.new_service(cfg), self.f.clone()) 60 | } 61 | } 62 | 63 | #[pin_project::pin_project] 64 | pub struct MapInitErrFuture 65 | where 66 | A: ServiceFactory, 67 | F: Fn(A::InitError) -> E, 68 | { 69 | f: F, 70 | #[pin] 71 | fut: A::Future, 72 | } 73 | 74 | impl MapInitErrFuture 75 | where 76 | A: ServiceFactory, 77 | F: Fn(A::InitError) -> E, 78 | { 79 | fn new(fut: A::Future, f: F) -> Self { 80 | MapInitErrFuture { f, fut } 81 | } 82 | } 83 | 84 | impl Future for MapInitErrFuture 85 | where 86 | A: ServiceFactory, 87 | F: Fn(A::InitError) -> E, 88 | { 89 | type Output = Result; 90 | 91 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 92 | let this = self.project(); 93 | this.fut.poll(cx).map_err(this.f) 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /actix-utils/src/stream.rs: -------------------------------------------------------------------------------- 1 | use std::future::Future; 2 | use std::pin::Pin; 3 | use std::task::{Context, Poll}; 4 | 5 | use actix_service::{IntoService, Service}; 6 | use futures::{FutureExt, Stream}; 7 | 8 | use crate::mpsc; 9 | 10 | #[pin_project::pin_project] 11 | pub struct Dispatcher 12 | where 13 | S: Stream, 14 | T: Service + 'static, 15 | { 16 | #[pin] 17 | stream: S, 18 | service: T, 19 | err_rx: mpsc::Receiver, 20 | err_tx: mpsc::Sender, 21 | } 22 | 23 | impl Dispatcher 24 | where 25 | S: Stream, 26 | T: Service + 'static, 27 | { 28 | pub fn new(stream: S, service: F) -> Self 29 | where 30 | F: IntoService, 31 | { 32 | let (err_tx, err_rx) = mpsc::channel(); 33 | Dispatcher { 34 | err_rx, 35 | err_tx, 36 | stream, 37 | service: service.into_service(), 38 | } 39 | } 40 | } 41 | 42 | impl Future for Dispatcher 43 | where 44 | S: Stream, 45 | T: Service + 'static, 46 | { 47 | type Output = Result<(), T::Error>; 48 | 49 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 50 | let mut this = self.as_mut().project(); 51 | 52 | if let Poll::Ready(Some(e)) = Pin::new(&mut this.err_rx).poll_next(cx) { 53 | return Poll::Ready(Err(e)); 54 | } 55 | 56 | loop { 57 | return match this.service.poll_ready(cx)? { 58 | Poll::Ready(_) => match this.stream.poll_next(cx) { 59 | Poll::Ready(Some(item)) => { 60 | let stop = this.err_tx.clone(); 61 | actix_rt::spawn(this.service.call(item).map(move |res| { 62 | if let Err(e) = res { 63 | let _ = stop.send(e); 64 | } 65 | })); 66 | this = self.as_mut().project(); 67 | continue; 68 | } 69 | Poll::Pending => Poll::Pending, 70 | Poll::Ready(None) => Poll::Ready(Ok(())), 71 | }, 72 | Poll::Pending => Poll::Pending, 73 | }; 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /actix-service/src/transform_err.rs: -------------------------------------------------------------------------------- 1 | use std::future::Future; 2 | use std::marker::PhantomData; 3 | use std::pin::Pin; 4 | use std::task::{Context, Poll}; 5 | 6 | use super::Transform; 7 | 8 | /// Transform for the `map_init_err` combinator, changing the type of a new 9 | /// transform's init error. 10 | /// 11 | /// This is created by the `Transform::map_init_err` method. 12 | pub struct TransformMapInitErr { 13 | t: T, 14 | f: F, 15 | e: PhantomData<(S, E)>, 16 | } 17 | 18 | impl TransformMapInitErr { 19 | pub(crate) fn new(t: T, f: F) -> Self 20 | where 21 | T: Transform, 22 | F: Fn(T::InitError) -> E, 23 | { 24 | Self { 25 | t, 26 | f, 27 | e: PhantomData, 28 | } 29 | } 30 | } 31 | 32 | impl Clone for TransformMapInitErr 33 | where 34 | T: Clone, 35 | F: Clone, 36 | { 37 | fn clone(&self) -> Self { 38 | Self { 39 | t: self.t.clone(), 40 | f: self.f.clone(), 41 | e: PhantomData, 42 | } 43 | } 44 | } 45 | 46 | impl Transform for TransformMapInitErr 47 | where 48 | T: Transform, 49 | F: Fn(T::InitError) -> E + Clone, 50 | { 51 | type Request = T::Request; 52 | type Response = T::Response; 53 | type Error = T::Error; 54 | type Transform = T::Transform; 55 | 56 | type InitError = E; 57 | type Future = TransformMapInitErrFuture; 58 | 59 | fn new_transform(&self, service: S) -> Self::Future { 60 | TransformMapInitErrFuture { 61 | fut: self.t.new_transform(service), 62 | f: self.f.clone(), 63 | } 64 | } 65 | } 66 | 67 | #[pin_project::pin_project] 68 | pub struct TransformMapInitErrFuture 69 | where 70 | T: Transform, 71 | F: Fn(T::InitError) -> E, 72 | { 73 | #[pin] 74 | fut: T::Future, 75 | f: F, 76 | } 77 | 78 | impl Future for TransformMapInitErrFuture 79 | where 80 | T: Transform, 81 | F: Fn(T::InitError) -> E + Clone, 82 | { 83 | type Output = Result; 84 | 85 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 86 | let this = self.project(); 87 | if let Poll::Ready(res) = this.fut.poll(cx) { 88 | Poll::Ready(res.map_err(this.f)) 89 | } else { 90 | Poll::Pending 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /.github/workflows/linux.yml: -------------------------------------------------------------------------------- 1 | name: CI (Linux) 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build_and_test: 7 | strategy: 8 | fail-fast: false 9 | matrix: 10 | version: 11 | - 1.39.0 12 | - stable 13 | - nightly 14 | 15 | name: ${{ matrix.version }} - x86_64-unknown-linux-gnu 16 | runs-on: ubuntu-latest 17 | 18 | steps: 19 | - uses: actions/checkout@master 20 | 21 | - name: Install ${{ matrix.version }} 22 | uses: actions-rs/toolchain@v1 23 | with: 24 | toolchain: ${{ matrix.version }}-x86_64-unknown-linux-gnu 25 | profile: minimal 26 | override: true 27 | 28 | - name: Generate Cargo.lock 29 | uses: actions-rs/cargo@v1 30 | with: 31 | command: generate-lockfile 32 | - name: Cache cargo registry 33 | uses: actions/cache@v1 34 | with: 35 | path: ~/.cargo/registry 36 | key: ${{ matrix.version }}-x86_64-unknown-linux-gnu-cargo-registry-trimmed-${{ hashFiles('**/Cargo.lock') }} 37 | - name: Cache cargo index 38 | uses: actions/cache@v1 39 | with: 40 | path: ~/.cargo/git 41 | key: ${{ matrix.version }}-x86_64-unknown-linux-gnu-cargo-index-trimmed-${{ hashFiles('**/Cargo.lock') }} 42 | - name: Cache cargo build 43 | uses: actions/cache@v1 44 | with: 45 | path: target 46 | key: ${{ matrix.version }}-x86_64-unknown-linux-gnu-cargo-build-trimmed-${{ hashFiles('**/Cargo.lock') }} 47 | 48 | - name: check build 49 | uses: actions-rs/cargo@v1 50 | with: 51 | command: check 52 | args: --all --bins --examples --tests 53 | 54 | - name: tests 55 | uses: actions-rs/cargo@v1 56 | timeout-minutes: 40 57 | with: 58 | command: test 59 | args: --all --all-features --no-fail-fast -- --nocapture 60 | 61 | - name: Generate coverage file 62 | if: matrix.version == 'stable' && github.ref == 'refs/heads/master' 63 | run: | 64 | cargo install cargo-tarpaulin 65 | cargo tarpaulin --out Xml --workspace --all-features 66 | 67 | - name: Upload to Codecov 68 | if: matrix.version == 'stable' && github.ref == 'refs/heads/master' 69 | uses: codecov/codecov-action@v1 70 | with: 71 | token: ${{ secrets.CODECOV_TOKEN }} 72 | file: cobertura.xml 73 | 74 | - name: Clear the cargo caches 75 | run: | 76 | cargo install cargo-cache --no-default-features --features ci-autoclean 77 | cargo-cache 78 | -------------------------------------------------------------------------------- /actix-utils/src/task.rs: -------------------------------------------------------------------------------- 1 | use std::cell::UnsafeCell; 2 | use std::marker::PhantomData; 3 | use std::task::Waker; 4 | use std::{fmt, rc}; 5 | 6 | /// A synchronization primitive for task wakeup. 7 | /// 8 | /// Sometimes the task interested in a given event will change over time. 9 | /// An `LocalWaker` can coordinate concurrent notifications with the consumer 10 | /// potentially "updating" the underlying task to wake up. This is useful in 11 | /// scenarios where a computation completes in another task and wants to 12 | /// notify the consumer, but the consumer is in the process of being migrated to 13 | /// a new logical task. 14 | /// 15 | /// Consumers should call `register` before checking the result of a computation 16 | /// and producers should call `wake` after producing the computation (this 17 | /// differs from the usual `thread::park` pattern). It is also permitted for 18 | /// `wake` to be called **before** `register`. This results in a no-op. 19 | /// 20 | /// A single `AtomicWaker` may be reused for any number of calls to `register` or 21 | /// `wake`. 22 | #[derive(Default)] 23 | pub struct LocalWaker { 24 | pub(crate) waker: UnsafeCell>, 25 | _t: PhantomData>, 26 | } 27 | 28 | impl LocalWaker { 29 | /// Create an `LocalWaker`. 30 | pub fn new() -> Self { 31 | LocalWaker { 32 | waker: UnsafeCell::new(None), 33 | _t: PhantomData, 34 | } 35 | } 36 | 37 | #[inline] 38 | /// Check if waker has been registered. 39 | pub fn is_registed(&self) -> bool { 40 | unsafe { (*self.waker.get()).is_some() } 41 | } 42 | 43 | #[inline] 44 | /// Registers the waker to be notified on calls to `wake`. 45 | /// 46 | /// Returns `true` if waker was registered before. 47 | pub fn register(&self, waker: &Waker) -> bool { 48 | unsafe { 49 | let w = self.waker.get(); 50 | let is_registered = (*w).is_some(); 51 | *w = Some(waker.clone()); 52 | is_registered 53 | } 54 | } 55 | 56 | #[inline] 57 | /// Calls `wake` on the last `Waker` passed to `register`. 58 | /// 59 | /// If `register` has not been called yet, then this does nothing. 60 | pub fn wake(&self) { 61 | if let Some(waker) = self.take() { 62 | waker.wake(); 63 | } 64 | } 65 | 66 | /// Returns the last `Waker` passed to `register`, so that the user can wake it. 67 | /// 68 | /// If a waker has not been registered, this returns `None`. 69 | pub fn take(&self) -> Option { 70 | unsafe { (*self.waker.get()).take() } 71 | } 72 | } 73 | 74 | impl fmt::Debug for LocalWaker { 75 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 76 | write!(f, "LocalWaker") 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /actix-threadpool/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Thread pool for blocking operations 2 | 3 | use std::fmt; 4 | use std::future::Future; 5 | use std::pin::Pin; 6 | use std::task::{Context, Poll}; 7 | 8 | use derive_more::Display; 9 | use futures_channel::oneshot; 10 | use parking_lot::Mutex; 11 | use threadpool::ThreadPool; 12 | 13 | /// Env variable for default cpu pool size. 14 | const ENV_CPU_POOL_VAR: &str = "ACTIX_THREADPOOL"; 15 | 16 | lazy_static::lazy_static! { 17 | pub(crate) static ref DEFAULT_POOL: Mutex = { 18 | let num = std::env::var(ENV_CPU_POOL_VAR) 19 | .map_err(|_| ()) 20 | .and_then(|val| { 21 | val.parse().map_err(|_| log::warn!( 22 | "Can not parse {} value, using default", 23 | ENV_CPU_POOL_VAR, 24 | )) 25 | }) 26 | .unwrap_or_else(|_| num_cpus::get() * 5); 27 | Mutex::new( 28 | threadpool::Builder::new() 29 | .thread_name("actix-web".to_owned()) 30 | .num_threads(num) 31 | .build(), 32 | ) 33 | }; 34 | } 35 | 36 | thread_local! { 37 | static POOL: ThreadPool = { 38 | DEFAULT_POOL.lock().clone() 39 | }; 40 | } 41 | 42 | /// Blocking operation execution error 43 | #[derive(Debug, Display)] 44 | pub enum BlockingError { 45 | #[display(fmt = "{:?}", _0)] 46 | Error(E), 47 | #[display(fmt = "Thread pool is gone")] 48 | Canceled, 49 | } 50 | 51 | /// Execute blocking function on a thread pool, returns future that resolves 52 | /// to result of the function execution. 53 | pub fn run(f: F) -> CpuFuture 54 | where 55 | F: FnOnce() -> Result + Send + 'static, 56 | I: Send + 'static, 57 | E: Send + fmt::Debug + 'static, 58 | { 59 | let (tx, rx) = oneshot::channel(); 60 | POOL.with(|pool| { 61 | pool.execute(move || { 62 | if !tx.is_canceled() { 63 | let _ = tx.send(f()); 64 | } 65 | }) 66 | }); 67 | 68 | CpuFuture { rx } 69 | } 70 | 71 | /// Blocking operation completion future. It resolves with results 72 | /// of blocking function execution. 73 | pub struct CpuFuture { 74 | rx: oneshot::Receiver>, 75 | } 76 | 77 | impl Future for CpuFuture { 78 | type Output = Result>; 79 | 80 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 81 | let rx = Pin::new(&mut self.rx); 82 | let res = match rx.poll(cx) { 83 | Poll::Pending => return Poll::Pending, 84 | Poll::Ready(res) => res 85 | .map_err(|_| BlockingError::Canceled) 86 | .and_then(|res| res.map_err(BlockingError::Error)), 87 | }; 88 | Poll::Ready(res) 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /actix-macros/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Macros for use with Tokio 2 | extern crate proc_macro; 3 | 4 | use proc_macro::TokenStream; 5 | use quote::quote; 6 | 7 | /// Marks async function to be executed by actix system. 8 | /// 9 | /// ## Usage 10 | /// 11 | /// ```rust 12 | /// #[actix_rt::main] 13 | /// async fn main() { 14 | /// println!("Hello world"); 15 | /// } 16 | /// ``` 17 | #[allow(clippy::needless_doctest_main)] 18 | #[proc_macro_attribute] 19 | #[cfg(not(test))] // Work around for rust-lang/rust#62127 20 | pub fn main(_: TokenStream, item: TokenStream) -> TokenStream { 21 | let mut input = syn::parse_macro_input!(item as syn::ItemFn); 22 | let attrs = &input.attrs; 23 | let vis = &input.vis; 24 | let sig = &mut input.sig; 25 | let body = &input.block; 26 | let name = &sig.ident; 27 | 28 | if sig.asyncness.is_none() { 29 | return syn::Error::new_spanned(sig.fn_token, "only async fn is supported") 30 | .to_compile_error() 31 | .into(); 32 | } 33 | 34 | sig.asyncness = None; 35 | 36 | (quote! { 37 | #(#attrs)* 38 | #vis #sig { 39 | actix_rt::System::new(stringify!(#name)) 40 | .block_on(async move { #body }) 41 | } 42 | }) 43 | .into() 44 | } 45 | 46 | /// Marks async test function to be executed by actix runtime. 47 | /// 48 | /// ## Usage 49 | /// 50 | /// ```no_run 51 | /// #[actix_rt::test] 52 | /// async fn my_test() { 53 | /// assert!(true); 54 | /// } 55 | /// ``` 56 | #[proc_macro_attribute] 57 | pub fn test(_: TokenStream, item: TokenStream) -> TokenStream { 58 | let input = syn::parse_macro_input!(item as syn::ItemFn); 59 | 60 | let ret = &input.sig.output; 61 | let name = &input.sig.ident; 62 | let body = &input.block; 63 | let attrs = &input.attrs; 64 | let mut has_test_attr = false; 65 | 66 | for attr in attrs { 67 | if attr.path.is_ident("test") { 68 | has_test_attr = true; 69 | } 70 | } 71 | 72 | if input.sig.asyncness.is_none() { 73 | return syn::Error::new_spanned( 74 | input.sig.fn_token, 75 | format!("only async fn is supported, {}", input.sig.ident), 76 | ) 77 | .to_compile_error() 78 | .into(); 79 | } 80 | 81 | let result = if has_test_attr { 82 | quote! { 83 | #(#attrs)* 84 | fn #name() #ret { 85 | actix_rt::System::new("test") 86 | .block_on(async { #body }) 87 | } 88 | } 89 | } else { 90 | quote! { 91 | #[test] 92 | #(#attrs)* 93 | fn #name() #ret { 94 | actix_rt::System::new("test") 95 | .block_on(async { #body }) 96 | } 97 | } 98 | }; 99 | 100 | result.into() 101 | } 102 | -------------------------------------------------------------------------------- /examples/basic.rs: -------------------------------------------------------------------------------- 1 | //! simple composite service 2 | //! build: cargo run --example basic --features "ssl" 3 | //! to test: curl https://127.0.0.1:8443/ -k 4 | use std::sync::{ 5 | atomic::{AtomicUsize, Ordering}, 6 | Arc, 7 | }; 8 | use std::{env, fmt, io}; 9 | 10 | use actix_codec::{AsyncRead, AsyncWrite}; 11 | use actix_rt::System; 12 | use actix_server::{Io, Server}; 13 | use actix_service::{service_fn, NewService}; 14 | use futures::{future, Future}; 15 | use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod}; 16 | use tokio_openssl::SslAcceptorExt; 17 | 18 | /// Simple logger service, it just prints fact of the new connections 19 | fn logger( 20 | stream: T, 21 | ) -> impl Future { 22 | println!("New connection: {:?}", stream); 23 | future::ok(stream) 24 | } 25 | 26 | fn main() -> io::Result<()> { 27 | env::set_var("RUST_LOG", "actix_net=trace"); 28 | env_logger::init(); 29 | 30 | let sys = System::new("test"); 31 | 32 | // load ssl keys 33 | let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap(); 34 | builder 35 | .set_private_key_file("./examples/key.pem", SslFiletype::PEM) 36 | .unwrap(); 37 | builder 38 | .set_certificate_chain_file("./examples/cert.pem") 39 | .unwrap(); 40 | let acceptor = builder.build(); 41 | 42 | let num = Arc::new(AtomicUsize::new(0)); 43 | 44 | // bind socket address and start workers. By default server uses number of 45 | // available logical cpu as threads count. actix net start separate 46 | // instances of service pipeline in each worker. 47 | Server::build() 48 | .bind( 49 | // configure service pipeline 50 | "basic", 51 | "0.0.0.0:8443", 52 | move || { 53 | let num = num.clone(); 54 | let acceptor = acceptor.clone(); 55 | 56 | // service for converting incoming TcpStream to a SslStream 57 | service_fn(move |stream: Io| { 58 | SslAcceptorExt::accept_async(&acceptor, stream.into_parts().0) 59 | .map_err(|e| println!("Openssl error: {}", e)) 60 | }) 61 | // .and_then() combinator uses other service to convert incoming `Request` to a 62 | // `Response` and then uses that response as an input for next 63 | // service. in this case, on success we use `logger` service 64 | .and_then(logger) 65 | // Next service counts number of connections 66 | .and_then(move |_| { 67 | let num = num.fetch_add(1, Ordering::Relaxed); 68 | println!("got ssl connection {:?}", num); 69 | future::ok(()) 70 | }) 71 | }, 72 | ) 73 | .unwrap() 74 | .start(); 75 | 76 | sys.run() 77 | } 78 | -------------------------------------------------------------------------------- /actix-rt/src/runtime.rs: -------------------------------------------------------------------------------- 1 | use std::future::Future; 2 | use std::io; 3 | use tokio::{runtime, task::LocalSet}; 4 | 5 | /// Single-threaded runtime provides a way to start reactor 6 | /// and runtime on the current thread. 7 | /// 8 | /// See [module level][mod] documentation for more details. 9 | /// 10 | /// [mod]: index.html 11 | #[derive(Debug)] 12 | pub struct Runtime { 13 | local: LocalSet, 14 | rt: runtime::Runtime, 15 | } 16 | 17 | impl Runtime { 18 | #[allow(clippy::new_ret_no_self)] 19 | /// Returns a new runtime initialized with default configuration values. 20 | pub fn new() -> io::Result { 21 | let rt = runtime::Builder::new() 22 | .enable_io() 23 | .enable_time() 24 | .basic_scheduler() 25 | .build()?; 26 | 27 | Ok(Runtime { 28 | rt, 29 | local: LocalSet::new(), 30 | }) 31 | } 32 | 33 | /// Spawn a future onto the single-threaded runtime. 34 | /// 35 | /// See [module level][mod] documentation for more details. 36 | /// 37 | /// [mod]: index.html 38 | /// 39 | /// # Examples 40 | /// 41 | /// ```rust,ignore 42 | /// # use futures::{future, Future, Stream}; 43 | /// use actix_rt::Runtime; 44 | /// 45 | /// # fn dox() { 46 | /// // Create the runtime 47 | /// let mut rt = Runtime::new().unwrap(); 48 | /// 49 | /// // Spawn a future onto the runtime 50 | /// rt.spawn(future::lazy(|_| { 51 | /// println!("running on the runtime"); 52 | /// })); 53 | /// # } 54 | /// # pub fn main() {} 55 | /// ``` 56 | /// 57 | /// # Panics 58 | /// 59 | /// This function panics if the spawn fails. Failure occurs if the executor 60 | /// is currently at capacity and is unable to spawn a new future. 61 | pub fn spawn(&self, future: F) -> &Self 62 | where 63 | F: Future + 'static, 64 | { 65 | self.local.spawn_local(future); 66 | self 67 | } 68 | 69 | /// Runs the provided future, blocking the current thread until the future 70 | /// completes. 71 | /// 72 | /// This function can be used to synchronously block the current thread 73 | /// until the provided `future` has resolved either successfully or with an 74 | /// error. The result of the future is then returned from this function 75 | /// call. 76 | /// 77 | /// Note that this function will **also** execute any spawned futures on the 78 | /// current thread, but will **not** block until these other spawned futures 79 | /// have completed. Once the function returns, any uncompleted futures 80 | /// remain pending in the `Runtime` instance. These futures will not run 81 | /// until `block_on` or `run` is called again. 82 | /// 83 | /// The caller is responsible for ensuring that other spawned futures 84 | /// complete execution by calling `block_on` or `run`. 85 | pub fn block_on(&mut self, f: F) -> F::Output 86 | where 87 | F: Future + 'static, 88 | { 89 | self.local.block_on(&mut self.rt, f) 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /actix-service/src/map_config.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | 3 | use super::{IntoServiceFactory, ServiceFactory}; 4 | 5 | /// Adapt external config argument to a config for provided service factory 6 | /// 7 | /// Note that this function consumes the receiving service factory and returns 8 | /// a wrapped version of it. 9 | pub fn map_config(factory: U, f: F) -> MapConfig 10 | where 11 | T: ServiceFactory, 12 | U: IntoServiceFactory, 13 | F: Fn(C) -> T::Config, 14 | { 15 | MapConfig::new(factory.into_factory(), f) 16 | } 17 | 18 | /// Replace config with unit 19 | pub fn unit_config(factory: U) -> UnitConfig 20 | where 21 | T: ServiceFactory, 22 | U: IntoServiceFactory, 23 | { 24 | UnitConfig::new(factory.into_factory()) 25 | } 26 | 27 | /// `map_config()` adapter service factory 28 | pub struct MapConfig { 29 | a: A, 30 | f: F, 31 | e: PhantomData, 32 | } 33 | 34 | impl MapConfig { 35 | /// Create new `MapConfig` combinator 36 | pub(crate) fn new(a: A, f: F) -> Self 37 | where 38 | A: ServiceFactory, 39 | F: Fn(C) -> A::Config, 40 | { 41 | Self { 42 | a, 43 | f, 44 | e: PhantomData, 45 | } 46 | } 47 | } 48 | 49 | impl Clone for MapConfig 50 | where 51 | A: Clone, 52 | F: Clone, 53 | { 54 | fn clone(&self) -> Self { 55 | Self { 56 | a: self.a.clone(), 57 | f: self.f.clone(), 58 | e: PhantomData, 59 | } 60 | } 61 | } 62 | 63 | impl ServiceFactory for MapConfig 64 | where 65 | A: ServiceFactory, 66 | F: Fn(C) -> A::Config, 67 | { 68 | type Request = A::Request; 69 | type Response = A::Response; 70 | type Error = A::Error; 71 | 72 | type Config = C; 73 | type Service = A::Service; 74 | type InitError = A::InitError; 75 | type Future = A::Future; 76 | 77 | fn new_service(&self, cfg: C) -> Self::Future { 78 | self.a.new_service((self.f)(cfg)) 79 | } 80 | } 81 | 82 | /// `unit_config()` config combinator 83 | pub struct UnitConfig { 84 | a: A, 85 | e: PhantomData, 86 | } 87 | 88 | impl UnitConfig 89 | where 90 | A: ServiceFactory, 91 | { 92 | /// Create new `UnitConfig` combinator 93 | pub(crate) fn new(a: A) -> Self { 94 | Self { a, e: PhantomData } 95 | } 96 | } 97 | 98 | impl Clone for UnitConfig 99 | where 100 | A: Clone, 101 | { 102 | fn clone(&self) -> Self { 103 | Self { 104 | a: self.a.clone(), 105 | e: PhantomData, 106 | } 107 | } 108 | } 109 | 110 | impl ServiceFactory for UnitConfig 111 | where 112 | A: ServiceFactory, 113 | { 114 | type Request = A::Request; 115 | type Response = A::Response; 116 | type Error = A::Error; 117 | 118 | type Config = C; 119 | type Service = A::Service; 120 | type InitError = A::InitError; 121 | type Future = A::Future; 122 | 123 | fn new_service(&self, _: C) -> Self::Future { 124 | self.a.new_service(()) 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at fafhrd91@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /actix-server/src/signals.rs: -------------------------------------------------------------------------------- 1 | use std::future::Future; 2 | use std::io; 3 | use std::pin::Pin; 4 | use std::task::{Context, Poll}; 5 | 6 | use futures::future::lazy; 7 | 8 | use crate::server::Server; 9 | 10 | /// Different types of process signals 11 | #[allow(dead_code)] 12 | #[derive(PartialEq, Clone, Copy, Debug)] 13 | pub(crate) enum Signal { 14 | /// SIGHUP 15 | Hup, 16 | /// SIGINT 17 | Int, 18 | /// SIGTERM 19 | Term, 20 | /// SIGQUIT 21 | Quit, 22 | } 23 | 24 | pub(crate) struct Signals { 25 | srv: Server, 26 | #[cfg(not(unix))] 27 | stream: Pin>>>, 28 | #[cfg(unix)] 29 | streams: Vec<(Signal, actix_rt::signal::unix::Signal)>, 30 | } 31 | 32 | impl Signals { 33 | pub(crate) fn start(srv: Server) -> io::Result<()> { 34 | actix_rt::spawn(lazy(|_| { 35 | #[cfg(not(unix))] 36 | { 37 | actix_rt::spawn(Signals { 38 | srv, 39 | stream: Box::pin(actix_rt::signal::ctrl_c()), 40 | }); 41 | } 42 | #[cfg(unix)] 43 | { 44 | use actix_rt::signal::unix; 45 | 46 | let mut streams = Vec::new(); 47 | 48 | let sig_map = [ 49 | (unix::SignalKind::interrupt(), Signal::Int), 50 | (unix::SignalKind::hangup(), Signal::Hup), 51 | (unix::SignalKind::terminate(), Signal::Term), 52 | (unix::SignalKind::quit(), Signal::Quit), 53 | ]; 54 | 55 | for (kind, sig) in sig_map.iter() { 56 | match unix::signal(*kind) { 57 | Ok(stream) => streams.push((*sig, stream)), 58 | Err(e) => log::error!( 59 | "Can not initialize stream handler for {:?} err: {}", 60 | sig, 61 | e 62 | ), 63 | } 64 | } 65 | 66 | actix_rt::spawn(Signals { srv, streams }) 67 | } 68 | })); 69 | 70 | Ok(()) 71 | } 72 | } 73 | 74 | impl Future for Signals { 75 | type Output = (); 76 | 77 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 78 | #[cfg(not(unix))] 79 | match Pin::new(&mut self.stream).poll(cx) { 80 | Poll::Ready(_) => { 81 | self.srv.signal(Signal::Int); 82 | Poll::Ready(()) 83 | } 84 | Poll::Pending => return Poll::Pending, 85 | } 86 | #[cfg(unix)] 87 | { 88 | for idx in 0..self.streams.len() { 89 | loop { 90 | match self.streams[idx].1.poll_recv(cx) { 91 | Poll::Ready(None) => return Poll::Ready(()), 92 | Poll::Pending => break, 93 | Poll::Ready(Some(_)) => { 94 | let sig = self.streams[idx].0; 95 | self.srv.signal(sig); 96 | } 97 | } 98 | } 99 | } 100 | Poll::Pending 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /examples/key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIJKAIBAAKCAgEA2WzIA2IpVR9Tb9EFhITlxuhE5rY2a3S6qzYNzQVgSFggxXEP 3 | n8k1sQEcer5BfAP986Sck3H0FvB4Bt/I8PwOtUCmhwcc8KtB5TcGPR4fjXnrpC+M 4 | IK5UNLkwuyBDKziYzTdBj8kUFX1WxmvEHEgqToPOZfBgsS71cJAR/zOWraDLSRM5 5 | 4jXyvoLZN4Ti9rQagQrvTQ44Vz5ycDQy7UxtbUGh1CVv69vNVr7/SOOh/Nw5FNOZ 6 | WLWrodGyoec5wh9iqRZgRqiTUc6Lt7V2RWc2X2gjwST2UfI+U46Ip3oaQ7ZD4eAk 7 | oqNDxdniBZAykVG3c/99ux4BAESTF8fsNch6UticBxYMuTu+ouvP0psfI9wwwNli 8 | JDmACRUTB9AgRynbL1AzhqQoDfsb98IZfjfNOpwnwuLwpMAPhbgd5KNdZaIJ4Hb6 9 | /stIyFElOExxd3TAxF2Gshd/lq1JcNHAZ1DSXV5MvOWT/NWgXwbIzUgQ8eIi+HuD 10 | YX2UUuaB6R8tbd52H7rbUv6HrfinuSlKWqjSYLkiKHkwUpoMw8y9UycRSzs1E9nP 11 | wPTOvRXb0mNCQeBCV9FvStNVXdCUTT8LGPv87xSD2pmt7LijlE6mHLG8McfcWkzA 12 | 69unCEHIFAFDimTuN7EBljc119xWFTcHMyoZAfFF+oTqwSbBGImruCxnaJECAwEA 13 | AQKCAgAME3aoeXNCPxMrSri7u4Xnnk71YXl0Tm9vwvjRQlMusXZggP8VKN/KjP0/ 14 | 9AE/GhmoxqPLrLCZ9ZE1EIjgmZ9Xgde9+C8rTtfCG2RFUL7/5J2p6NonlocmxoJm 15 | YkxYwjP6ce86RTjQWL3RF3s09u0inz9/efJk5O7M6bOWMQ9VZXDlBiRY5BYvbqUR 16 | 6FeSzD4MnMbdyMRoVBeXE88gTvZk8xhB6DJnLzYgc0tKiRoeKT0iYv5JZw25VyRM 17 | ycLzfTrFmXCPfB1ylb483d9Ly4fBlM8nkx37PzEnAuukIawDxsPOb9yZC+hfvNJI 18 | 7NFiMN+3maEqG2iC00w4Lep4skHY7eHUEUMl+Wjr+koAy2YGLWAwHZQTm7iXn9Ab 19 | L6adL53zyCKelRuEQOzbeosJAqS+5fpMK0ekXyoFIuskj7bWuIoCX7K/kg6q5IW+ 20 | vC2FrlsrbQ79GztWLVmHFO1I4J9M5r666YS0qdh8c+2yyRl4FmSiHfGxb3eOKpxQ 21 | b6uI97iZlkxPF9LYUCSc7wq0V2gGz+6LnGvTHlHrOfVXqw/5pLAKhXqxvnroDTwz 22 | 0Ay/xFF6ei/NSxBY5t8ztGCBm45wCU3l8pW0X6dXqwUipw5b4MRy1VFRu6rqlmbL 23 | OPSCuLxqyqsigiEYsBgS/icvXz9DWmCQMPd2XM9YhsHvUq+R4QKCAQEA98EuMMXI 24 | 6UKIt1kK2t/3OeJRyDd4iv/fCMUAnuPjLBvFE4cXD/SbqCxcQYqb+pue3PYkiTIC 25 | 71rN8OQAc5yKhzmmnCE5N26br/0pG4pwEjIr6mt8kZHmemOCNEzvhhT83nfKmV0g 26 | 9lNtuGEQMiwmZrpUOF51JOMC39bzcVjYX2Cmvb7cFbIq3lR0zwM+aZpQ4P8LHCIu 27 | bgHmwbdlkLyIULJcQmHIbo6nPFB3ZZE4mqmjwY+rA6Fh9rgBa8OFCfTtrgeYXrNb 28 | IgZQ5U8GoYRPNC2ot0vpTinraboa/cgm6oG4M7FW1POCJTl+/ktHEnKuO5oroSga 29 | /BSg7hCNFVaOhwKCAQEA4Kkys0HtwEbV5mY/NnvUD5KwfXX7BxoXc9lZ6seVoLEc 30 | KjgPYxqYRVrC7dB2YDwwp3qcRTi/uBAgFNm3iYlDzI4xS5SeaudUWjglj7BSgXE2 31 | iOEa7EwcvVPluLaTgiWjlzUKeUCNNHWSeQOt+paBOT+IgwRVemGVpAgkqQzNh/nP 32 | tl3p9aNtgzEm1qVlPclY/XUCtf3bcOR+z1f1b4jBdn0leu5OhnxkC+Htik+2fTXD 33 | jt6JGrMkanN25YzsjnD3Sn+v6SO26H99wnYx5oMSdmb8SlWRrKtfJHnihphjG/YY 34 | l1cyorV6M/asSgXNQfGJm4OuJi0I4/FL2wLUHnU+JwKCAQEAzh4WipcRthYXXcoj 35 | gMKRkMOb3GFh1OpYqJgVExtudNTJmZxq8GhFU51MR27Eo7LycMwKy2UjEfTOnplh 36 | Us2qZiPtW7k8O8S2m6yXlYUQBeNdq9IuuYDTaYD94vsazscJNSAeGodjE+uGvb1q 37 | 1wLqE87yoE7dUInYa1cOA3+xy2/CaNuviBFJHtzOrSb6tqqenQEyQf6h9/12+DTW 38 | t5pSIiixHrzxHiFqOoCLRKGToQB+71rSINwTf0nITNpGBWmSj5VcC3VV3TG5/XxI 39 | fPlxV2yhD5WFDPVNGBGvwPDSh4jSMZdZMSNBZCy4XWFNSKjGEWoK4DFYed3DoSt9 40 | 5IG1YwKCAQA63ntHl64KJUWlkwNbboU583FF3uWBjee5VqoGKHhf3CkKMxhtGqnt 41 | +oN7t5VdUEhbinhqdx1dyPPvIsHCS3K1pkjqii4cyzNCVNYa2dQ00Qq+QWZBpwwc 42 | 3GAkz8rFXsGIPMDa1vxpU6mnBjzPniKMcsZ9tmQDppCEpBGfLpio2eAA5IkK8eEf 43 | cIDB3CM0Vo94EvI76CJZabaE9IJ+0HIJb2+jz9BJ00yQBIqvJIYoNy9gP5Xjpi+T 44 | qV/tdMkD5jwWjHD3AYHLWKUGkNwwkAYFeqT/gX6jpWBP+ZRPOp011X3KInJFSpKU 45 | DT5GQ1Dux7EMTCwVGtXqjO8Ym5wjwwsfAoIBAEcxlhIW1G6BiNfnWbNPWBdh3v/K 46 | 5Ln98Rcrz8UIbWyl7qNPjYb13C1KmifVG1Rym9vWMO3KuG5atK3Mz2yLVRtmWAVc 47 | fxzR57zz9MZFDun66xo+Z1wN3fVxQB4CYpOEI4Lb9ioX4v85hm3D6RpFukNtRQEc 48 | Gfr4scTjJX4jFWDp0h6ffMb8mY+quvZoJ0TJqV9L9Yj6Ksdvqez/bdSraev97bHQ 49 | 4gbQxaTZ6WjaD4HjpPQefMdWp97Metg0ZQSS8b8EzmNFgyJ3XcjirzwliKTAQtn6 50 | I2sd0NCIooelrKRD8EJoDUwxoOctY7R97wpZ7/wEHU45cBCbRV3H4JILS5c= 51 | -----END RSA PRIVATE KEY----- 52 | -------------------------------------------------------------------------------- /actix-utils/src/condition.rs: -------------------------------------------------------------------------------- 1 | use std::future::Future; 2 | use std::pin::Pin; 3 | use std::task::{Context, Poll}; 4 | 5 | use slab::Slab; 6 | 7 | use crate::cell::Cell; 8 | use crate::task::LocalWaker; 9 | 10 | /// Condition allows to notify multiple receivers at the same time 11 | pub struct Condition(Cell); 12 | 13 | struct Inner { 14 | data: Slab>, 15 | } 16 | 17 | impl Default for Condition { 18 | fn default() -> Self { 19 | Self::new() 20 | } 21 | } 22 | 23 | impl Condition { 24 | pub fn new() -> Condition { 25 | Condition(Cell::new(Inner { data: Slab::new() })) 26 | } 27 | 28 | /// Get condition waiter 29 | pub fn wait(&mut self) -> Waiter { 30 | let token = self.0.get_mut().data.insert(None); 31 | Waiter { 32 | token, 33 | inner: self.0.clone(), 34 | } 35 | } 36 | 37 | /// Notify all waiters 38 | pub fn notify(&self) { 39 | let inner = self.0.get_ref(); 40 | for item in inner.data.iter() { 41 | if let Some(waker) = item.1 { 42 | waker.wake(); 43 | } 44 | } 45 | } 46 | } 47 | 48 | impl Drop for Condition { 49 | fn drop(&mut self) { 50 | self.notify() 51 | } 52 | } 53 | 54 | #[must_use = "Waiter do nothing unless polled"] 55 | pub struct Waiter { 56 | token: usize, 57 | inner: Cell, 58 | } 59 | 60 | impl Clone for Waiter { 61 | fn clone(&self) -> Self { 62 | let token = unsafe { self.inner.get_mut_unsafe() }.data.insert(None); 63 | Waiter { 64 | token, 65 | inner: self.inner.clone(), 66 | } 67 | } 68 | } 69 | 70 | impl Future for Waiter { 71 | type Output = (); 72 | 73 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 74 | let this = self.get_mut(); 75 | 76 | let inner = unsafe { this.inner.get_mut().data.get_unchecked_mut(this.token) }; 77 | if inner.is_none() { 78 | let waker = LocalWaker::default(); 79 | waker.register(cx.waker()); 80 | *inner = Some(waker); 81 | Poll::Pending 82 | } else if inner.as_mut().unwrap().register(cx.waker()) { 83 | Poll::Pending 84 | } else { 85 | Poll::Ready(()) 86 | } 87 | } 88 | } 89 | 90 | impl Drop for Waiter { 91 | fn drop(&mut self) { 92 | self.inner.get_mut().data.remove(self.token); 93 | } 94 | } 95 | 96 | #[cfg(test)] 97 | mod tests { 98 | use super::*; 99 | use futures::future::lazy; 100 | 101 | #[actix_rt::test] 102 | async fn test_condition() { 103 | let mut cond = Condition::new(); 104 | let mut waiter = cond.wait(); 105 | assert_eq!( 106 | lazy(|cx| Pin::new(&mut waiter).poll(cx)).await, 107 | Poll::Pending 108 | ); 109 | cond.notify(); 110 | waiter.await; 111 | 112 | let mut waiter = cond.wait(); 113 | assert_eq!( 114 | lazy(|cx| Pin::new(&mut waiter).poll(cx)).await, 115 | Poll::Pending 116 | ); 117 | let mut waiter2 = waiter.clone(); 118 | assert_eq!( 119 | lazy(|cx| Pin::new(&mut waiter2).poll(cx)).await, 120 | Poll::Pending 121 | ); 122 | 123 | drop(cond); 124 | waiter.await; 125 | waiter2.await; 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /actix-tls/src/nativetls.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | use std::task::{Context, Poll}; 3 | 4 | use actix_codec::{AsyncRead, AsyncWrite}; 5 | use actix_service::{Service, ServiceFactory}; 6 | use actix_utils::counter::Counter; 7 | use futures::future::{self, FutureExt, LocalBoxFuture, TryFutureExt}; 8 | pub use native_tls::Error; 9 | pub use tokio_tls::{TlsAcceptor, TlsStream}; 10 | 11 | use crate::MAX_CONN_COUNTER; 12 | 13 | /// Support `SSL` connections via native-tls package 14 | /// 15 | /// `tls` feature enables `NativeTlsAcceptor` type 16 | pub struct NativeTlsAcceptor { 17 | acceptor: TlsAcceptor, 18 | io: PhantomData, 19 | } 20 | 21 | impl NativeTlsAcceptor 22 | where 23 | T: AsyncRead + AsyncWrite + Unpin, 24 | { 25 | /// Create `NativeTlsAcceptor` instance 26 | #[inline] 27 | pub fn new(acceptor: TlsAcceptor) -> Self { 28 | NativeTlsAcceptor { 29 | acceptor, 30 | io: PhantomData, 31 | } 32 | } 33 | } 34 | 35 | impl Clone for NativeTlsAcceptor { 36 | #[inline] 37 | fn clone(&self) -> Self { 38 | Self { 39 | acceptor: self.acceptor.clone(), 40 | io: PhantomData, 41 | } 42 | } 43 | } 44 | 45 | impl ServiceFactory for NativeTlsAcceptor 46 | where 47 | T: AsyncRead + AsyncWrite + Unpin + 'static, 48 | { 49 | type Request = T; 50 | type Response = TlsStream; 51 | type Error = Error; 52 | type Service = NativeTlsAcceptorService; 53 | 54 | type Config = (); 55 | type InitError = (); 56 | type Future = future::Ready>; 57 | 58 | fn new_service(&self, _: ()) -> Self::Future { 59 | MAX_CONN_COUNTER.with(|conns| { 60 | future::ok(NativeTlsAcceptorService { 61 | acceptor: self.acceptor.clone(), 62 | conns: conns.clone(), 63 | io: PhantomData, 64 | }) 65 | }) 66 | } 67 | } 68 | 69 | pub struct NativeTlsAcceptorService { 70 | acceptor: TlsAcceptor, 71 | io: PhantomData, 72 | conns: Counter, 73 | } 74 | 75 | impl Clone for NativeTlsAcceptorService { 76 | fn clone(&self) -> Self { 77 | Self { 78 | acceptor: self.acceptor.clone(), 79 | io: PhantomData, 80 | conns: self.conns.clone(), 81 | } 82 | } 83 | } 84 | 85 | impl Service for NativeTlsAcceptorService 86 | where 87 | T: AsyncRead + AsyncWrite + Unpin + 'static, 88 | { 89 | type Request = T; 90 | type Response = TlsStream; 91 | type Error = Error; 92 | type Future = LocalBoxFuture<'static, Result, Error>>; 93 | 94 | fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { 95 | if self.conns.available(cx) { 96 | Poll::Ready(Ok(())) 97 | } else { 98 | Poll::Pending 99 | } 100 | } 101 | 102 | fn call(&mut self, req: Self::Request) -> Self::Future { 103 | let guard = self.conns.get(); 104 | let this = self.clone(); 105 | async move { this.acceptor.accept(req).await } 106 | .map_ok(move |io| { 107 | // Required to preserve `CounterGuard` until `Self::Future` 108 | // is completely resolved. 109 | let _ = guard; 110 | io 111 | }) 112 | .boxed_local() 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /actix-server/CHANGES.md: -------------------------------------------------------------------------------- 1 | # Changes 2 | 3 | ## [1.0.1] - 2019-12-29 4 | 5 | ### Changed 6 | 7 | * Rename `.start()` method to `.run()` 8 | 9 | ## [1.0.0] - 2019-12-11 10 | 11 | ### Changed 12 | 13 | * Use actix-net releases 14 | 15 | 16 | ## [1.0.0-alpha.4] - 2019-12-08 17 | 18 | ### Changed 19 | 20 | * Use actix-service 1.0.0-alpha.4 21 | 22 | ## [1.0.0-alpha.3] - 2019-12-07 23 | 24 | ### Changed 25 | 26 | * Migrate to tokio 0.2 27 | 28 | ### Fixed 29 | 30 | * Fix compilation on non-unix platforms 31 | 32 | * Better handling server configuration 33 | 34 | 35 | ## [1.0.0-alpha.2] - 2019-12-02 36 | 37 | ### Changed 38 | 39 | * Simplify server service (remove actix-server-config) 40 | 41 | * Allow to wait on `Server` until server stops 42 | 43 | 44 | ## [0.8.0-alpha.1] - 2019-11-22 45 | 46 | ### Changed 47 | 48 | * Migrate to `std::future` 49 | 50 | 51 | ## [0.7.0] - 2019-10-04 52 | 53 | ### Changed 54 | 55 | * Update `rustls` to 0.16 56 | * Minimum required Rust version upped to 1.37.0 57 | 58 | 59 | ## [0.6.1] - 2019-09-25 60 | 61 | ### Added 62 | 63 | * Add UDS listening support to `ServerBuilder` 64 | 65 | 66 | ## [0.6.0] - 2019-07-18 67 | 68 | ### Added 69 | 70 | * Support Unix domain sockets #3 71 | 72 | 73 | ## [0.5.1] - 2019-05-18 74 | 75 | ### Changed 76 | 77 | * ServerBuilder::shutdown_timeout() accepts u64 78 | 79 | 80 | ## [0.5.0] - 2019-05-12 81 | 82 | ### Added 83 | 84 | * Add `Debug` impl for `SslError` 85 | 86 | * Derive debug for `Server` and `ServerCommand` 87 | 88 | ### Changed 89 | 90 | * Upgrade to actix-service 0.4 91 | 92 | 93 | ## [0.4.3] - 2019-04-16 94 | 95 | ### Added 96 | 97 | * Re-export `IoStream` trait 98 | 99 | ### Changed 100 | 101 | * Deppend on `ssl` and `rust-tls` features from actix-server-config 102 | 103 | 104 | ## [0.4.2] - 2019-03-30 105 | 106 | ### Fixed 107 | 108 | * Fix SIGINT force shutdown 109 | 110 | 111 | ## [0.4.1] - 2019-03-14 112 | 113 | ### Added 114 | 115 | * `SystemRuntime::on_start()` - allow to run future before server service initialization 116 | 117 | 118 | ## [0.4.0] - 2019-03-12 119 | 120 | ### Changed 121 | 122 | * Use `ServerConfig` for service factory 123 | 124 | * Wrap tcp socket to `Io` type 125 | 126 | * Upgrade actix-service 127 | 128 | 129 | ## [0.3.1] - 2019-03-04 130 | 131 | ### Added 132 | 133 | * Add `ServerBuilder::maxconnrate` sets the maximum per-worker number of concurrent connections 134 | 135 | * Add helper ssl error `SslError` 136 | 137 | 138 | ### Changed 139 | 140 | * Rename `StreamServiceFactory` to `ServiceFactory` 141 | 142 | * Deprecate `StreamServiceFactory` 143 | 144 | 145 | ## [0.3.0] - 2019-03-02 146 | 147 | ### Changed 148 | 149 | * Use new `NewService` trait 150 | 151 | 152 | ## [0.2.1] - 2019-02-09 153 | 154 | ### Changed 155 | 156 | * Drop service response 157 | 158 | 159 | ## [0.2.0] - 2019-02-01 160 | 161 | ### Changed 162 | 163 | * Migrate to actix-service 0.2 164 | 165 | * Updated rustls dependency 166 | 167 | 168 | ## [0.1.3] - 2018-12-21 169 | 170 | ### Fixed 171 | 172 | * Fix max concurrent connections handling 173 | 174 | 175 | ## [0.1.2] - 2018-12-12 176 | 177 | ### Changed 178 | 179 | * rename ServiceConfig::rt() to ServiceConfig::apply() 180 | 181 | 182 | ### Fixed 183 | 184 | * Fix back-pressure for concurrent ssl handshakes 185 | 186 | 187 | ## [0.1.1] - 2018-12-11 188 | 189 | * Fix signal handling on windows 190 | 191 | 192 | ## [0.1.0] - 2018-12-09 193 | 194 | * Move server to separate crate 195 | -------------------------------------------------------------------------------- /actix-server/src/server.rs: -------------------------------------------------------------------------------- 1 | use std::future::Future; 2 | use std::io; 3 | use std::pin::Pin; 4 | use std::task::{Context, Poll}; 5 | 6 | use futures::channel::mpsc::UnboundedSender; 7 | use futures::channel::oneshot; 8 | use futures::FutureExt; 9 | 10 | use crate::builder::ServerBuilder; 11 | use crate::signals::Signal; 12 | 13 | #[derive(Debug)] 14 | pub(crate) enum ServerCommand { 15 | WorkerFaulted(usize), 16 | Pause(oneshot::Sender<()>), 17 | Resume(oneshot::Sender<()>), 18 | Signal(Signal), 19 | /// Whether to try and shut down gracefully 20 | Stop { 21 | graceful: bool, 22 | completion: Option>, 23 | }, 24 | /// Notify of server stop 25 | Notify(oneshot::Sender<()>), 26 | } 27 | 28 | #[derive(Debug)] 29 | pub struct Server( 30 | UnboundedSender, 31 | Option>, 32 | ); 33 | 34 | impl Server { 35 | pub(crate) fn new(tx: UnboundedSender) -> Self { 36 | Server(tx, None) 37 | } 38 | 39 | /// Start server building process 40 | pub fn build() -> ServerBuilder { 41 | ServerBuilder::default() 42 | } 43 | 44 | pub(crate) fn signal(&self, sig: Signal) { 45 | let _ = self.0.unbounded_send(ServerCommand::Signal(sig)); 46 | } 47 | 48 | pub(crate) fn worker_faulted(&self, idx: usize) { 49 | let _ = self.0.unbounded_send(ServerCommand::WorkerFaulted(idx)); 50 | } 51 | 52 | /// Pause accepting incoming connections 53 | /// 54 | /// If socket contains some pending connection, they might be dropped. 55 | /// All opened connection remains active. 56 | pub fn pause(&self) -> impl Future { 57 | let (tx, rx) = oneshot::channel(); 58 | let _ = self.0.unbounded_send(ServerCommand::Pause(tx)); 59 | rx.map(|_| ()) 60 | } 61 | 62 | /// Resume accepting incoming connections 63 | pub fn resume(&self) -> impl Future { 64 | let (tx, rx) = oneshot::channel(); 65 | let _ = self.0.unbounded_send(ServerCommand::Resume(tx)); 66 | rx.map(|_| ()) 67 | } 68 | 69 | /// Stop incoming connection processing, stop all workers and exit. 70 | /// 71 | /// If server starts with `spawn()` method, then spawned thread get terminated. 72 | pub fn stop(&self, graceful: bool) -> impl Future { 73 | let (tx, rx) = oneshot::channel(); 74 | let _ = self.0.unbounded_send(ServerCommand::Stop { 75 | graceful, 76 | completion: Some(tx), 77 | }); 78 | rx.map(|_| ()) 79 | } 80 | } 81 | 82 | impl Clone for Server { 83 | fn clone(&self) -> Self { 84 | Self(self.0.clone(), None) 85 | } 86 | } 87 | 88 | impl Future for Server { 89 | type Output = io::Result<()>; 90 | 91 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 92 | let this = self.get_mut(); 93 | 94 | if this.1.is_none() { 95 | let (tx, rx) = oneshot::channel(); 96 | if this.0.unbounded_send(ServerCommand::Notify(tx)).is_err() { 97 | return Poll::Ready(Ok(())); 98 | } 99 | this.1 = Some(rx); 100 | } 101 | 102 | match Pin::new(this.1.as_mut().unwrap()).poll(cx) { 103 | Poll::Pending => Poll::Pending, 104 | Poll::Ready(Ok(_)) => Poll::Ready(Ok(())), 105 | Poll::Ready(Err(_)) => Poll::Ready(Ok(())), 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /actix-utils/src/keepalive.rs: -------------------------------------------------------------------------------- 1 | use std::convert::Infallible; 2 | use std::future::Future; 3 | use std::marker::PhantomData; 4 | use std::pin::Pin; 5 | use std::task::{Context, Poll}; 6 | use std::time::Duration; 7 | 8 | use actix_rt::time::{delay_until, Delay, Instant}; 9 | use actix_service::{Service, ServiceFactory}; 10 | use futures::future::{ok, Ready}; 11 | 12 | use super::time::{LowResTime, LowResTimeService}; 13 | 14 | pub struct KeepAlive { 15 | f: F, 16 | ka: Duration, 17 | time: LowResTime, 18 | _t: PhantomData<(R, E)>, 19 | } 20 | 21 | impl KeepAlive 22 | where 23 | F: Fn() -> E + Clone, 24 | { 25 | pub fn new(ka: Duration, time: LowResTime, f: F) -> Self { 26 | KeepAlive { 27 | f, 28 | ka, 29 | time, 30 | _t: PhantomData, 31 | } 32 | } 33 | } 34 | 35 | impl Clone for KeepAlive 36 | where 37 | F: Clone, 38 | { 39 | fn clone(&self) -> Self { 40 | KeepAlive { 41 | f: self.f.clone(), 42 | ka: self.ka, 43 | time: self.time.clone(), 44 | _t: PhantomData, 45 | } 46 | } 47 | } 48 | 49 | impl ServiceFactory for KeepAlive 50 | where 51 | F: Fn() -> E + Clone, 52 | { 53 | type Request = R; 54 | type Response = R; 55 | type Error = E; 56 | type InitError = Infallible; 57 | type Config = (); 58 | type Service = KeepAliveService; 59 | type Future = Ready>; 60 | 61 | fn new_service(&self, _: ()) -> Self::Future { 62 | ok(KeepAliveService::new( 63 | self.ka, 64 | self.time.timer(), 65 | self.f.clone(), 66 | )) 67 | } 68 | } 69 | 70 | pub struct KeepAliveService { 71 | f: F, 72 | ka: Duration, 73 | time: LowResTimeService, 74 | delay: Delay, 75 | expire: Instant, 76 | _t: PhantomData<(R, E)>, 77 | } 78 | 79 | impl KeepAliveService 80 | where 81 | F: Fn() -> E, 82 | { 83 | pub fn new(ka: Duration, time: LowResTimeService, f: F) -> Self { 84 | let expire = Instant::from_std(time.now() + ka); 85 | KeepAliveService { 86 | f, 87 | ka, 88 | time, 89 | expire, 90 | delay: delay_until(expire), 91 | _t: PhantomData, 92 | } 93 | } 94 | } 95 | 96 | impl Service for KeepAliveService 97 | where 98 | F: Fn() -> E, 99 | { 100 | type Request = R; 101 | type Response = R; 102 | type Error = E; 103 | type Future = Ready>; 104 | 105 | fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { 106 | match Pin::new(&mut self.delay).poll(cx) { 107 | Poll::Ready(_) => { 108 | let now = Instant::from_std(self.time.now()); 109 | if self.expire <= now { 110 | Poll::Ready(Err((self.f)())) 111 | } else { 112 | self.delay.reset(self.expire); 113 | let _ = Pin::new(&mut self.delay).poll(cx); 114 | Poll::Ready(Ok(())) 115 | } 116 | } 117 | Poll::Pending => Poll::Ready(Ok(())), 118 | } 119 | } 120 | 121 | fn call(&mut self, req: R) -> Self::Future { 122 | self.expire = Instant::from_std(self.time.now() + self.ka); 123 | ok(req) 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /actix-tls/src/rustls.rs: -------------------------------------------------------------------------------- 1 | use std::future::Future; 2 | use std::io; 3 | use std::marker::PhantomData; 4 | use std::pin::Pin; 5 | use std::sync::Arc; 6 | use std::task::{Context, Poll}; 7 | 8 | use actix_codec::{AsyncRead, AsyncWrite}; 9 | use actix_service::{Service, ServiceFactory}; 10 | use actix_utils::counter::{Counter, CounterGuard}; 11 | use futures::future::{ok, Ready}; 12 | use tokio_rustls::{Accept, TlsAcceptor}; 13 | 14 | pub use rust_tls::{ServerConfig, Session}; 15 | pub use tokio_rustls::server::TlsStream; 16 | pub use webpki_roots::TLS_SERVER_ROOTS; 17 | 18 | use crate::MAX_CONN_COUNTER; 19 | 20 | /// Support `SSL` connections via rustls package 21 | /// 22 | /// `rust-tls` feature enables `RustlsAcceptor` type 23 | pub struct Acceptor { 24 | config: Arc, 25 | io: PhantomData, 26 | } 27 | 28 | impl Acceptor { 29 | /// Create rustls based `Acceptor` service factory 30 | pub fn new(config: ServerConfig) -> Self { 31 | Acceptor { 32 | config: Arc::new(config), 33 | io: PhantomData, 34 | } 35 | } 36 | } 37 | 38 | impl Clone for Acceptor { 39 | fn clone(&self) -> Self { 40 | Self { 41 | config: self.config.clone(), 42 | io: PhantomData, 43 | } 44 | } 45 | } 46 | 47 | impl ServiceFactory for Acceptor { 48 | type Request = T; 49 | type Response = TlsStream; 50 | type Error = io::Error; 51 | type Service = AcceptorService; 52 | 53 | type Config = (); 54 | type InitError = (); 55 | type Future = Ready>; 56 | 57 | fn new_service(&self, _: ()) -> Self::Future { 58 | MAX_CONN_COUNTER.with(|conns| { 59 | ok(AcceptorService { 60 | acceptor: self.config.clone().into(), 61 | conns: conns.clone(), 62 | io: PhantomData, 63 | }) 64 | }) 65 | } 66 | } 67 | 68 | /// RusTLS based `Acceptor` service 69 | pub struct AcceptorService { 70 | acceptor: TlsAcceptor, 71 | io: PhantomData, 72 | conns: Counter, 73 | } 74 | 75 | impl Service for AcceptorService { 76 | type Request = T; 77 | type Response = TlsStream; 78 | type Error = io::Error; 79 | type Future = AcceptorServiceFut; 80 | 81 | fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { 82 | if self.conns.available(cx) { 83 | Poll::Ready(Ok(())) 84 | } else { 85 | Poll::Pending 86 | } 87 | } 88 | 89 | fn call(&mut self, req: Self::Request) -> Self::Future { 90 | AcceptorServiceFut { 91 | _guard: self.conns.get(), 92 | fut: self.acceptor.accept(req), 93 | } 94 | } 95 | } 96 | 97 | pub struct AcceptorServiceFut 98 | where 99 | T: AsyncRead + AsyncWrite + Unpin, 100 | { 101 | fut: Accept, 102 | _guard: CounterGuard, 103 | } 104 | 105 | impl Future for AcceptorServiceFut { 106 | type Output = Result, io::Error>; 107 | 108 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 109 | let this = self.get_mut(); 110 | 111 | let res = futures::ready!(Pin::new(&mut this.fut).poll(cx)); 112 | match res { 113 | Ok(io) => Poll::Ready(Ok(io)), 114 | Err(e) => Poll::Ready(Err(e)), 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /actix-tls/src/openssl.rs: -------------------------------------------------------------------------------- 1 | use std::future::Future; 2 | use std::marker::PhantomData; 3 | use std::pin::Pin; 4 | use std::task::{Context, Poll}; 5 | 6 | pub use open_ssl::ssl::{AlpnError, SslAcceptor, SslAcceptorBuilder}; 7 | pub use tokio_openssl::{HandshakeError, SslStream}; 8 | 9 | use actix_codec::{AsyncRead, AsyncWrite}; 10 | use actix_service::{Service, ServiceFactory}; 11 | use actix_utils::counter::{Counter, CounterGuard}; 12 | use futures::future::{ok, FutureExt, LocalBoxFuture, Ready}; 13 | 14 | use crate::MAX_CONN_COUNTER; 15 | 16 | /// Support `TLS` server connections via openssl package 17 | /// 18 | /// `openssl` feature enables `Acceptor` type 19 | pub struct Acceptor { 20 | acceptor: SslAcceptor, 21 | io: PhantomData, 22 | } 23 | 24 | impl Acceptor { 25 | /// Create default `OpensslAcceptor` 26 | pub fn new(acceptor: SslAcceptor) -> Self { 27 | Acceptor { 28 | acceptor, 29 | io: PhantomData, 30 | } 31 | } 32 | } 33 | 34 | impl Clone for Acceptor { 35 | fn clone(&self) -> Self { 36 | Self { 37 | acceptor: self.acceptor.clone(), 38 | io: PhantomData, 39 | } 40 | } 41 | } 42 | 43 | impl ServiceFactory for Acceptor { 44 | type Request = T; 45 | type Response = SslStream; 46 | type Error = HandshakeError; 47 | type Config = (); 48 | type Service = AcceptorService; 49 | type InitError = (); 50 | type Future = Ready>; 51 | 52 | fn new_service(&self, _: ()) -> Self::Future { 53 | MAX_CONN_COUNTER.with(|conns| { 54 | ok(AcceptorService { 55 | acceptor: self.acceptor.clone(), 56 | conns: conns.clone(), 57 | io: PhantomData, 58 | }) 59 | }) 60 | } 61 | } 62 | 63 | pub struct AcceptorService { 64 | acceptor: SslAcceptor, 65 | conns: Counter, 66 | io: PhantomData, 67 | } 68 | 69 | impl Service for AcceptorService { 70 | type Request = T; 71 | type Response = SslStream; 72 | type Error = HandshakeError; 73 | type Future = AcceptorServiceResponse; 74 | 75 | fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll> { 76 | if self.conns.available(ctx) { 77 | Poll::Ready(Ok(())) 78 | } else { 79 | Poll::Pending 80 | } 81 | } 82 | 83 | fn call(&mut self, req: Self::Request) -> Self::Future { 84 | let acc = self.acceptor.clone(); 85 | AcceptorServiceResponse { 86 | _guard: self.conns.get(), 87 | fut: async move { 88 | let acc = acc; 89 | tokio_openssl::accept(&acc, req).await 90 | } 91 | .boxed_local(), 92 | } 93 | } 94 | } 95 | 96 | pub struct AcceptorServiceResponse 97 | where 98 | T: AsyncRead + AsyncWrite, 99 | { 100 | fut: LocalBoxFuture<'static, Result, HandshakeError>>, 101 | _guard: CounterGuard, 102 | } 103 | 104 | impl Future for AcceptorServiceResponse { 105 | type Output = Result, HandshakeError>; 106 | 107 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 108 | let io = futures::ready!(Pin::new(&mut self.fut).poll(cx))?; 109 | Poll::Ready(Ok(io)) 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /actix-ioframe/src/connect.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | use std::pin::Pin; 3 | use std::task::{Context, Poll}; 4 | 5 | use actix_codec::{AsyncRead, AsyncWrite, Decoder, Encoder, Framed}; 6 | use actix_utils::mpsc::Receiver; 7 | use futures::Stream; 8 | 9 | pub struct Connect 10 | where 11 | Codec: Encoder + Decoder, 12 | { 13 | io: Io, 14 | _t: PhantomData, 15 | } 16 | 17 | impl Connect 18 | where 19 | Io: AsyncRead + AsyncWrite, 20 | Codec: Encoder + Decoder, 21 | { 22 | pub(crate) fn new(io: Io) -> Self { 23 | Self { 24 | io, 25 | _t: PhantomData, 26 | } 27 | } 28 | 29 | pub fn codec( 30 | self, 31 | codec: Codec, 32 | ) -> ConnectResult::Item>> { 33 | ConnectResult { 34 | state: (), 35 | out: None, 36 | framed: Framed::new(self.io, codec), 37 | } 38 | } 39 | } 40 | 41 | #[pin_project::pin_project] 42 | pub struct ConnectResult { 43 | pub(crate) state: St, 44 | pub(crate) out: Option, 45 | pub(crate) framed: Framed, 46 | } 47 | 48 | impl ConnectResult { 49 | #[inline] 50 | pub fn get_ref(&self) -> &Io { 51 | self.framed.get_ref() 52 | } 53 | 54 | #[inline] 55 | pub fn get_mut(&mut self) -> &mut Io { 56 | self.framed.get_mut() 57 | } 58 | 59 | pub fn out(self, out: U) -> ConnectResult 60 | where 61 | U: Stream::Item> + Unpin, 62 | { 63 | ConnectResult { 64 | state: self.state, 65 | framed: self.framed, 66 | out: Some(out), 67 | } 68 | } 69 | 70 | #[inline] 71 | pub fn state(self, state: S) -> ConnectResult { 72 | ConnectResult { 73 | state, 74 | framed: self.framed, 75 | out: self.out, 76 | } 77 | } 78 | } 79 | 80 | impl Stream for ConnectResult 81 | where 82 | Io: AsyncRead + AsyncWrite, 83 | Codec: Encoder + Decoder, 84 | { 85 | type Item = Result<::Item, ::Error>; 86 | 87 | fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 88 | self.project().framed.next_item(cx) 89 | } 90 | } 91 | 92 | impl futures::Sink<::Item> 93 | for ConnectResult 94 | where 95 | Io: AsyncRead + AsyncWrite, 96 | Codec: Encoder + Decoder, 97 | { 98 | type Error = ::Error; 99 | 100 | fn poll_ready(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { 101 | if self.framed.is_write_ready() { 102 | Poll::Ready(Ok(())) 103 | } else { 104 | Poll::Pending 105 | } 106 | } 107 | 108 | fn start_send( 109 | self: Pin<&mut Self>, 110 | item: ::Item, 111 | ) -> Result<(), Self::Error> { 112 | self.project().framed.write(item) 113 | } 114 | 115 | fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 116 | self.get_mut().framed.flush(cx) 117 | } 118 | 119 | fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 120 | self.get_mut().framed.close(cx) 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /router/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Resource path matching library. 2 | mod de; 3 | mod path; 4 | mod resource; 5 | mod router; 6 | 7 | pub use self::de::PathDeserializer; 8 | pub use self::path::Path; 9 | pub use self::resource::ResourceDef; 10 | pub use self::router::{ResourceInfo, Router, RouterBuilder}; 11 | 12 | pub trait Resource { 13 | fn resource_path(&mut self) -> &mut Path; 14 | } 15 | 16 | pub trait ResourcePath { 17 | fn path(&self) -> &str; 18 | } 19 | 20 | impl ResourcePath for String { 21 | fn path(&self) -> &str { 22 | self.as_str() 23 | } 24 | } 25 | 26 | impl<'a> ResourcePath for &'a str { 27 | fn path(&self) -> &str { 28 | self 29 | } 30 | } 31 | 32 | impl ResourcePath for bytestring::ByteString { 33 | fn path(&self) -> &str { 34 | &*self 35 | } 36 | } 37 | 38 | /// Helper trait for type that could be converted to path pattern 39 | pub trait IntoPattern { 40 | /// Signle patter 41 | fn is_single(&self) -> bool; 42 | 43 | fn patterns(&self) -> Vec; 44 | } 45 | 46 | impl IntoPattern for String { 47 | fn is_single(&self) -> bool { 48 | true 49 | } 50 | 51 | fn patterns(&self) -> Vec { 52 | vec![self.clone()] 53 | } 54 | } 55 | 56 | impl<'a> IntoPattern for &'a String { 57 | fn is_single(&self) -> bool { 58 | true 59 | } 60 | 61 | fn patterns(&self) -> Vec { 62 | vec![self.as_str().to_string()] 63 | } 64 | } 65 | 66 | impl<'a> IntoPattern for &'a str { 67 | fn is_single(&self) -> bool { 68 | true 69 | } 70 | 71 | fn patterns(&self) -> Vec { 72 | vec![(*self).to_string()] 73 | } 74 | } 75 | 76 | impl> IntoPattern for Vec { 77 | fn is_single(&self) -> bool { 78 | self.len() == 1 79 | } 80 | 81 | fn patterns(&self) -> Vec { 82 | self.iter().map(|v| v.as_ref().to_string()).collect() 83 | } 84 | } 85 | 86 | macro_rules! array_patterns (($tp:ty, $num:tt) => { 87 | impl IntoPattern for [$tp; $num] { 88 | fn is_single(&self) -> bool { 89 | $num == 1 90 | } 91 | 92 | fn patterns(&self) -> Vec { 93 | self.iter().map(|v| v.to_string()).collect() 94 | } 95 | } 96 | }); 97 | 98 | array_patterns!(&str, 1); 99 | array_patterns!(&str, 2); 100 | array_patterns!(&str, 3); 101 | array_patterns!(&str, 4); 102 | array_patterns!(&str, 5); 103 | array_patterns!(&str, 6); 104 | array_patterns!(&str, 7); 105 | array_patterns!(&str, 8); 106 | array_patterns!(&str, 9); 107 | array_patterns!(&str, 10); 108 | array_patterns!(&str, 11); 109 | array_patterns!(&str, 12); 110 | array_patterns!(&str, 13); 111 | array_patterns!(&str, 14); 112 | array_patterns!(&str, 15); 113 | array_patterns!(&str, 16); 114 | 115 | array_patterns!(String, 1); 116 | array_patterns!(String, 2); 117 | array_patterns!(String, 3); 118 | array_patterns!(String, 4); 119 | array_patterns!(String, 5); 120 | array_patterns!(String, 6); 121 | array_patterns!(String, 7); 122 | array_patterns!(String, 8); 123 | array_patterns!(String, 9); 124 | array_patterns!(String, 10); 125 | array_patterns!(String, 11); 126 | array_patterns!(String, 12); 127 | array_patterns!(String, 13); 128 | array_patterns!(String, 14); 129 | array_patterns!(String, 15); 130 | array_patterns!(String, 16); 131 | 132 | #[cfg(feature = "http")] 133 | mod url; 134 | 135 | #[cfg(feature = "http")] 136 | pub use self::url::{Quoter, Url}; 137 | 138 | #[cfg(feature = "http")] 139 | mod http_support { 140 | use super::ResourcePath; 141 | use http::Uri; 142 | 143 | impl ResourcePath for Uri { 144 | fn path(&self) -> &str { 145 | self.path() 146 | } 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /actix-connect/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Actix connect - tcp connector service 2 | //! 3 | //! ## Package feature 4 | //! 5 | //! * `openssl` - enables ssl support via `openssl` crate 6 | //! * `rustls` - enables ssl support via `rustls` crate 7 | #![deny(rust_2018_idioms, warnings)] 8 | #![allow(clippy::type_complexity)] 9 | #![recursion_limit = "128"] 10 | 11 | #[macro_use] 12 | extern crate log; 13 | 14 | mod connect; 15 | mod connector; 16 | mod error; 17 | mod resolve; 18 | mod service; 19 | pub mod ssl; 20 | 21 | #[cfg(feature = "uri")] 22 | mod uri; 23 | 24 | use actix_rt::{net::TcpStream, Arbiter}; 25 | use actix_service::{pipeline, pipeline_factory, Service, ServiceFactory}; 26 | use trust_dns_resolver::config::{ResolverConfig, ResolverOpts}; 27 | use trust_dns_resolver::system_conf::read_system_conf; 28 | use trust_dns_resolver::AsyncResolver; 29 | 30 | pub mod resolver { 31 | pub use trust_dns_resolver::config::{ResolverConfig, ResolverOpts}; 32 | pub use trust_dns_resolver::system_conf::read_system_conf; 33 | pub use trust_dns_resolver::{error::ResolveError, AsyncResolver}; 34 | } 35 | 36 | pub use self::connect::{Address, Connect, Connection}; 37 | pub use self::connector::{TcpConnector, TcpConnectorFactory}; 38 | pub use self::error::ConnectError; 39 | pub use self::resolve::{Resolver, ResolverFactory}; 40 | pub use self::service::{ConnectService, ConnectServiceFactory, TcpConnectService}; 41 | 42 | pub fn start_resolver(cfg: ResolverConfig, opts: ResolverOpts) -> AsyncResolver { 43 | let (resolver, bg) = AsyncResolver::new(cfg, opts); 44 | actix_rt::spawn(bg); 45 | resolver 46 | } 47 | 48 | struct DefaultResolver(AsyncResolver); 49 | 50 | pub(crate) fn get_default_resolver() -> AsyncResolver { 51 | if Arbiter::contains_item::() { 52 | Arbiter::get_item(|item: &DefaultResolver| item.0.clone()) 53 | } else { 54 | let (cfg, opts) = match read_system_conf() { 55 | Ok((cfg, opts)) => (cfg, opts), 56 | Err(e) => { 57 | log::error!("TRust-DNS can not load system config: {}", e); 58 | (ResolverConfig::default(), ResolverOpts::default()) 59 | } 60 | }; 61 | 62 | let (resolver, bg) = AsyncResolver::new(cfg, opts); 63 | actix_rt::spawn(bg); 64 | 65 | Arbiter::set_item(DefaultResolver(resolver.clone())); 66 | resolver 67 | } 68 | } 69 | 70 | pub fn start_default_resolver() -> AsyncResolver { 71 | get_default_resolver() 72 | } 73 | 74 | /// Create tcp connector service 75 | pub fn new_connector( 76 | resolver: AsyncResolver, 77 | ) -> impl Service, Response = Connection, Error = ConnectError> 78 | + Clone { 79 | pipeline(Resolver::new(resolver)).and_then(TcpConnector::new()) 80 | } 81 | 82 | /// Create tcp connector service 83 | pub fn new_connector_factory( 84 | resolver: AsyncResolver, 85 | ) -> impl ServiceFactory< 86 | Config = (), 87 | Request = Connect, 88 | Response = Connection, 89 | Error = ConnectError, 90 | InitError = (), 91 | > + Clone { 92 | pipeline_factory(ResolverFactory::new(resolver)).and_then(TcpConnectorFactory::new()) 93 | } 94 | 95 | /// Create connector service with default parameters 96 | pub fn default_connector( 97 | ) -> impl Service, Response = Connection, Error = ConnectError> 98 | + Clone { 99 | pipeline(Resolver::default()).and_then(TcpConnector::new()) 100 | } 101 | 102 | /// Create connector service factory with default parameters 103 | pub fn default_connector_factory() -> impl ServiceFactory< 104 | Config = (), 105 | Request = Connect, 106 | Response = Connection, 107 | Error = ConnectError, 108 | InitError = (), 109 | > + Clone { 110 | pipeline_factory(ResolverFactory::default()).and_then(TcpConnectorFactory::new()) 111 | } 112 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Actix net [![codecov](https://codecov.io/gh/actix/actix-net/branch/master/graph/badge.svg)](https://codecov.io/gh/actix/actix-net) [![Join the chat at https://gitter.im/actix/actix](https://badges.gitter.im/actix/actix.svg)](https://gitter.im/actix/actix?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 2 | 3 | Actix net - framework for composable network services 4 | 5 | ## Build statuses 6 | 7 | | Platform | Build Status | 8 | | ---------------- | ------------ | 9 | | Linux | [![build status](https://github.com/actix/actix-net/workflows/CI%20%28Linux%29/badge.svg?branch=master&event=push)](https://github.com/actix/actix-net/actions?query=workflow%3A"CI+(Linux)") | 10 | | macOS | [![build status](https://github.com/actix/actix-net/workflows/CI%20%28macOS%29/badge.svg?branch=master&event=push)](https://github.com/actix/actix-net/actions?query=workflow%3A"CI+(macOS)") | 11 | | Windows | [![build status](https://github.com/actix/actix-net/workflows/CI%20%28Windows%29/badge.svg?branch=master&event=push)](https://github.com/actix/actix-net/actions?query=workflow%3A"CI+(Windows)") | 12 | | Windows (MinGW) | [![build status](https://github.com/actix/actix-net/workflows/CI%20%28Windows-mingw%29/badge.svg?branch=master&event=push)](https://github.com/actix/actix-net/actions?query=workflow%3A"CI+(Windows-mingw)") | 13 | 14 | ## Documentation & community resources 15 | 16 | * [Chat on gitter](https://gitter.im/actix/actix) 17 | * Minimum supported Rust version: 1.39 or later 18 | 19 | ## Example 20 | 21 | ```rust 22 | fn main() -> io::Result<()> { 23 | // load ssl keys 24 | let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap(); 25 | builder.set_private_key_file("./examples/key.pem", SslFiletype::PEM).unwrap(); 26 | builder.set_certificate_chain_file("./examples/cert.pem").unwrap(); 27 | let acceptor = builder.build(); 28 | 29 | let num = Arc::new(AtomicUsize::new(0)); 30 | 31 | // bind socket address and start workers. By default server uses number of 32 | // available logical cpu as threads count. actix net start separate 33 | // instances of service pipeline in each worker. 34 | Server::build() 35 | .bind( 36 | // configure service pipeline 37 | "basic", "0.0.0.0:8443", 38 | move || { 39 | let num = num.clone(); 40 | let acceptor = acceptor.clone(); 41 | 42 | // construct transformation pipeline 43 | pipeline( 44 | // service for converting incoming TcpStream to a SslStream 45 | fn_service(move |stream: actix_rt::net::TcpStream| async move { 46 | SslAcceptorExt::accept_async(&acceptor, stream.into_parts().0).await 47 | .map_err(|e| println!("Openssl error: {}", e)) 48 | })) 49 | // .and_then() combinator chains result of previos service call to argument 50 | /// for next service calll. in this case, on success we chain 51 | /// ssl stream to the `logger` service. 52 | .and_then(fn_service(logger)) 53 | // Next service counts number of connections 54 | .and_then(move |_| { 55 | let num = num.fetch_add(1, Ordering::Relaxed); 56 | println!("got ssl connection {:?}", num); 57 | future::ok(()) 58 | }) 59 | }, 60 | )? 61 | .run() 62 | } 63 | ``` 64 | 65 | ## License 66 | 67 | This project is licensed under either of 68 | 69 | * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0)) 70 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or [http://opensource.org/licenses/MIT](http://opensource.org/licenses/MIT)) 71 | 72 | at your option. 73 | 74 | ## Code of Conduct 75 | 76 | Contribution to the actix-net crate is organized under the terms of the 77 | Contributor Covenant, the maintainer of actix-net, @fafhrd91, promises to 78 | intervene to uphold that code of conduct. 79 | -------------------------------------------------------------------------------- /actix-service/src/boxed.rs: -------------------------------------------------------------------------------- 1 | use std::future::Future; 2 | use std::pin::Pin; 3 | use std::task::{Context, Poll}; 4 | 5 | use futures_util::future::FutureExt; 6 | 7 | use crate::{Service, ServiceFactory}; 8 | 9 | pub type BoxFuture = Pin>>>; 10 | 11 | pub type BoxService = 12 | Box>>; 13 | 14 | pub struct BoxServiceFactory(Inner); 15 | 16 | /// Create boxed service factory 17 | pub fn factory( 18 | factory: T, 19 | ) -> BoxServiceFactory 20 | where 21 | T: ServiceFactory + 'static, 22 | T::Request: 'static, 23 | T::Response: 'static, 24 | T::Service: 'static, 25 | T::Future: 'static, 26 | T::Error: 'static, 27 | T::InitError: 'static, 28 | { 29 | BoxServiceFactory(Box::new(FactoryWrapper { 30 | factory, 31 | _t: std::marker::PhantomData, 32 | })) 33 | } 34 | 35 | /// Create boxed service 36 | pub fn service(service: T) -> BoxService 37 | where 38 | T: Service + 'static, 39 | T::Future: 'static, 40 | { 41 | Box::new(ServiceWrapper(service)) 42 | } 43 | 44 | type Inner = Box< 45 | dyn ServiceFactory< 46 | Config = C, 47 | Request = Req, 48 | Response = Res, 49 | Error = Err, 50 | InitError = InitErr, 51 | Service = BoxService, 52 | Future = BoxFuture, InitErr>, 53 | >, 54 | >; 55 | 56 | impl ServiceFactory for BoxServiceFactory 57 | where 58 | Req: 'static, 59 | Res: 'static, 60 | Err: 'static, 61 | InitErr: 'static, 62 | { 63 | type Request = Req; 64 | type Response = Res; 65 | type Error = Err; 66 | type InitError = InitErr; 67 | type Config = C; 68 | type Service = BoxService; 69 | 70 | type Future = BoxFuture; 71 | 72 | fn new_service(&self, cfg: C) -> Self::Future { 73 | self.0.new_service(cfg) 74 | } 75 | } 76 | 77 | struct FactoryWrapper { 78 | factory: T, 79 | _t: std::marker::PhantomData, 80 | } 81 | 82 | impl ServiceFactory for FactoryWrapper 83 | where 84 | Req: 'static, 85 | Res: 'static, 86 | Err: 'static, 87 | InitErr: 'static, 88 | T: ServiceFactory< 89 | Config = C, 90 | Request = Req, 91 | Response = Res, 92 | Error = Err, 93 | InitError = InitErr, 94 | >, 95 | T::Future: 'static, 96 | T::Service: 'static, 97 | ::Future: 'static, 98 | { 99 | type Request = Req; 100 | type Response = Res; 101 | type Error = Err; 102 | type InitError = InitErr; 103 | type Config = C; 104 | type Service = BoxService; 105 | type Future = BoxFuture; 106 | 107 | fn new_service(&self, cfg: C) -> Self::Future { 108 | Box::pin( 109 | self.factory 110 | .new_service(cfg) 111 | .map(|res| res.map(ServiceWrapper::boxed)), 112 | ) 113 | } 114 | } 115 | 116 | struct ServiceWrapper(T); 117 | 118 | impl ServiceWrapper 119 | where 120 | T: Service + 'static, 121 | T::Future: 'static, 122 | { 123 | fn boxed(service: T) -> BoxService { 124 | Box::new(ServiceWrapper(service)) 125 | } 126 | } 127 | 128 | impl Service for ServiceWrapper 129 | where 130 | T: Service, 131 | T::Future: 'static, 132 | { 133 | type Request = Req; 134 | type Response = Res; 135 | type Error = Err; 136 | type Future = BoxFuture; 137 | 138 | fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll> { 139 | self.0.poll_ready(ctx) 140 | } 141 | 142 | fn call(&mut self, req: Self::Request) -> Self::Future { 143 | Box::pin(self.0.call(req)) 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /actix-utils/CHANGES.md: -------------------------------------------------------------------------------- 1 | # Changes 2 | 3 | ## [1.0.6] - 2020-01-08 4 | 5 | * Add `Clone` impl for `condition::Waiter` 6 | 7 | ## [1.0.5] - 2020-01-08 8 | 9 | * Add `Condition` type. 10 | 11 | * Add `Pool` of one-shot's. 12 | 13 | ## [1.0.4] - 2019-12-20 14 | 15 | * Add methods to check `LocalWaker` registration state. 16 | 17 | ## [1.0.3] - 2019-12-11 18 | 19 | * Revert InOrder service changes 20 | 21 | ## [1.0.2] - 2019-12-11 22 | 23 | * Allow to create `framed::Dispatcher` with custom `mpsc::Receiver` 24 | 25 | * Add `oneshot::Sender::is_canceled()` method 26 | 27 | ## [1.0.1] - 2019-12-11 28 | 29 | * Optimize InOrder service 30 | 31 | ## [1.0.0] - 2019-12-11 32 | 33 | * Simplify oneshot and mpsc implementations 34 | 35 | ## [1.0.0-alpha.3] - 2019-12-07 36 | 37 | * Migrate to tokio 0.2 38 | 39 | * Fix oneshot 40 | 41 | ## [1.0.0-alpha.2] - 2019-12-02 42 | 43 | * Migrate to `std::future` 44 | 45 | ## [0.4.7] - 2019-10-14 46 | 47 | * Re-register task on every framed transport poll. 48 | 49 | 50 | ## [0.4.6] - 2019-10-08 51 | 52 | * Refactor `Counter` type. register current task in available method. 53 | 54 | 55 | ## [0.4.5] - 2019-07-19 56 | 57 | ### Removed 58 | 59 | * Deprecated `CloneableService` as it is not safe 60 | 61 | 62 | ## [0.4.4] - 2019-07-17 63 | 64 | ### Changed 65 | 66 | * Undeprecate `FramedTransport` as it is actually useful 67 | 68 | 69 | ## [0.4.3] - 2019-07-17 70 | 71 | ### Deprecated 72 | 73 | * Deprecate `CloneableService` as it is not safe and in general not very useful 74 | 75 | * Deprecate `FramedTransport` in favor of `actix-ioframe` 76 | 77 | 78 | ## [0.4.2] - 2019-06-26 79 | 80 | ### Fixed 81 | 82 | * Do not block on sink drop for FramedTransport 83 | 84 | 85 | ## [0.4.1] - 2019-05-15 86 | 87 | ### Changed 88 | 89 | * Change `Either` constructor 90 | 91 | 92 | ## [0.4.0] - 2019-05-11 93 | 94 | ### Changed 95 | 96 | * Change `Either` to handle two nexted services 97 | 98 | * Upgrade actix-service 0.4 99 | 100 | ### Deleted 101 | 102 | * Framed related services 103 | 104 | * Stream related services 105 | 106 | ## [0.3.5] - 2019-04-04 107 | 108 | ### Added 109 | 110 | * Allow to send messages to `FramedTransport` via mpsc channel. 111 | 112 | ### Changed 113 | 114 | * Remove 'static constraint from Clonable service 115 | 116 | 117 | ## [0.3.4] - 2019-03-12 118 | 119 | ### Changed 120 | 121 | * `TimeoutService`, `InOrderService`, `InFlightService` accepts generic IntoService services. 122 | 123 | ### Fixed 124 | 125 | * Fix `InFlightService::poll_ready()` nested service readiness check 126 | 127 | * Fix `InOrderService::poll_ready()` nested service readiness check 128 | 129 | 130 | ## [0.3.3] - 2019-03-09 131 | 132 | ### Changed 133 | 134 | * Revert IntoFuture change 135 | 136 | * Add generic config param for IntoFramed and TakeOne new services 137 | 138 | 139 | ## [0.3.2] - 2019-03-04 140 | 141 | ### Changed 142 | 143 | * Use IntoFuture for new services 144 | 145 | 146 | ## [0.3.1] - 2019-03-04 147 | 148 | ### Changed 149 | 150 | * Use new type of transform trait 151 | 152 | 153 | ## [0.3.0] - 2019-03-02 154 | 155 | ### Changed 156 | 157 | * Use new `NewService` trait 158 | 159 | * BoxedNewService` and `BoxedService` types moved to actix-service crate. 160 | 161 | 162 | ## [0.2.4] - 2019-02-21 163 | 164 | ### Changed 165 | 166 | * Custom `BoxedNewService` implementation. 167 | 168 | 169 | ## [0.2.3] - 2019-02-21 170 | 171 | ### Added 172 | 173 | * Add `BoxedNewService` and `BoxedService` 174 | 175 | 176 | ## [0.2.2] - 2019-02-11 177 | 178 | ### Added 179 | 180 | * Add `Display` impl for `TimeoutError` 181 | 182 | * Add `Display` impl for `InOrderError` 183 | 184 | 185 | ## [0.2.1] - 2019-02-06 186 | 187 | ### Added 188 | 189 | * Add `InOrder` service. the service yields responses as they become available, 190 | in the order that their originating requests were submitted to the service. 191 | 192 | ### Changed 193 | 194 | * Convert `Timeout` and `InFlight` services to a transforms 195 | 196 | 197 | ## [0.2.0] - 2019-02-01 198 | 199 | * Fix framed transport error handling 200 | 201 | * Added Clone impl for Either service 202 | 203 | * Added Clone impl for Timeout service factory 204 | 205 | * Added Service and NewService for Stream dispatcher 206 | 207 | * Switch to actix-service 0.2 208 | 209 | 210 | ## [0.1.0] - 2018-12-09 211 | 212 | * Move utils services to separate crate 213 | -------------------------------------------------------------------------------- /actix-connect/src/ssl/rustls.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::future::Future; 3 | use std::marker::PhantomData; 4 | use std::pin::Pin; 5 | use std::sync::Arc; 6 | use std::task::{Context, Poll}; 7 | 8 | pub use rust_tls::Session; 9 | pub use tokio_rustls::{client::TlsStream, rustls::ClientConfig}; 10 | 11 | use actix_codec::{AsyncRead, AsyncWrite}; 12 | use actix_service::{Service, ServiceFactory}; 13 | use futures::future::{ok, Ready}; 14 | use tokio_rustls::{Connect, TlsConnector}; 15 | use webpki::DNSNameRef; 16 | 17 | use crate::{Address, Connection}; 18 | 19 | /// Rustls connector factory 20 | pub struct RustlsConnector { 21 | connector: Arc, 22 | _t: PhantomData<(T, U)>, 23 | } 24 | 25 | impl RustlsConnector { 26 | pub fn new(connector: Arc) -> Self { 27 | RustlsConnector { 28 | connector, 29 | _t: PhantomData, 30 | } 31 | } 32 | } 33 | 34 | impl RustlsConnector 35 | where 36 | T: Address, 37 | U: AsyncRead + AsyncWrite + Unpin + fmt::Debug, 38 | { 39 | pub fn service(connector: Arc) -> RustlsConnectorService { 40 | RustlsConnectorService { 41 | connector, 42 | _t: PhantomData, 43 | } 44 | } 45 | } 46 | 47 | impl Clone for RustlsConnector { 48 | fn clone(&self) -> Self { 49 | Self { 50 | connector: self.connector.clone(), 51 | _t: PhantomData, 52 | } 53 | } 54 | } 55 | 56 | impl ServiceFactory for RustlsConnector 57 | where 58 | U: AsyncRead + AsyncWrite + Unpin + fmt::Debug, 59 | { 60 | type Request = Connection; 61 | type Response = Connection>; 62 | type Error = std::io::Error; 63 | type Config = (); 64 | type Service = RustlsConnectorService; 65 | type InitError = (); 66 | type Future = Ready>; 67 | 68 | fn new_service(&self, _: ()) -> Self::Future { 69 | ok(RustlsConnectorService { 70 | connector: self.connector.clone(), 71 | _t: PhantomData, 72 | }) 73 | } 74 | } 75 | 76 | pub struct RustlsConnectorService { 77 | connector: Arc, 78 | _t: PhantomData<(T, U)>, 79 | } 80 | 81 | impl Clone for RustlsConnectorService { 82 | fn clone(&self) -> Self { 83 | Self { 84 | connector: self.connector.clone(), 85 | _t: PhantomData, 86 | } 87 | } 88 | } 89 | 90 | impl Service for RustlsConnectorService 91 | where 92 | U: AsyncRead + AsyncWrite + Unpin + fmt::Debug, 93 | { 94 | type Request = Connection; 95 | type Response = Connection>; 96 | type Error = std::io::Error; 97 | type Future = ConnectAsyncExt; 98 | 99 | fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { 100 | Poll::Ready(Ok(())) 101 | } 102 | 103 | fn call(&mut self, stream: Connection) -> Self::Future { 104 | trace!("SSL Handshake start for: {:?}", stream.host()); 105 | let (io, stream) = stream.replace(()); 106 | let host = DNSNameRef::try_from_ascii_str(stream.host()) 107 | .expect("rustls currently only handles hostname-based connections. See https://github.com/briansmith/webpki/issues/54"); 108 | ConnectAsyncExt { 109 | fut: TlsConnector::from(self.connector.clone()).connect(host, io), 110 | stream: Some(stream), 111 | } 112 | } 113 | } 114 | 115 | pub struct ConnectAsyncExt { 116 | fut: Connect, 117 | stream: Option>, 118 | } 119 | 120 | impl Future for ConnectAsyncExt 121 | where 122 | U: AsyncRead + AsyncWrite + Unpin + fmt::Debug, 123 | { 124 | type Output = Result>, std::io::Error>; 125 | 126 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 127 | let this = self.get_mut(); 128 | Poll::Ready( 129 | futures::ready!(Pin::new(&mut this.fut).poll(cx)).map(|stream| { 130 | let s = this.stream.take().unwrap(); 131 | trace!("SSL Handshake success: {:?}", s.host()); 132 | s.replace(stream).1 133 | }), 134 | ) 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /actix-rt/src/system.rs: -------------------------------------------------------------------------------- 1 | use std::cell::RefCell; 2 | use std::future::Future; 3 | use std::io; 4 | use std::sync::atomic::{AtomicUsize, Ordering}; 5 | 6 | use futures::channel::mpsc::UnboundedSender; 7 | use tokio::task::LocalSet; 8 | 9 | use crate::arbiter::{Arbiter, SystemCommand}; 10 | use crate::builder::{Builder, SystemRunner}; 11 | 12 | static SYSTEM_COUNT: AtomicUsize = AtomicUsize::new(0); 13 | 14 | /// System is a runtime manager. 15 | #[derive(Clone, Debug)] 16 | pub struct System { 17 | id: usize, 18 | sys: UnboundedSender, 19 | arbiter: Arbiter, 20 | stop_on_panic: bool, 21 | } 22 | 23 | thread_local!( 24 | static CURRENT: RefCell> = RefCell::new(None); 25 | ); 26 | 27 | impl System { 28 | /// Constructs new system and sets it as current 29 | pub(crate) fn construct( 30 | sys: UnboundedSender, 31 | arbiter: Arbiter, 32 | stop_on_panic: bool, 33 | ) -> Self { 34 | let sys = System { 35 | sys, 36 | arbiter, 37 | stop_on_panic, 38 | id: SYSTEM_COUNT.fetch_add(1, Ordering::SeqCst), 39 | }; 40 | System::set_current(sys.clone()); 41 | sys 42 | } 43 | 44 | /// Build a new system with a customized tokio runtime. 45 | /// 46 | /// This allows to customize the runtime. See struct level docs on 47 | /// `Builder` for more information. 48 | pub fn builder() -> Builder { 49 | Builder::new() 50 | } 51 | 52 | #[allow(clippy::new_ret_no_self)] 53 | /// Create new system. 54 | /// 55 | /// This method panics if it can not create tokio runtime 56 | pub fn new>(name: T) -> SystemRunner { 57 | Self::builder().name(name).build() 58 | } 59 | 60 | #[allow(clippy::new_ret_no_self)] 61 | /// Create new system using provided tokio Handle. 62 | /// 63 | /// This method panics if it can not spawn system arbiter 64 | pub fn run_in_tokio>( 65 | name: T, 66 | local: &LocalSet, 67 | ) -> impl Future> { 68 | Self::builder() 69 | .name(name) 70 | .build_async(local) 71 | .run_nonblocking() 72 | } 73 | 74 | /// Get current running system. 75 | pub fn current() -> System { 76 | CURRENT.with(|cell| match *cell.borrow() { 77 | Some(ref sys) => sys.clone(), 78 | None => panic!("System is not running"), 79 | }) 80 | } 81 | 82 | /// Check if current system is running. 83 | pub fn is_set() -> bool { 84 | CURRENT.with(|cell| cell.borrow().is_some()) 85 | } 86 | 87 | /// Set current running system. 88 | #[doc(hidden)] 89 | pub fn set_current(sys: System) { 90 | CURRENT.with(|s| { 91 | *s.borrow_mut() = Some(sys); 92 | }) 93 | } 94 | 95 | /// Execute function with system reference. 96 | pub fn with_current(f: F) -> R 97 | where 98 | F: FnOnce(&System) -> R, 99 | { 100 | CURRENT.with(|cell| match *cell.borrow() { 101 | Some(ref sys) => f(sys), 102 | None => panic!("System is not running"), 103 | }) 104 | } 105 | 106 | /// System id 107 | pub fn id(&self) -> usize { 108 | self.id 109 | } 110 | 111 | /// Stop the system 112 | pub fn stop(&self) { 113 | self.stop_with_code(0) 114 | } 115 | 116 | /// Stop the system with a particular exit code. 117 | pub fn stop_with_code(&self, code: i32) { 118 | let _ = self.sys.unbounded_send(SystemCommand::Exit(code)); 119 | } 120 | 121 | pub(crate) fn sys(&self) -> &UnboundedSender { 122 | &self.sys 123 | } 124 | 125 | /// Return status of 'stop_on_panic' option which controls whether the System is stopped when an 126 | /// uncaught panic is thrown from a worker thread. 127 | pub fn stop_on_panic(&self) -> bool { 128 | self.stop_on_panic 129 | } 130 | 131 | /// System arbiter 132 | pub fn arbiter(&self) -> &Arbiter { 133 | &self.arbiter 134 | } 135 | 136 | /// This function will start tokio runtime and will finish once the 137 | /// `System::stop()` message get called. 138 | /// Function `f` get called within tokio runtime context. 139 | pub fn run(f: F) -> io::Result<()> 140 | where 141 | F: FnOnce() + 'static, 142 | { 143 | Self::builder().run(f) 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /actix-testing/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Various helpers for Actix applications to use during testing. 2 | #![deny(rust_2018_idioms, warnings)] 3 | #![allow(clippy::type_complexity, clippy::needless_doctest_main)] 4 | 5 | use std::sync::mpsc; 6 | use std::{net, thread}; 7 | 8 | use actix_rt::{net::TcpStream, System}; 9 | use actix_server::{Server, ServerBuilder, ServiceFactory}; 10 | use net2::TcpBuilder; 11 | 12 | #[cfg(not(test))] // Work around for rust-lang/rust#62127 13 | pub use actix_macros::test; 14 | 15 | /// The `TestServer` type. 16 | /// 17 | /// `TestServer` is very simple test server that simplify process of writing 18 | /// integration tests for actix-net applications. 19 | /// 20 | /// # Examples 21 | /// 22 | /// ```rust 23 | /// use actix_service::fn_service; 24 | /// use actix_testing::TestServer; 25 | /// 26 | /// #[actix_rt::main] 27 | /// async fn main() { 28 | /// let srv = TestServer::with(|| fn_service( 29 | /// |sock| async move { 30 | /// println!("New connection: {:?}", sock); 31 | /// Ok::<_, ()>(()) 32 | /// } 33 | /// )); 34 | /// 35 | /// println!("SOCKET: {:?}", srv.connect()); 36 | /// } 37 | /// ``` 38 | pub struct TestServer; 39 | 40 | /// Test server runstime 41 | pub struct TestServerRuntime { 42 | addr: net::SocketAddr, 43 | host: String, 44 | port: u16, 45 | system: System, 46 | } 47 | 48 | impl TestServer { 49 | /// Start new server with server builder 50 | pub fn start(mut factory: F) -> TestServerRuntime 51 | where 52 | F: FnMut(ServerBuilder) -> ServerBuilder + Send + 'static, 53 | { 54 | let (tx, rx) = mpsc::channel(); 55 | 56 | // run server in separate thread 57 | thread::spawn(move || { 58 | let sys = System::new("actix-test-server"); 59 | factory(Server::build()) 60 | .workers(1) 61 | .disable_signals() 62 | .start(); 63 | 64 | tx.send(System::current()).unwrap(); 65 | sys.run() 66 | }); 67 | let system = rx.recv().unwrap(); 68 | 69 | TestServerRuntime { 70 | system, 71 | addr: "127.0.0.1:0".parse().unwrap(), 72 | host: "127.0.0.1".to_string(), 73 | port: 0, 74 | } 75 | } 76 | 77 | /// Start new test server with application factory 78 | pub fn with>(factory: F) -> TestServerRuntime { 79 | let (tx, rx) = mpsc::channel(); 80 | 81 | // run server in separate thread 82 | thread::spawn(move || { 83 | let sys = System::new("actix-test-server"); 84 | let tcp = net::TcpListener::bind("127.0.0.1:0").unwrap(); 85 | let local_addr = tcp.local_addr().unwrap(); 86 | 87 | Server::build() 88 | .listen("test", tcp, factory)? 89 | .workers(1) 90 | .disable_signals() 91 | .start(); 92 | 93 | tx.send((System::current(), local_addr)).unwrap(); 94 | sys.run() 95 | }); 96 | 97 | let (system, addr) = rx.recv().unwrap(); 98 | 99 | let host = format!("{}", addr.ip()); 100 | let port = addr.port(); 101 | 102 | TestServerRuntime { 103 | system, 104 | addr, 105 | host, 106 | port, 107 | } 108 | } 109 | 110 | /// Get firat available unused local address 111 | pub fn unused_addr() -> net::SocketAddr { 112 | let addr: net::SocketAddr = "127.0.0.1:0".parse().unwrap(); 113 | let socket = TcpBuilder::new_v4().unwrap(); 114 | socket.bind(&addr).unwrap(); 115 | socket.reuse_address(true).unwrap(); 116 | let tcp = socket.to_tcp_listener().unwrap(); 117 | tcp.local_addr().unwrap() 118 | } 119 | } 120 | 121 | impl TestServerRuntime { 122 | /// Test server host 123 | pub fn host(&self) -> &str { 124 | &self.host 125 | } 126 | 127 | /// Test server port 128 | pub fn port(&self) -> u16 { 129 | self.port 130 | } 131 | 132 | /// Get test server address 133 | pub fn addr(&self) -> net::SocketAddr { 134 | self.addr 135 | } 136 | 137 | /// Stop http server 138 | fn stop(&mut self) { 139 | self.system.stop(); 140 | } 141 | 142 | /// Connect to server, return tokio TcpStream 143 | pub fn connect(&self) -> std::io::Result { 144 | TcpStream::from_std(net::TcpStream::connect(self.addr)?) 145 | } 146 | } 147 | 148 | impl Drop for TestServerRuntime { 149 | fn drop(&mut self) { 150 | self.stop() 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /actix-connect/tests/test_connect.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | 3 | use actix_codec::{BytesCodec, Framed}; 4 | use actix_rt::net::TcpStream; 5 | use actix_service::{fn_service, Service, ServiceFactory}; 6 | use actix_testing::TestServer; 7 | use bytes::Bytes; 8 | use futures::SinkExt; 9 | 10 | use actix_connect::resolver::{ResolverConfig, ResolverOpts}; 11 | use actix_connect::Connect; 12 | 13 | #[cfg(feature = "openssl")] 14 | #[actix_rt::test] 15 | async fn test_string() { 16 | let srv = TestServer::with(|| { 17 | fn_service(|io: TcpStream| { 18 | async { 19 | let mut framed = Framed::new(io, BytesCodec); 20 | framed.send(Bytes::from_static(b"test")).await?; 21 | Ok::<_, io::Error>(()) 22 | } 23 | }) 24 | }); 25 | 26 | let mut conn = actix_connect::default_connector(); 27 | let addr = format!("localhost:{}", srv.port()); 28 | let con = conn.call(addr.into()).await.unwrap(); 29 | assert_eq!(con.peer_addr().unwrap(), srv.addr()); 30 | } 31 | 32 | #[cfg(feature = "rustls")] 33 | #[actix_rt::test] 34 | async fn test_rustls_string() { 35 | let srv = TestServer::with(|| { 36 | fn_service(|io: TcpStream| { 37 | async { 38 | let mut framed = Framed::new(io, BytesCodec); 39 | framed.send(Bytes::from_static(b"test")).await?; 40 | Ok::<_, io::Error>(()) 41 | } 42 | }) 43 | }); 44 | 45 | let mut conn = actix_connect::default_connector(); 46 | let addr = format!("localhost:{}", srv.port()); 47 | let con = conn.call(addr.into()).await.unwrap(); 48 | assert_eq!(con.peer_addr().unwrap(), srv.addr()); 49 | } 50 | 51 | #[actix_rt::test] 52 | async fn test_static_str() { 53 | let srv = TestServer::with(|| { 54 | fn_service(|io: TcpStream| { 55 | async { 56 | let mut framed = Framed::new(io, BytesCodec); 57 | framed.send(Bytes::from_static(b"test")).await?; 58 | Ok::<_, io::Error>(()) 59 | } 60 | }) 61 | }); 62 | 63 | let resolver = actix_connect::start_default_resolver(); 64 | let mut conn = actix_connect::new_connector(resolver.clone()); 65 | 66 | let con = conn.call(Connect::with("10", srv.addr())).await.unwrap(); 67 | assert_eq!(con.peer_addr().unwrap(), srv.addr()); 68 | 69 | let connect = Connect::new(srv.host().to_owned()); 70 | let mut conn = actix_connect::new_connector(resolver); 71 | let con = conn.call(connect).await; 72 | assert!(con.is_err()); 73 | } 74 | 75 | #[actix_rt::test] 76 | async fn test_new_service() { 77 | let srv = TestServer::with(|| { 78 | fn_service(|io: TcpStream| { 79 | async { 80 | let mut framed = Framed::new(io, BytesCodec); 81 | framed.send(Bytes::from_static(b"test")).await?; 82 | Ok::<_, io::Error>(()) 83 | } 84 | }) 85 | }); 86 | 87 | let resolver = 88 | actix_connect::start_resolver(ResolverConfig::default(), ResolverOpts::default()); 89 | 90 | let factory = actix_connect::new_connector_factory(resolver); 91 | 92 | let mut conn = factory.new_service(()).await.unwrap(); 93 | let con = conn.call(Connect::with("10", srv.addr())).await.unwrap(); 94 | assert_eq!(con.peer_addr().unwrap(), srv.addr()); 95 | } 96 | 97 | #[cfg(feature = "openssl")] 98 | #[actix_rt::test] 99 | async fn test_uri() { 100 | use std::convert::TryFrom; 101 | 102 | let srv = TestServer::with(|| { 103 | fn_service(|io: TcpStream| { 104 | async { 105 | let mut framed = Framed::new(io, BytesCodec); 106 | framed.send(Bytes::from_static(b"test")).await?; 107 | Ok::<_, io::Error>(()) 108 | } 109 | }) 110 | }); 111 | 112 | let mut conn = actix_connect::default_connector(); 113 | let addr = http::Uri::try_from(format!("https://localhost:{}", srv.port())).unwrap(); 114 | let con = conn.call(addr.into()).await.unwrap(); 115 | assert_eq!(con.peer_addr().unwrap(), srv.addr()); 116 | } 117 | 118 | #[cfg(feature = "rustls")] 119 | #[actix_rt::test] 120 | async fn test_rustls_uri() { 121 | use std::convert::TryFrom; 122 | 123 | let srv = TestServer::with(|| { 124 | fn_service(|io: TcpStream| { 125 | async { 126 | let mut framed = Framed::new(io, BytesCodec); 127 | framed.send(Bytes::from_static(b"test")).await?; 128 | Ok::<_, io::Error>(()) 129 | } 130 | }) 131 | }); 132 | 133 | let mut conn = actix_connect::default_connector(); 134 | let addr = http::Uri::try_from(format!("https://localhost:{}", srv.port())).unwrap(); 135 | let con = conn.call(addr.into()).await.unwrap(); 136 | assert_eq!(con.peer_addr().unwrap(), srv.addr()); 137 | } 138 | -------------------------------------------------------------------------------- /actix-utils/src/either.rs: -------------------------------------------------------------------------------- 1 | //! Contains `Either` service and related types and functions. 2 | use std::pin::Pin; 3 | use std::task::{Context, Poll}; 4 | 5 | use actix_service::{Service, ServiceFactory}; 6 | use futures::{future, ready, Future}; 7 | 8 | /// Combine two different service types into a single type. 9 | /// 10 | /// Both services must be of the same request, response, and error types. 11 | /// `EitherService` is useful for handling conditional branching in service 12 | /// middleware to different inner service types. 13 | pub struct EitherService { 14 | left: A, 15 | right: B, 16 | } 17 | 18 | impl Clone for EitherService { 19 | fn clone(&self) -> Self { 20 | EitherService { 21 | left: self.left.clone(), 22 | right: self.right.clone(), 23 | } 24 | } 25 | } 26 | 27 | impl Service for EitherService 28 | where 29 | A: Service, 30 | B: Service, 31 | { 32 | type Request = either::Either; 33 | type Response = A::Response; 34 | type Error = A::Error; 35 | type Future = future::Either; 36 | 37 | fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { 38 | let left = self.left.poll_ready(cx)?; 39 | let right = self.right.poll_ready(cx)?; 40 | 41 | if left.is_ready() && right.is_ready() { 42 | Poll::Ready(Ok(())) 43 | } else { 44 | Poll::Pending 45 | } 46 | } 47 | 48 | fn call(&mut self, req: either::Either) -> Self::Future { 49 | match req { 50 | either::Either::Left(req) => future::Either::Left(self.left.call(req)), 51 | either::Either::Right(req) => future::Either::Right(self.right.call(req)), 52 | } 53 | } 54 | } 55 | 56 | /// Combine two different new service types into a single service. 57 | pub struct Either { 58 | left: A, 59 | right: B, 60 | } 61 | 62 | impl Either { 63 | pub fn new(left: A, right: B) -> Either 64 | where 65 | A: ServiceFactory, 66 | A::Config: Clone, 67 | B: ServiceFactory< 68 | Config = A::Config, 69 | Response = A::Response, 70 | Error = A::Error, 71 | InitError = A::InitError, 72 | >, 73 | { 74 | Either { left, right } 75 | } 76 | } 77 | 78 | impl ServiceFactory for Either 79 | where 80 | A: ServiceFactory, 81 | A::Config: Clone, 82 | B: ServiceFactory< 83 | Config = A::Config, 84 | Response = A::Response, 85 | Error = A::Error, 86 | InitError = A::InitError, 87 | >, 88 | { 89 | type Request = either::Either; 90 | type Response = A::Response; 91 | type Error = A::Error; 92 | type InitError = A::InitError; 93 | type Config = A::Config; 94 | type Service = EitherService; 95 | type Future = EitherNewService; 96 | 97 | fn new_service(&self, cfg: A::Config) -> Self::Future { 98 | EitherNewService { 99 | left: None, 100 | right: None, 101 | left_fut: self.left.new_service(cfg.clone()), 102 | right_fut: self.right.new_service(cfg), 103 | } 104 | } 105 | } 106 | 107 | impl Clone for Either { 108 | fn clone(&self) -> Self { 109 | Self { 110 | left: self.left.clone(), 111 | right: self.right.clone(), 112 | } 113 | } 114 | } 115 | 116 | #[doc(hidden)] 117 | #[pin_project::pin_project] 118 | pub struct EitherNewService { 119 | left: Option, 120 | right: Option, 121 | #[pin] 122 | left_fut: A::Future, 123 | #[pin] 124 | right_fut: B::Future, 125 | } 126 | 127 | impl Future for EitherNewService 128 | where 129 | A: ServiceFactory, 130 | B: ServiceFactory, 131 | { 132 | type Output = Result, A::InitError>; 133 | 134 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 135 | let this = self.project(); 136 | 137 | if this.left.is_none() { 138 | *this.left = Some(ready!(this.left_fut.poll(cx))?); 139 | } 140 | if this.right.is_none() { 141 | *this.right = Some(ready!(this.right_fut.poll(cx))?); 142 | } 143 | 144 | if this.left.is_some() && this.right.is_some() { 145 | Poll::Ready(Ok(EitherService { 146 | left: this.left.take().unwrap(), 147 | right: this.right.take().unwrap(), 148 | })) 149 | } else { 150 | Poll::Pending 151 | } 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /actix-utils/src/inflight.rs: -------------------------------------------------------------------------------- 1 | use std::convert::Infallible; 2 | use std::future::Future; 3 | use std::pin::Pin; 4 | use std::task::{Context, Poll}; 5 | 6 | use actix_service::{IntoService, Service, Transform}; 7 | use futures::future::{ok, Ready}; 8 | 9 | use super::counter::{Counter, CounterGuard}; 10 | 11 | /// InFlight - new service for service that can limit number of in-flight 12 | /// async requests. 13 | /// 14 | /// Default number of in-flight requests is 15 15 | pub struct InFlight { 16 | max_inflight: usize, 17 | } 18 | 19 | impl InFlight { 20 | pub fn new(max: usize) -> Self { 21 | Self { max_inflight: max } 22 | } 23 | } 24 | 25 | impl Default for InFlight { 26 | fn default() -> Self { 27 | Self::new(15) 28 | } 29 | } 30 | 31 | impl Transform for InFlight 32 | where 33 | S: Service, 34 | { 35 | type Request = S::Request; 36 | type Response = S::Response; 37 | type Error = S::Error; 38 | type InitError = Infallible; 39 | type Transform = InFlightService; 40 | type Future = Ready>; 41 | 42 | fn new_transform(&self, service: S) -> Self::Future { 43 | ok(InFlightService::new(self.max_inflight, service)) 44 | } 45 | } 46 | 47 | pub struct InFlightService { 48 | count: Counter, 49 | service: S, 50 | } 51 | 52 | impl InFlightService 53 | where 54 | S: Service, 55 | { 56 | pub fn new(max: usize, service: U) -> Self 57 | where 58 | U: IntoService, 59 | { 60 | Self { 61 | count: Counter::new(max), 62 | service: service.into_service(), 63 | } 64 | } 65 | } 66 | 67 | impl Service for InFlightService 68 | where 69 | T: Service, 70 | { 71 | type Request = T::Request; 72 | type Response = T::Response; 73 | type Error = T::Error; 74 | type Future = InFlightServiceResponse; 75 | 76 | fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { 77 | if let Poll::Pending = self.service.poll_ready(cx)? { 78 | Poll::Pending 79 | } else if !self.count.available(cx) { 80 | log::trace!("InFlight limit exceeded"); 81 | Poll::Pending 82 | } else { 83 | Poll::Ready(Ok(())) 84 | } 85 | } 86 | 87 | fn call(&mut self, req: T::Request) -> Self::Future { 88 | InFlightServiceResponse { 89 | fut: self.service.call(req), 90 | _guard: self.count.get(), 91 | } 92 | } 93 | } 94 | 95 | #[doc(hidden)] 96 | #[pin_project::pin_project] 97 | pub struct InFlightServiceResponse { 98 | #[pin] 99 | fut: T::Future, 100 | _guard: CounterGuard, 101 | } 102 | 103 | impl Future for InFlightServiceResponse { 104 | type Output = Result; 105 | 106 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 107 | self.project().fut.poll(cx) 108 | } 109 | } 110 | 111 | #[cfg(test)] 112 | mod tests { 113 | 114 | use std::task::{Context, Poll}; 115 | use std::time::Duration; 116 | 117 | use super::*; 118 | use actix_service::{apply, fn_factory, Service, ServiceFactory}; 119 | use futures::future::{lazy, ok, FutureExt, LocalBoxFuture}; 120 | 121 | struct SleepService(Duration); 122 | 123 | impl Service for SleepService { 124 | type Request = (); 125 | type Response = (); 126 | type Error = (); 127 | type Future = LocalBoxFuture<'static, Result<(), ()>>; 128 | 129 | fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { 130 | Poll::Ready(Ok(())) 131 | } 132 | 133 | fn call(&mut self, _: ()) -> Self::Future { 134 | actix_rt::time::delay_for(self.0) 135 | .then(|_| ok::<_, ()>(())) 136 | .boxed_local() 137 | } 138 | } 139 | 140 | #[actix_rt::test] 141 | async fn test_transform() { 142 | let wait_time = Duration::from_millis(50); 143 | 144 | let mut srv = InFlightService::new(1, SleepService(wait_time)); 145 | assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(()))); 146 | 147 | let res = srv.call(()); 148 | assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Pending); 149 | 150 | let _ = res.await; 151 | assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(()))); 152 | } 153 | 154 | #[actix_rt::test] 155 | async fn test_newtransform() { 156 | let wait_time = Duration::from_millis(50); 157 | 158 | let srv = apply(InFlight::new(1), fn_factory(|| ok(SleepService(wait_time)))); 159 | 160 | let mut srv = srv.new_service(&()).await.unwrap(); 161 | assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(()))); 162 | 163 | let res = srv.call(()); 164 | assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Pending); 165 | 166 | let _ = res.await; 167 | assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(()))); 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /actix-service/CHANGES.md: -------------------------------------------------------------------------------- 1 | # Changes 2 | 3 | ## [1.0.5] - 2020-01-16 4 | 5 | ### Fixed 6 | 7 | * Fixed unsoundness in .and_then()/.then() service combinators 8 | 9 | ## [1.0.4] - 2020-01-15 10 | 11 | ### Fixed 12 | 13 | * Revert 1.0.3 change 14 | 15 | ## [1.0.3] - 2020-01-15 16 | 17 | ### Fixed 18 | 19 | * Fixed unsoundness in `AndThenService` impl 20 | 21 | ## [1.0.2] - 2020-01-08 22 | 23 | ### Added 24 | 25 | * Add `into_service` helper function 26 | 27 | 28 | ## [1.0.1] - 2019-12-22 29 | 30 | ### Changed 31 | 32 | * `map_config()` and `unit_config()` accepts `IntoServiceFactory` type 33 | 34 | 35 | ## [1.0.0] - 2019-12-11 36 | 37 | ### Added 38 | 39 | * Add Clone impl for Apply service 40 | 41 | 42 | ## [1.0.0-alpha.4] - 2019-12-08 43 | 44 | ### Changed 45 | 46 | * Renamed `service_fn` to `fn_service` 47 | 48 | * Renamed `factory_fn` to `fn_factory` 49 | 50 | * Renamed `factory_fn_cfg` to `fn_factory_with_config` 51 | 52 | 53 | ## [1.0.0-alpha.3] - 2019-12-06 54 | 55 | ### Changed 56 | 57 | * Add missing Clone impls 58 | 59 | * Restore `Transform::map_init_err()` combinator 60 | 61 | * Restore `Service/Factory::apply_fn()` in form of `Pipeline/Factory::and_then_apply_fn()` 62 | 63 | * Optimize service combinators and futures memory layout 64 | 65 | 66 | ## [1.0.0-alpha.2] - 2019-12-02 67 | 68 | ### Changed 69 | 70 | * Use owned config value for service factory 71 | 72 | * Renamed BoxedNewService/BoxedService to BoxServiceFactory/BoxService 73 | 74 | 75 | ## [1.0.0-alpha.1] - 2019-11-25 76 | 77 | ### Changed 78 | 79 | * Migraded to `std::future` 80 | 81 | * `NewService` renamed to `ServiceFactory` 82 | 83 | * Added `pipeline` and `pipeline_factory` function 84 | 85 | 86 | ## [0.4.2] - 2019-08-27 87 | 88 | ### Fixed 89 | 90 | * Check service readiness for `new_apply_cfg` combinator 91 | 92 | 93 | ## [0.4.1] - 2019-06-06 94 | 95 | ### Added 96 | 97 | * Add `new_apply_cfg` function 98 | 99 | ## [0.4.0] - 2019-05-12 100 | 101 | ### Changed 102 | 103 | * Use associated type for `NewService` config 104 | 105 | * Change `apply_cfg` function 106 | 107 | * Renamed helper functions 108 | 109 | ### Added 110 | 111 | * Add `NewService::map_config` and `NewService::unit_config` combinators 112 | 113 | 114 | ## [0.3.6] - 2019-04-07 115 | 116 | ### Changed 117 | 118 | * Poll boxed service call result immediately 119 | 120 | 121 | ## [0.3.5] - 2019-03-29 122 | 123 | ### Added 124 | 125 | * Add `impl Service for Rc>` 126 | 127 | 128 | ## [0.3.4] - 2019-03-12 129 | 130 | ### Added 131 | 132 | * Add `Transform::from_err()` combinator 133 | 134 | * Add `apply_fn` helper 135 | 136 | * Add `apply_fn_factory` helper 137 | 138 | * Add `apply_transform` helper 139 | 140 | * Add `apply_cfg` helper 141 | 142 | 143 | ## [0.3.3] - 2019-03-09 144 | 145 | ### Added 146 | 147 | * Add `ApplyTransform` new service for transform and new service. 148 | 149 | * Add `NewService::apply_cfg()` combinator, allows to use 150 | nested `NewService` with different config parameter. 151 | 152 | ### Changed 153 | 154 | * Revert IntoFuture change 155 | 156 | 157 | ## [0.3.2] - 2019-03-04 158 | 159 | ### Changed 160 | 161 | * Change `NewService::Future` and `Transform::Future` to the `IntoFuture` trait. 162 | 163 | * Export `AndThenTransform` type 164 | 165 | 166 | ## [0.3.1] - 2019-03-04 167 | 168 | ### Changed 169 | 170 | * Simplify Transform trait 171 | 172 | 173 | ## [0.3.0] - 2019-03-02 174 | 175 | ## Added 176 | 177 | * Added boxed NewService and Service. 178 | 179 | ## Changed 180 | 181 | * Added `Config` parameter to `NewService` trait. 182 | 183 | * Added `Config` parameter to `NewTransform` trait. 184 | 185 | 186 | ## [0.2.2] - 2019-02-19 187 | 188 | ### Added 189 | 190 | * Added `NewService` impl for `Rc where S: NewService` 191 | 192 | * Added `NewService` impl for `Arc where S: NewService` 193 | 194 | 195 | ## [0.2.1] - 2019-02-03 196 | 197 | ### Changed 198 | 199 | * Generalize `.apply` combinator with Transform trait 200 | 201 | 202 | ## [0.2.0] - 2019-02-01 203 | 204 | ### Changed 205 | 206 | * Use associated type instead of generic for Service definition. 207 | 208 | * Before: 209 | 210 | ```rust 211 | impl Service for Client { 212 | type Response = Response; 213 | // ... 214 | } 215 | ``` 216 | * After: 217 | 218 | ```rust 219 | impl Service for Client { 220 | type Request = Request; 221 | type Response = Response; 222 | // ... 223 | } 224 | ``` 225 | 226 | 227 | ## [0.1.6] - 2019-01-24 228 | 229 | ### Changed 230 | 231 | * Use `FnMut` instead of `Fn` for .apply() and .map() combinators and `FnService` type 232 | 233 | * Change `.apply()` error semantic, new service's error is `From` 234 | 235 | 236 | ## [0.1.5] - 2019-01-13 237 | 238 | ### Changed 239 | 240 | * Make `Out::Error` convertable from `T::Error` for apply combinator 241 | 242 | 243 | ## [0.1.4] - 2019-01-11 244 | 245 | ### Changed 246 | 247 | * Use `FnMut` instead of `Fn` for `FnService` 248 | 249 | 250 | ## [0.1.3] - 2018-12-12 251 | 252 | ### Changed 253 | 254 | * Split service combinators to separate trait 255 | 256 | 257 | ## [0.1.2] - 2018-12-12 258 | 259 | ### Fixed 260 | 261 | * Release future early for `.and_then()` and `.then()` combinators 262 | 263 | 264 | ## [0.1.1] - 2018-12-09 265 | 266 | ### Added 267 | 268 | * Added Service impl for Box 269 | 270 | 271 | ## [0.1.0] - 2018-12-09 272 | 273 | * Initial import 274 | -------------------------------------------------------------------------------- /actix-server/src/service.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | use std::net::SocketAddr; 3 | use std::task::{Context, Poll}; 4 | use std::time::Duration; 5 | 6 | use actix_rt::spawn; 7 | use actix_service::{self as actix, Service, ServiceFactory as ActixServiceFactory}; 8 | use actix_utils::counter::CounterGuard; 9 | use futures::future::{err, ok, LocalBoxFuture, Ready}; 10 | use futures::{FutureExt, TryFutureExt}; 11 | use log::error; 12 | 13 | use super::Token; 14 | use crate::socket::{FromStream, StdStream}; 15 | 16 | /// Server message 17 | pub(crate) enum ServerMessage { 18 | /// New stream 19 | Connect(StdStream), 20 | /// Gracefull shutdown 21 | Shutdown(Duration), 22 | /// Force shutdown 23 | ForceShutdown, 24 | } 25 | 26 | pub trait ServiceFactory: Send + Clone + 'static { 27 | type Factory: actix::ServiceFactory; 28 | 29 | fn create(&self) -> Self::Factory; 30 | } 31 | 32 | pub(crate) trait InternalServiceFactory: Send { 33 | fn name(&self, token: Token) -> &str; 34 | 35 | fn clone_factory(&self) -> Box; 36 | 37 | fn create(&self) -> LocalBoxFuture<'static, Result, ()>>; 38 | } 39 | 40 | pub(crate) type BoxedServerService = Box< 41 | dyn Service< 42 | Request = (Option, ServerMessage), 43 | Response = (), 44 | Error = (), 45 | Future = Ready>, 46 | >, 47 | >; 48 | 49 | pub(crate) struct StreamService { 50 | service: T, 51 | } 52 | 53 | impl StreamService { 54 | pub(crate) fn new(service: T) -> Self { 55 | StreamService { service } 56 | } 57 | } 58 | 59 | impl Service for StreamService 60 | where 61 | T: Service, 62 | T::Future: 'static, 63 | T::Error: 'static, 64 | I: FromStream, 65 | { 66 | type Request = (Option, ServerMessage); 67 | type Response = (); 68 | type Error = (); 69 | type Future = Ready>; 70 | 71 | fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll> { 72 | self.service.poll_ready(ctx).map_err(|_| ()) 73 | } 74 | 75 | fn call(&mut self, (guard, req): (Option, ServerMessage)) -> Self::Future { 76 | match req { 77 | ServerMessage::Connect(stream) => { 78 | let stream = FromStream::from_stdstream(stream).map_err(|e| { 79 | error!("Can not convert to an async tcp stream: {}", e); 80 | }); 81 | 82 | if let Ok(stream) = stream { 83 | let f = self.service.call(stream); 84 | spawn(async move { 85 | let _ = f.await; 86 | drop(guard); 87 | }); 88 | ok(()) 89 | } else { 90 | err(()) 91 | } 92 | } 93 | _ => ok(()), 94 | } 95 | } 96 | } 97 | 98 | pub(crate) struct StreamNewService, Io: FromStream> { 99 | name: String, 100 | inner: F, 101 | token: Token, 102 | addr: SocketAddr, 103 | _t: PhantomData, 104 | } 105 | 106 | impl StreamNewService 107 | where 108 | F: ServiceFactory, 109 | Io: FromStream + Send + 'static, 110 | { 111 | pub(crate) fn create( 112 | name: String, 113 | token: Token, 114 | inner: F, 115 | addr: SocketAddr, 116 | ) -> Box { 117 | Box::new(Self { 118 | name, 119 | token, 120 | inner, 121 | addr, 122 | _t: PhantomData, 123 | }) 124 | } 125 | } 126 | 127 | impl InternalServiceFactory for StreamNewService 128 | where 129 | F: ServiceFactory, 130 | Io: FromStream + Send + 'static, 131 | { 132 | fn name(&self, _: Token) -> &str { 133 | &self.name 134 | } 135 | 136 | fn clone_factory(&self) -> Box { 137 | Box::new(Self { 138 | name: self.name.clone(), 139 | inner: self.inner.clone(), 140 | token: self.token, 141 | addr: self.addr, 142 | _t: PhantomData, 143 | }) 144 | } 145 | 146 | fn create(&self) -> LocalBoxFuture<'static, Result, ()>> { 147 | let token = self.token; 148 | self.inner 149 | .create() 150 | .new_service(()) 151 | .map_err(|_| ()) 152 | .map_ok(move |inner| { 153 | let service: BoxedServerService = Box::new(StreamService::new(inner)); 154 | vec![(token, service)] 155 | }) 156 | .boxed_local() 157 | } 158 | } 159 | 160 | impl InternalServiceFactory for Box { 161 | fn name(&self, token: Token) -> &str { 162 | self.as_ref().name(token) 163 | } 164 | 165 | fn clone_factory(&self) -> Box { 166 | self.as_ref().clone_factory() 167 | } 168 | 169 | fn create(&self) -> LocalBoxFuture<'static, Result, ()>> { 170 | self.as_ref().create() 171 | } 172 | } 173 | 174 | impl ServiceFactory for F 175 | where 176 | F: Fn() -> T + Send + Clone + 'static, 177 | T: actix::ServiceFactory, 178 | I: FromStream, 179 | { 180 | type Factory = T; 181 | 182 | fn create(&self) -> T { 183 | (self)() 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /actix-connect/src/connector.rs: -------------------------------------------------------------------------------- 1 | use std::collections::VecDeque; 2 | use std::future::Future; 3 | use std::io; 4 | use std::marker::PhantomData; 5 | use std::net::SocketAddr; 6 | use std::pin::Pin; 7 | use std::task::{Context, Poll}; 8 | 9 | use actix_rt::net::TcpStream; 10 | use actix_service::{Service, ServiceFactory}; 11 | use futures::future::{err, ok, BoxFuture, Either, FutureExt, Ready}; 12 | 13 | use super::connect::{Address, Connect, Connection}; 14 | use super::error::ConnectError; 15 | 16 | /// Tcp connector service factory 17 | #[derive(Debug)] 18 | pub struct TcpConnectorFactory(PhantomData); 19 | 20 | impl TcpConnectorFactory { 21 | pub fn new() -> Self { 22 | TcpConnectorFactory(PhantomData) 23 | } 24 | 25 | /// Create tcp connector service 26 | pub fn service(&self) -> TcpConnector { 27 | TcpConnector(PhantomData) 28 | } 29 | } 30 | 31 | impl Default for TcpConnectorFactory { 32 | fn default() -> Self { 33 | TcpConnectorFactory(PhantomData) 34 | } 35 | } 36 | 37 | impl Clone for TcpConnectorFactory { 38 | fn clone(&self) -> Self { 39 | TcpConnectorFactory(PhantomData) 40 | } 41 | } 42 | 43 | impl ServiceFactory for TcpConnectorFactory { 44 | type Request = Connect; 45 | type Response = Connection; 46 | type Error = ConnectError; 47 | type Config = (); 48 | type Service = TcpConnector; 49 | type InitError = (); 50 | type Future = Ready>; 51 | 52 | fn new_service(&self, _: ()) -> Self::Future { 53 | ok(self.service()) 54 | } 55 | } 56 | 57 | /// Tcp connector service 58 | #[derive(Default, Debug)] 59 | pub struct TcpConnector(PhantomData); 60 | 61 | impl TcpConnector { 62 | pub fn new() -> Self { 63 | TcpConnector(PhantomData) 64 | } 65 | } 66 | 67 | impl Clone for TcpConnector { 68 | fn clone(&self) -> Self { 69 | TcpConnector(PhantomData) 70 | } 71 | } 72 | 73 | impl Service for TcpConnector { 74 | type Request = Connect; 75 | type Response = Connection; 76 | type Error = ConnectError; 77 | type Future = Either, Ready>>; 78 | 79 | fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { 80 | Poll::Ready(Ok(())) 81 | } 82 | 83 | fn call(&mut self, req: Connect) -> Self::Future { 84 | let port = req.port(); 85 | let Connect { req, addr, .. } = req; 86 | 87 | if let Some(addr) = addr { 88 | Either::Left(TcpConnectorResponse::new(req, port, addr)) 89 | } else { 90 | error!("TCP connector: got unresolved address"); 91 | Either::Right(err(ConnectError::Unresolverd)) 92 | } 93 | } 94 | } 95 | 96 | #[doc(hidden)] 97 | /// Tcp stream connector response future 98 | pub struct TcpConnectorResponse { 99 | req: Option, 100 | port: u16, 101 | addrs: Option>, 102 | stream: Option>>, 103 | } 104 | 105 | impl TcpConnectorResponse { 106 | pub fn new( 107 | req: T, 108 | port: u16, 109 | addr: either::Either>, 110 | ) -> TcpConnectorResponse { 111 | trace!( 112 | "TCP connector - connecting to {:?} port:{}", 113 | req.host(), 114 | port 115 | ); 116 | 117 | match addr { 118 | either::Either::Left(addr) => TcpConnectorResponse { 119 | req: Some(req), 120 | port, 121 | addrs: None, 122 | stream: Some(TcpStream::connect(addr).boxed()), 123 | }, 124 | either::Either::Right(addrs) => TcpConnectorResponse { 125 | req: Some(req), 126 | port, 127 | addrs: Some(addrs), 128 | stream: None, 129 | }, 130 | } 131 | } 132 | } 133 | 134 | impl Future for TcpConnectorResponse { 135 | type Output = Result, ConnectError>; 136 | 137 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 138 | let this = self.get_mut(); 139 | 140 | // connect 141 | loop { 142 | if let Some(new) = this.stream.as_mut() { 143 | match new.as_mut().poll(cx) { 144 | Poll::Ready(Ok(sock)) => { 145 | let req = this.req.take().unwrap(); 146 | trace!( 147 | "TCP connector - successfully connected to connecting to {:?} - {:?}", 148 | req.host(), sock.peer_addr() 149 | ); 150 | return Poll::Ready(Ok(Connection::new(sock, req))); 151 | } 152 | Poll::Pending => return Poll::Pending, 153 | Poll::Ready(Err(err)) => { 154 | trace!( 155 | "TCP connector - failed to connect to connecting to {:?} port: {}", 156 | this.req.as_ref().unwrap().host(), 157 | this.port, 158 | ); 159 | if this.addrs.is_none() || this.addrs.as_ref().unwrap().is_empty() { 160 | return Poll::Ready(Err(err.into())); 161 | } 162 | } 163 | } 164 | } 165 | 166 | // try to connect 167 | let addr = this.addrs.as_mut().unwrap().pop_front().unwrap(); 168 | this.stream = Some(TcpStream::connect(addr).boxed()); 169 | } 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /actix-server/src/socket.rs: -------------------------------------------------------------------------------- 1 | use std::{fmt, io, net}; 2 | 3 | use actix_codec::{AsyncRead, AsyncWrite}; 4 | use actix_rt::net::TcpStream; 5 | 6 | pub(crate) enum StdListener { 7 | Tcp(net::TcpListener), 8 | #[cfg(all(unix))] 9 | Uds(std::os::unix::net::UnixListener), 10 | } 11 | 12 | pub(crate) enum SocketAddr { 13 | Tcp(net::SocketAddr), 14 | #[cfg(all(unix))] 15 | Uds(std::os::unix::net::SocketAddr), 16 | } 17 | 18 | impl fmt::Display for SocketAddr { 19 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 20 | match *self { 21 | SocketAddr::Tcp(ref addr) => write!(f, "{}", addr), 22 | #[cfg(all(unix))] 23 | SocketAddr::Uds(ref addr) => write!(f, "{:?}", addr), 24 | } 25 | } 26 | } 27 | 28 | impl fmt::Debug for SocketAddr { 29 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 30 | match *self { 31 | SocketAddr::Tcp(ref addr) => write!(f, "{:?}", addr), 32 | #[cfg(all(unix))] 33 | SocketAddr::Uds(ref addr) => write!(f, "{:?}", addr), 34 | } 35 | } 36 | } 37 | 38 | impl fmt::Display for StdListener { 39 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 40 | match *self { 41 | StdListener::Tcp(ref lst) => write!(f, "{}", lst.local_addr().ok().unwrap()), 42 | #[cfg(all(unix))] 43 | StdListener::Uds(ref lst) => write!(f, "{:?}", lst.local_addr().ok().unwrap()), 44 | } 45 | } 46 | } 47 | 48 | impl StdListener { 49 | pub(crate) fn local_addr(&self) -> SocketAddr { 50 | match self { 51 | StdListener::Tcp(lst) => SocketAddr::Tcp(lst.local_addr().unwrap()), 52 | #[cfg(all(unix))] 53 | StdListener::Uds(lst) => SocketAddr::Uds(lst.local_addr().unwrap()), 54 | } 55 | } 56 | 57 | pub(crate) fn into_listener(self) -> SocketListener { 58 | match self { 59 | StdListener::Tcp(lst) => SocketListener::Tcp( 60 | mio::net::TcpListener::from_std(lst) 61 | .expect("Can not create mio::net::TcpListener"), 62 | ), 63 | #[cfg(all(unix))] 64 | StdListener::Uds(lst) => SocketListener::Uds( 65 | mio_uds::UnixListener::from_listener(lst) 66 | .expect("Can not create mio_uds::UnixListener"), 67 | ), 68 | } 69 | } 70 | } 71 | 72 | #[derive(Debug)] 73 | pub enum StdStream { 74 | Tcp(std::net::TcpStream), 75 | #[cfg(all(unix))] 76 | Uds(std::os::unix::net::UnixStream), 77 | } 78 | 79 | pub(crate) enum SocketListener { 80 | Tcp(mio::net::TcpListener), 81 | #[cfg(all(unix))] 82 | Uds(mio_uds::UnixListener), 83 | } 84 | 85 | impl SocketListener { 86 | pub(crate) fn accept(&self) -> io::Result> { 87 | match *self { 88 | SocketListener::Tcp(ref lst) => lst 89 | .accept_std() 90 | .map(|(stream, addr)| Some((StdStream::Tcp(stream), SocketAddr::Tcp(addr)))), 91 | #[cfg(all(unix))] 92 | SocketListener::Uds(ref lst) => lst.accept_std().map(|res| { 93 | res.map(|(stream, addr)| (StdStream::Uds(stream), SocketAddr::Uds(addr))) 94 | }), 95 | } 96 | } 97 | } 98 | 99 | impl mio::Evented for SocketListener { 100 | fn register( 101 | &self, 102 | poll: &mio::Poll, 103 | token: mio::Token, 104 | interest: mio::Ready, 105 | opts: mio::PollOpt, 106 | ) -> io::Result<()> { 107 | match *self { 108 | SocketListener::Tcp(ref lst) => lst.register(poll, token, interest, opts), 109 | #[cfg(all(unix))] 110 | SocketListener::Uds(ref lst) => lst.register(poll, token, interest, opts), 111 | } 112 | } 113 | 114 | fn reregister( 115 | &self, 116 | poll: &mio::Poll, 117 | token: mio::Token, 118 | interest: mio::Ready, 119 | opts: mio::PollOpt, 120 | ) -> io::Result<()> { 121 | match *self { 122 | SocketListener::Tcp(ref lst) => lst.reregister(poll, token, interest, opts), 123 | #[cfg(all(unix))] 124 | SocketListener::Uds(ref lst) => lst.reregister(poll, token, interest, opts), 125 | } 126 | } 127 | fn deregister(&self, poll: &mio::Poll) -> io::Result<()> { 128 | match *self { 129 | SocketListener::Tcp(ref lst) => lst.deregister(poll), 130 | #[cfg(all(unix))] 131 | SocketListener::Uds(ref lst) => { 132 | let res = lst.deregister(poll); 133 | 134 | // cleanup file path 135 | if let Ok(addr) = lst.local_addr() { 136 | if let Some(path) = addr.as_pathname() { 137 | let _ = std::fs::remove_file(path); 138 | } 139 | } 140 | res 141 | } 142 | } 143 | } 144 | } 145 | 146 | pub trait FromStream: AsyncRead + AsyncWrite + Sized { 147 | fn from_stdstream(sock: StdStream) -> io::Result; 148 | } 149 | 150 | impl FromStream for TcpStream { 151 | fn from_stdstream(sock: StdStream) -> io::Result { 152 | match sock { 153 | StdStream::Tcp(stream) => TcpStream::from_std(stream), 154 | #[cfg(all(unix))] 155 | StdStream::Uds(_) => { 156 | panic!("Should not happen, bug in server impl"); 157 | } 158 | } 159 | } 160 | } 161 | 162 | #[cfg(all(unix))] 163 | impl FromStream for actix_rt::net::UnixStream { 164 | fn from_stdstream(sock: StdStream) -> io::Result { 165 | match sock { 166 | StdStream::Tcp(_) => panic!("Should not happen, bug in server impl"), 167 | StdStream::Uds(stream) => actix_rt::net::UnixStream::from_std(stream), 168 | } 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /actix-connect/src/resolve.rs: -------------------------------------------------------------------------------- 1 | use std::future::Future; 2 | use std::marker::PhantomData; 3 | use std::net::SocketAddr; 4 | use std::pin::Pin; 5 | use std::task::{Context, Poll}; 6 | 7 | use actix_service::{Service, ServiceFactory}; 8 | use futures::future::{ok, Either, Ready}; 9 | use trust_dns_resolver::lookup_ip::LookupIpFuture; 10 | use trust_dns_resolver::{AsyncResolver, Background}; 11 | 12 | use crate::connect::{Address, Connect}; 13 | use crate::error::ConnectError; 14 | use crate::get_default_resolver; 15 | 16 | /// DNS Resolver Service factory 17 | pub struct ResolverFactory { 18 | resolver: Option, 19 | _t: PhantomData, 20 | } 21 | 22 | impl ResolverFactory { 23 | /// Create new resolver instance with custom configuration and options. 24 | pub fn new(resolver: AsyncResolver) -> Self { 25 | ResolverFactory { 26 | resolver: Some(resolver), 27 | _t: PhantomData, 28 | } 29 | } 30 | 31 | pub fn service(&self) -> Resolver { 32 | Resolver { 33 | resolver: self.resolver.clone(), 34 | _t: PhantomData, 35 | } 36 | } 37 | } 38 | 39 | impl Default for ResolverFactory { 40 | fn default() -> Self { 41 | ResolverFactory { 42 | resolver: None, 43 | _t: PhantomData, 44 | } 45 | } 46 | } 47 | 48 | impl Clone for ResolverFactory { 49 | fn clone(&self) -> Self { 50 | ResolverFactory { 51 | resolver: self.resolver.clone(), 52 | _t: PhantomData, 53 | } 54 | } 55 | } 56 | 57 | impl ServiceFactory for ResolverFactory { 58 | type Request = Connect; 59 | type Response = Connect; 60 | type Error = ConnectError; 61 | type Config = (); 62 | type Service = Resolver; 63 | type InitError = (); 64 | type Future = Ready>; 65 | 66 | fn new_service(&self, _: ()) -> Self::Future { 67 | ok(self.service()) 68 | } 69 | } 70 | 71 | /// DNS Resolver Service 72 | pub struct Resolver { 73 | resolver: Option, 74 | _t: PhantomData, 75 | } 76 | 77 | impl Resolver { 78 | /// Create new resolver instance with custom configuration and options. 79 | pub fn new(resolver: AsyncResolver) -> Self { 80 | Resolver { 81 | resolver: Some(resolver), 82 | _t: PhantomData, 83 | } 84 | } 85 | } 86 | 87 | impl Default for Resolver { 88 | fn default() -> Self { 89 | Resolver { 90 | resolver: None, 91 | _t: PhantomData, 92 | } 93 | } 94 | } 95 | 96 | impl Clone for Resolver { 97 | fn clone(&self) -> Self { 98 | Resolver { 99 | resolver: self.resolver.clone(), 100 | _t: PhantomData, 101 | } 102 | } 103 | } 104 | 105 | impl Service for Resolver { 106 | type Request = Connect; 107 | type Response = Connect; 108 | type Error = ConnectError; 109 | type Future = Either, Ready, Self::Error>>>; 110 | 111 | fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { 112 | Poll::Ready(Ok(())) 113 | } 114 | 115 | fn call(&mut self, mut req: Connect) -> Self::Future { 116 | if req.addr.is_some() { 117 | Either::Right(ok(req)) 118 | } else if let Ok(ip) = req.host().parse() { 119 | req.addr = Some(either::Either::Left(SocketAddr::new(ip, req.port()))); 120 | Either::Right(ok(req)) 121 | } else { 122 | trace!("DNS resolver: resolving host {:?}", req.host()); 123 | if self.resolver.is_none() { 124 | self.resolver = Some(get_default_resolver()); 125 | } 126 | Either::Left(ResolverFuture::new(req, self.resolver.as_ref().unwrap())) 127 | } 128 | } 129 | } 130 | 131 | #[doc(hidden)] 132 | /// Resolver future 133 | pub struct ResolverFuture { 134 | req: Option>, 135 | lookup: Background, 136 | } 137 | 138 | impl ResolverFuture { 139 | pub fn new(req: Connect, resolver: &AsyncResolver) -> Self { 140 | let lookup = if let Some(host) = req.host().splitn(2, ':').next() { 141 | resolver.lookup_ip(host) 142 | } else { 143 | resolver.lookup_ip(req.host()) 144 | }; 145 | 146 | ResolverFuture { 147 | lookup, 148 | req: Some(req), 149 | } 150 | } 151 | } 152 | 153 | impl Future for ResolverFuture { 154 | type Output = Result, ConnectError>; 155 | 156 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 157 | let this = self.get_mut(); 158 | 159 | match Pin::new(&mut this.lookup).poll(cx) { 160 | Poll::Pending => Poll::Pending, 161 | Poll::Ready(Ok(ips)) => { 162 | let req = this.req.take().unwrap(); 163 | let port = req.port(); 164 | let req = req.set_addrs(ips.iter().map(|ip| SocketAddr::new(ip, port))); 165 | 166 | trace!( 167 | "DNS resolver: host {:?} resolved to {:?}", 168 | req.host(), 169 | req.addrs() 170 | ); 171 | 172 | if req.addr.is_none() { 173 | Poll::Ready(Err(ConnectError::NoRecords)) 174 | } else { 175 | Poll::Ready(Ok(req)) 176 | } 177 | } 178 | Poll::Ready(Err(e)) => { 179 | trace!( 180 | "DNS resolver: failed to resolve host {:?} err: {}", 181 | this.req.as_ref().unwrap().host(), 182 | e 183 | ); 184 | Poll::Ready(Err(e.into())) 185 | } 186 | } 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /string/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! A utl-8 encoded read-only string with Bytes as a storage. 2 | use std::convert::TryFrom; 3 | use std::{borrow, fmt, hash, ops, str}; 4 | 5 | use bytes::Bytes; 6 | 7 | /// A utf-8 encoded string with [`Bytes`] as a storage. 8 | /// 9 | /// [`Bytes`]: https://docs.rs/bytes/0.5.3/bytes/struct.Bytes.html 10 | #[derive(Clone, Eq, Ord, PartialOrd, Default)] 11 | pub struct ByteString(Bytes); 12 | 13 | impl ByteString { 14 | /// Creates a new `ByteString`. 15 | pub fn new() -> Self { 16 | ByteString(Bytes::new()) 17 | } 18 | 19 | /// Get a reference to the underlying bytes object. 20 | pub fn get_ref(&self) -> &Bytes { 21 | &self.0 22 | } 23 | 24 | /// Unwraps this `ByteString`, returning the underlying bytes object. 25 | pub fn into_inner(self) -> Bytes { 26 | self.0 27 | } 28 | 29 | /// Creates a new `ByteString` from a static str. 30 | pub const fn from_static(src: &'static str) -> ByteString { 31 | Self(Bytes::from_static(src.as_bytes())) 32 | } 33 | 34 | /// Creates a new `ByteString` from a Bytes. 35 | pub const unsafe fn from_bytes_unchecked(src: Bytes) -> ByteString { 36 | Self(src) 37 | } 38 | } 39 | 40 | impl PartialEq for ByteString { 41 | fn eq(&self, other: &str) -> bool { 42 | &self[..] == other 43 | } 44 | } 45 | 46 | impl> PartialEq for ByteString { 47 | fn eq(&self, other: &T) -> bool { 48 | &self[..] == other.as_ref() 49 | } 50 | } 51 | 52 | impl AsRef<[u8]> for ByteString { 53 | fn as_ref(&self) -> &[u8] { 54 | self.0.as_ref() 55 | } 56 | } 57 | 58 | impl AsRef for ByteString { 59 | fn as_ref(&self) -> &str { 60 | &*self 61 | } 62 | } 63 | 64 | impl hash::Hash for ByteString { 65 | fn hash(&self, state: &mut H) { 66 | (**self).hash(state); 67 | } 68 | } 69 | 70 | impl ops::Deref for ByteString { 71 | type Target = str; 72 | 73 | #[inline] 74 | fn deref(&self) -> &str { 75 | let b = self.0.as_ref(); 76 | unsafe { str::from_utf8_unchecked(b) } 77 | } 78 | } 79 | 80 | impl borrow::Borrow for ByteString { 81 | fn borrow(&self) -> &str { 82 | &*self 83 | } 84 | } 85 | 86 | impl From for ByteString { 87 | fn from(value: String) -> Self { 88 | Self(Bytes::from(value)) 89 | } 90 | } 91 | 92 | impl<'a> From<&'a str> for ByteString { 93 | fn from(value: &'a str) -> Self { 94 | Self(Bytes::copy_from_slice(value.as_ref())) 95 | } 96 | } 97 | 98 | impl<'a> TryFrom<&'a [u8]> for ByteString { 99 | type Error = str::Utf8Error; 100 | 101 | fn try_from(value: &'a [u8]) -> Result { 102 | let _ = str::from_utf8(value)?; 103 | Ok(ByteString(Bytes::copy_from_slice(value))) 104 | } 105 | } 106 | 107 | impl TryFrom> for ByteString { 108 | type Error = str::Utf8Error; 109 | 110 | fn try_from(value: Vec) -> Result { 111 | let _ = str::from_utf8(value.as_ref())?; 112 | Ok(ByteString(Bytes::from(value))) 113 | } 114 | } 115 | 116 | impl TryFrom for ByteString { 117 | type Error = str::Utf8Error; 118 | 119 | fn try_from(value: Bytes) -> Result { 120 | let _ = str::from_utf8(value.as_ref())?; 121 | Ok(ByteString(value)) 122 | } 123 | } 124 | 125 | impl TryFrom for ByteString { 126 | type Error = str::Utf8Error; 127 | 128 | fn try_from(value: bytes::BytesMut) -> Result { 129 | let _ = str::from_utf8(value.as_ref())?; 130 | Ok(ByteString(value.freeze())) 131 | } 132 | } 133 | 134 | macro_rules! array_impls { 135 | ($($len:expr)+) => { 136 | $( 137 | impl<'a> TryFrom<&'a [u8; $len]> for ByteString { 138 | type Error = str::Utf8Error; 139 | 140 | fn try_from(value: &'a [u8; $len]) -> Result { 141 | ByteString::try_from(&value[..]) 142 | } 143 | } 144 | )+ 145 | } 146 | } 147 | 148 | array_impls!(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16); 149 | 150 | impl fmt::Debug for ByteString { 151 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 152 | (**self).fmt(fmt) 153 | } 154 | } 155 | 156 | impl fmt::Display for ByteString { 157 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 158 | (**self).fmt(fmt) 159 | } 160 | } 161 | 162 | #[cfg(test)] 163 | mod test { 164 | use super::*; 165 | use std::collections::hash_map::DefaultHasher; 166 | use std::hash::{Hash, Hasher}; 167 | 168 | #[test] 169 | fn test_partial_eq() { 170 | let s: ByteString = ByteString::from_static("test"); 171 | assert_eq!(s, "test"); 172 | assert_eq!(s, *"test"); 173 | assert_eq!(s, "test".to_string()); 174 | } 175 | 176 | #[test] 177 | fn test_new() { 178 | let _: ByteString = ByteString::new(); 179 | } 180 | 181 | #[test] 182 | fn test_hash() { 183 | let mut hasher1 = DefaultHasher::default(); 184 | "str".hash(&mut hasher1); 185 | 186 | let mut hasher2 = DefaultHasher::default(); 187 | let s = ByteString::from_static("str"); 188 | s.hash(&mut hasher2); 189 | assert_eq!(hasher1.finish(), hasher2.finish()); 190 | } 191 | 192 | #[test] 193 | fn test_from_string() { 194 | let s: ByteString = "hello".to_string().into(); 195 | assert_eq!(&s, "hello"); 196 | let t: &str = s.as_ref(); 197 | assert_eq!(t, "hello"); 198 | } 199 | 200 | #[test] 201 | fn test_from_str() { 202 | let _: ByteString = "str".into(); 203 | } 204 | 205 | #[test] 206 | fn test_from_static_str() { 207 | static _S: ByteString = ByteString::from_static("hello"); 208 | let _ = ByteString::from_static("str"); 209 | } 210 | 211 | #[test] 212 | fn test_try_from_rbytes() { 213 | let _ = ByteString::try_from(b"nice bytes").unwrap(); 214 | } 215 | 216 | #[test] 217 | fn test_try_from_bytes() { 218 | let _ = ByteString::try_from(Bytes::from_static(b"nice bytes")).unwrap(); 219 | } 220 | 221 | #[test] 222 | fn test_try_from_bytesmut() { 223 | let _ = ByteString::try_from(bytes::BytesMut::from(&b"nice bytes"[..])).unwrap(); 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /actix-server/tests/test_server.rs: -------------------------------------------------------------------------------- 1 | use std::sync::atomic::{AtomicUsize, Ordering::Relaxed}; 2 | use std::sync::{mpsc, Arc}; 3 | use std::{net, thread, time}; 4 | 5 | use actix_server::Server; 6 | use actix_service::fn_service; 7 | use futures::future::{lazy, ok}; 8 | use net2::TcpBuilder; 9 | 10 | fn unused_addr() -> net::SocketAddr { 11 | let addr: net::SocketAddr = "127.0.0.1:0".parse().unwrap(); 12 | let socket = TcpBuilder::new_v4().unwrap(); 13 | socket.bind(&addr).unwrap(); 14 | socket.reuse_address(true).unwrap(); 15 | let tcp = socket.to_tcp_listener().unwrap(); 16 | tcp.local_addr().unwrap() 17 | } 18 | 19 | #[test] 20 | fn test_bind() { 21 | let addr = unused_addr(); 22 | let (tx, rx) = mpsc::channel(); 23 | 24 | let h = thread::spawn(move || { 25 | let sys = actix_rt::System::new("test"); 26 | let srv = Server::build() 27 | .workers(1) 28 | .disable_signals() 29 | .bind("test", addr, move || fn_service(|_| ok::<_, ()>(()))) 30 | .unwrap() 31 | .start(); 32 | let _ = tx.send((srv, actix_rt::System::current())); 33 | let _ = sys.run(); 34 | }); 35 | let (_, sys) = rx.recv().unwrap(); 36 | 37 | thread::sleep(time::Duration::from_millis(500)); 38 | assert!(net::TcpStream::connect(addr).is_ok()); 39 | sys.stop(); 40 | let _ = h.join(); 41 | } 42 | 43 | #[test] 44 | fn test_listen() { 45 | let addr = unused_addr(); 46 | let (tx, rx) = mpsc::channel(); 47 | 48 | let h = thread::spawn(move || { 49 | let sys = actix_rt::System::new("test"); 50 | let lst = net::TcpListener::bind(addr).unwrap(); 51 | Server::build() 52 | .disable_signals() 53 | .workers(1) 54 | .listen("test", lst, move || fn_service(|_| ok::<_, ()>(()))) 55 | .unwrap() 56 | .start(); 57 | let _ = tx.send(actix_rt::System::current()); 58 | let _ = sys.run(); 59 | }); 60 | let sys = rx.recv().unwrap(); 61 | 62 | thread::sleep(time::Duration::from_millis(500)); 63 | assert!(net::TcpStream::connect(addr).is_ok()); 64 | sys.stop(); 65 | let _ = h.join(); 66 | } 67 | 68 | #[test] 69 | #[cfg(unix)] 70 | fn test_start() { 71 | use actix_codec::{BytesCodec, Framed}; 72 | use actix_rt::net::TcpStream; 73 | use bytes::Bytes; 74 | use futures::SinkExt; 75 | use std::io::Read; 76 | 77 | let addr = unused_addr(); 78 | let (tx, rx) = mpsc::channel(); 79 | 80 | let h = thread::spawn(move || { 81 | let sys = actix_rt::System::new("test"); 82 | let srv: Server = Server::build() 83 | .backlog(100) 84 | .disable_signals() 85 | .bind("test", addr, move || { 86 | fn_service(|io: TcpStream| { 87 | async move { 88 | let mut f = Framed::new(io, BytesCodec); 89 | f.send(Bytes::from_static(b"test")).await.unwrap(); 90 | Ok::<_, ()>(()) 91 | } 92 | }) 93 | }) 94 | .unwrap() 95 | .start(); 96 | 97 | let _ = tx.send((srv, actix_rt::System::current())); 98 | let _ = sys.run(); 99 | }); 100 | let (srv, sys) = rx.recv().unwrap(); 101 | 102 | let mut buf = [1u8; 4]; 103 | let mut conn = net::TcpStream::connect(addr).unwrap(); 104 | let _ = conn.read_exact(&mut buf); 105 | assert_eq!(buf, b"test"[..]); 106 | 107 | // pause 108 | let _ = srv.pause(); 109 | thread::sleep(time::Duration::from_millis(200)); 110 | let mut conn = net::TcpStream::connect(addr).unwrap(); 111 | conn.set_read_timeout(Some(time::Duration::from_millis(100))) 112 | .unwrap(); 113 | let res = conn.read_exact(&mut buf); 114 | assert!(res.is_err()); 115 | 116 | // resume 117 | let _ = srv.resume(); 118 | thread::sleep(time::Duration::from_millis(100)); 119 | assert!(net::TcpStream::connect(addr).is_ok()); 120 | assert!(net::TcpStream::connect(addr).is_ok()); 121 | assert!(net::TcpStream::connect(addr).is_ok()); 122 | 123 | let mut buf = [0u8; 4]; 124 | let mut conn = net::TcpStream::connect(addr).unwrap(); 125 | let _ = conn.read_exact(&mut buf); 126 | assert_eq!(buf, b"test"[..]); 127 | 128 | // stop 129 | let _ = srv.stop(false); 130 | thread::sleep(time::Duration::from_millis(100)); 131 | assert!(net::TcpStream::connect(addr).is_err()); 132 | 133 | thread::sleep(time::Duration::from_millis(100)); 134 | sys.stop(); 135 | let _ = h.join(); 136 | } 137 | 138 | #[test] 139 | fn test_configure() { 140 | let addr1 = unused_addr(); 141 | let addr2 = unused_addr(); 142 | let addr3 = unused_addr(); 143 | let (tx, rx) = mpsc::channel(); 144 | let num = Arc::new(AtomicUsize::new(0)); 145 | let num2 = num.clone(); 146 | 147 | let h = thread::spawn(move || { 148 | let num = num2.clone(); 149 | let sys = actix_rt::System::new("test"); 150 | let srv = Server::build() 151 | .disable_signals() 152 | .configure(move |cfg| { 153 | let num = num.clone(); 154 | let lst = net::TcpListener::bind(addr3).unwrap(); 155 | cfg.bind("addr1", addr1) 156 | .unwrap() 157 | .bind("addr2", addr2) 158 | .unwrap() 159 | .listen("addr3", lst) 160 | .apply(move |rt| { 161 | let num = num.clone(); 162 | rt.service("addr1", fn_service(|_| ok::<_, ()>(()))); 163 | rt.service("addr3", fn_service(|_| ok::<_, ()>(()))); 164 | rt.on_start(lazy(move |_| { 165 | let _ = num.fetch_add(1, Relaxed); 166 | })) 167 | }) 168 | }) 169 | .unwrap() 170 | .workers(1) 171 | .start(); 172 | let _ = tx.send((srv, actix_rt::System::current())); 173 | let _ = sys.run(); 174 | }); 175 | let (_, sys) = rx.recv().unwrap(); 176 | thread::sleep(time::Duration::from_millis(500)); 177 | 178 | assert!(net::TcpStream::connect(addr1).is_ok()); 179 | assert!(net::TcpStream::connect(addr2).is_ok()); 180 | assert!(net::TcpStream::connect(addr3).is_ok()); 181 | assert_eq!(num.load(Relaxed), 1); 182 | sys.stop(); 183 | let _ = h.join(); 184 | } 185 | -------------------------------------------------------------------------------- /router/src/path.rs: -------------------------------------------------------------------------------- 1 | use std::ops::Index; 2 | 3 | use serde::de; 4 | 5 | use crate::de::PathDeserializer; 6 | use crate::{Resource, ResourcePath}; 7 | 8 | #[derive(Debug, Clone, Copy)] 9 | pub(crate) enum PathItem { 10 | Static(&'static str), 11 | Segment(u16, u16), 12 | } 13 | 14 | /// Resource path match information 15 | /// 16 | /// If resource path contains variable patterns, `Path` stores them. 17 | #[derive(Debug)] 18 | pub struct Path { 19 | path: T, 20 | pub(crate) skip: u16, 21 | pub(crate) segments: Vec<(&'static str, PathItem)>, 22 | } 23 | 24 | impl Default for Path { 25 | fn default() -> Self { 26 | Path { 27 | path: T::default(), 28 | skip: 0, 29 | segments: Vec::new(), 30 | } 31 | } 32 | } 33 | 34 | impl Clone for Path { 35 | fn clone(&self) -> Self { 36 | Path { 37 | path: self.path.clone(), 38 | skip: self.skip, 39 | segments: self.segments.clone(), 40 | } 41 | } 42 | } 43 | 44 | impl Path { 45 | pub fn new(path: T) -> Path { 46 | Path { 47 | path, 48 | skip: 0, 49 | segments: Vec::new(), 50 | } 51 | } 52 | 53 | #[inline] 54 | /// Get reference to inner path instance 55 | pub fn get_ref(&self) -> &T { 56 | &self.path 57 | } 58 | 59 | #[inline] 60 | /// Get mutable reference to inner path instance 61 | pub fn get_mut(&mut self) -> &mut T { 62 | &mut self.path 63 | } 64 | 65 | #[inline] 66 | /// Path 67 | pub fn path(&self) -> &str { 68 | let skip = self.skip as usize; 69 | let path = self.path.path(); 70 | if skip <= path.len() { 71 | &path[skip..] 72 | } else { 73 | "" 74 | } 75 | } 76 | 77 | #[inline] 78 | /// Set new path 79 | pub fn set(&mut self, path: T) { 80 | self.skip = 0; 81 | self.path = path; 82 | self.segments.clear(); 83 | } 84 | 85 | #[inline] 86 | /// Reset state 87 | pub fn reset(&mut self) { 88 | self.skip = 0; 89 | self.segments.clear(); 90 | } 91 | 92 | #[inline] 93 | /// Skip first `n` chars in path 94 | pub fn skip(&mut self, n: u16) { 95 | self.skip += n; 96 | } 97 | 98 | pub(crate) fn add(&mut self, name: &'static str, value: PathItem) { 99 | match value { 100 | PathItem::Static(s) => self.segments.push((name, PathItem::Static(s))), 101 | PathItem::Segment(begin, end) => self 102 | .segments 103 | .push((name, PathItem::Segment(self.skip + begin, self.skip + end))), 104 | } 105 | } 106 | 107 | #[doc(hidden)] 108 | pub fn add_static(&mut self, name: &'static str, value: &'static str) { 109 | self.segments.push((name, PathItem::Static(value))); 110 | } 111 | 112 | #[inline] 113 | /// Check if there are any matched patterns 114 | pub fn is_empty(&self) -> bool { 115 | self.segments.is_empty() 116 | } 117 | 118 | #[inline] 119 | /// Check number of extracted parameters 120 | pub fn len(&self) -> usize { 121 | self.segments.len() 122 | } 123 | 124 | /// Get matched parameter by name without type conversion 125 | pub fn get(&self, key: &str) -> Option<&str> { 126 | for item in self.segments.iter() { 127 | if key == item.0 { 128 | return match item.1 { 129 | PathItem::Static(ref s) => Some(&s), 130 | PathItem::Segment(s, e) => { 131 | Some(&self.path.path()[(s as usize)..(e as usize)]) 132 | } 133 | }; 134 | } 135 | } 136 | if key == "tail" { 137 | Some(&self.path.path()[(self.skip as usize)..]) 138 | } else { 139 | None 140 | } 141 | } 142 | 143 | /// Get unprocessed part of the path 144 | pub fn unprocessed(&self) -> &str { 145 | &self.path.path()[(self.skip as usize)..] 146 | } 147 | 148 | /// Get matched parameter by name. 149 | /// 150 | /// If keyed parameter is not available empty string is used as default 151 | /// value. 152 | pub fn query(&self, key: &str) -> &str { 153 | if let Some(s) = self.get(key) { 154 | s 155 | } else { 156 | "" 157 | } 158 | } 159 | 160 | /// Return iterator to items in parameter container 161 | pub fn iter(&self) -> PathIter { 162 | PathIter { 163 | idx: 0, 164 | params: self, 165 | } 166 | } 167 | 168 | /// Try to deserialize matching parameters to a specified type `U` 169 | pub fn load<'de, U: serde::Deserialize<'de>>(&'de self) -> Result { 170 | de::Deserialize::deserialize(PathDeserializer::new(self)) 171 | } 172 | } 173 | 174 | #[derive(Debug)] 175 | pub struct PathIter<'a, T> { 176 | idx: usize, 177 | params: &'a Path, 178 | } 179 | 180 | impl<'a, T: ResourcePath> Iterator for PathIter<'a, T> { 181 | type Item = (&'a str, &'a str); 182 | 183 | #[inline] 184 | fn next(&mut self) -> Option<(&'a str, &'a str)> { 185 | if self.idx < self.params.len() { 186 | let idx = self.idx; 187 | let res = match self.params.segments[idx].1 { 188 | PathItem::Static(ref s) => &s, 189 | PathItem::Segment(s, e) => &self.params.path.path()[(s as usize)..(e as usize)], 190 | }; 191 | self.idx += 1; 192 | return Some((&self.params.segments[idx].0, res)); 193 | } 194 | None 195 | } 196 | } 197 | 198 | impl<'a, T: ResourcePath> Index<&'a str> for Path { 199 | type Output = str; 200 | 201 | fn index(&self, name: &'a str) -> &str { 202 | self.get(name) 203 | .expect("Value for parameter is not available") 204 | } 205 | } 206 | 207 | impl Index for Path { 208 | type Output = str; 209 | 210 | fn index(&self, idx: usize) -> &str { 211 | match self.segments[idx].1 { 212 | PathItem::Static(ref s) => &s, 213 | PathItem::Segment(s, e) => &self.path.path()[(s as usize)..(e as usize)], 214 | } 215 | } 216 | } 217 | 218 | impl Resource for Path { 219 | fn resource_path(&mut self) -> &mut Self { 220 | self 221 | } 222 | } 223 | --------------------------------------------------------------------------------