mirror of https://github.com/fafhrd91/actix-web
Merge branch 'master' into files/fix/percent-encoding
This commit is contained in:
commit
89b4e33547
|
@ -7,3 +7,8 @@ ci-default = "check --workspace --bins --tests --examples"
|
||||||
ci-full = "check --workspace --all-features --bins --tests --examples"
|
ci-full = "check --workspace --all-features --bins --tests --examples"
|
||||||
ci-test = "test --workspace --all-features --lib --tests --no-fail-fast -- --nocapture"
|
ci-test = "test --workspace --all-features --lib --tests --no-fail-fast -- --nocapture"
|
||||||
ci-doctest = "test --workspace --all-features --doc --no-fail-fast -- --nocapture"
|
ci-doctest = "test --workspace --all-features --doc --no-fail-fast -- --nocapture"
|
||||||
|
|
||||||
|
ci-feature-powerset-check-no-tls="hack --workspace --feature-powerset --skip=__compress,rustls,openssl check"
|
||||||
|
ci-feature-powerset-check-rustls="hack --workspace --feature-powerset --features=rustls --skip=__compress,openssl check"
|
||||||
|
ci-feature-powerset-check-openssl="hack --workspace --feature-powerset --features=openssl --skip=__compress,rustls check"
|
||||||
|
ci-feature-powerset-check-all="hack --workspace --feature-powerset --skip=__compress check"
|
||||||
|
|
|
@ -14,9 +14,9 @@ jobs:
|
||||||
target:
|
target:
|
||||||
- { name: Linux, os: ubuntu-latest, triple: x86_64-unknown-linux-gnu }
|
- { name: Linux, os: ubuntu-latest, triple: x86_64-unknown-linux-gnu }
|
||||||
- { name: macOS, os: macos-latest, triple: x86_64-apple-darwin }
|
- { name: macOS, os: macos-latest, triple: x86_64-apple-darwin }
|
||||||
- { name: Windows, os: windows-latest, triple: x86_64-pc-windows-msvc }
|
- { name: Windows, os: windows-2022, triple: x86_64-pc-windows-msvc }
|
||||||
version:
|
version:
|
||||||
- 1.51.0 # MSRV
|
- 1.52.0 # MSRV
|
||||||
- stable
|
- stable
|
||||||
- nightly
|
- nightly
|
||||||
|
|
||||||
|
@ -32,6 +32,8 @@ jobs:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
# install OpenSSL on Windows
|
# install OpenSSL on Windows
|
||||||
|
# TODO: GitHub actions docs state that OpenSSL is
|
||||||
|
# already installed on these Windows machines somewhere
|
||||||
- name: Set vcpkg root
|
- name: Set vcpkg root
|
||||||
if: matrix.target.triple == 'x86_64-pc-windows-msvc'
|
if: matrix.target.triple == 'x86_64-pc-windows-msvc'
|
||||||
run: echo "VCPKG_ROOT=$env:VCPKG_INSTALLATION_ROOT" | Out-File -FilePath $env:GITHUB_ENV -Append
|
run: echo "VCPKG_ROOT=$env:VCPKG_INSTALLATION_ROOT" | Out-File -FilePath $env:GITHUB_ENV -Append
|
||||||
|
@ -48,8 +50,7 @@ jobs:
|
||||||
|
|
||||||
- name: Generate Cargo.lock
|
- name: Generate Cargo.lock
|
||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1
|
||||||
with:
|
with: { command: generate-lockfile }
|
||||||
command: generate-lockfile
|
|
||||||
- name: Cache Dependencies
|
- name: Cache Dependencies
|
||||||
uses: Swatinem/rust-cache@v1.2.0
|
uses: Swatinem/rust-cache@v1.2.0
|
||||||
|
|
||||||
|
@ -82,32 +83,73 @@ jobs:
|
||||||
command: ci-test
|
command: ci-test
|
||||||
args: --skip=test_reading_deflate_encoding_large_random_rustls
|
args: --skip=test_reading_deflate_encoding_large_random_rustls
|
||||||
|
|
||||||
- name: Generate coverage file
|
|
||||||
if: >
|
|
||||||
matrix.target.os == 'ubuntu-latest'
|
|
||||||
&& matrix.version == 'stable'
|
|
||||||
&& github.ref == 'refs/heads/master'
|
|
||||||
run: |
|
|
||||||
cargo install cargo-tarpaulin --vers "^0.13"
|
|
||||||
cargo tarpaulin --out Xml --verbose
|
|
||||||
- name: Upload to Codecov
|
|
||||||
if: >
|
|
||||||
matrix.target.os == 'ubuntu-latest'
|
|
||||||
&& matrix.version == 'stable'
|
|
||||||
&& github.ref == 'refs/heads/master'
|
|
||||||
uses: codecov/codecov-action@v1
|
|
||||||
with:
|
|
||||||
file: cobertura.xml
|
|
||||||
|
|
||||||
- name: Clear the cargo caches
|
- name: Clear the cargo caches
|
||||||
run: |
|
run: |
|
||||||
cargo install cargo-cache --version 0.6.3 --no-default-features --features ci-autoclean
|
cargo install cargo-cache --version 0.6.3 --no-default-features --features ci-autoclean
|
||||||
cargo-cache
|
cargo-cache
|
||||||
|
|
||||||
|
ci_feature_powerset_check:
|
||||||
|
name: Verify Feature Combinations
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Install stable
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: stable-x86_64-unknown-linux-gnu
|
||||||
|
profile: minimal
|
||||||
|
override: true
|
||||||
|
|
||||||
|
- name: Generate Cargo.lock
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with: { command: generate-lockfile }
|
||||||
|
- name: Cache Dependencies
|
||||||
|
uses: Swatinem/rust-cache@v1.2.0
|
||||||
|
|
||||||
|
- name: Install cargo-hack
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: install
|
||||||
|
args: cargo-hack
|
||||||
|
|
||||||
|
- name: check feature combinations
|
||||||
|
# if: github.ref == 'refs/heads/master'
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with: { command: ci-feature-powerset-check-all }
|
||||||
|
|
||||||
|
coverage:
|
||||||
|
name: coverage
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Install stable
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: stable-x86_64-unknown-linux-gnu
|
||||||
|
profile: minimal
|
||||||
|
override: true
|
||||||
|
|
||||||
|
- name: Generate Cargo.lock
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with: { command: generate-lockfile }
|
||||||
|
- name: Cache Dependencies
|
||||||
|
uses: Swatinem/rust-cache@v1.2.0
|
||||||
|
|
||||||
|
- name: Generate coverage file
|
||||||
|
if: github.ref == 'refs/heads/master'
|
||||||
|
run: |
|
||||||
|
cargo install cargo-tarpaulin --vers "^0.13"
|
||||||
|
cargo tarpaulin --out Xml --verbose
|
||||||
|
- name: Upload to Codecov
|
||||||
|
if: github.ref == 'refs/heads/master'
|
||||||
|
uses: codecov/codecov-action@v1
|
||||||
|
with: { file: cobertura.xml }
|
||||||
|
|
||||||
rustdoc:
|
rustdoc:
|
||||||
name: rustdoc
|
name: rustdoc
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
|
16
CHANGES.md
16
CHANGES.md
|
@ -1,16 +1,32 @@
|
||||||
# Changes
|
# Changes
|
||||||
|
|
||||||
## Unreleased - 2021-xx-xx
|
## Unreleased - 2021-xx-xx
|
||||||
|
### Changed
|
||||||
|
* `ContentType::html` now returns `Content-Type: text/html; charset=utf-8` instead of `Content-Type: text/html`.
|
||||||
|
|
||||||
|
|
||||||
|
## 4.0.0-beta.10 - 2021-10-20
|
||||||
### Added
|
### Added
|
||||||
* Option to allow `Json` extractor to work without a `Content-Type` header present. [#2362]
|
* Option to allow `Json` extractor to work without a `Content-Type` header present. [#2362]
|
||||||
|
* `#[actix_web::test]` macro for setting up tests with a runtime. [#2409]
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
* Associated type `FromRequest::Config` was removed. [#2233]
|
* Associated type `FromRequest::Config` was removed. [#2233]
|
||||||
* Inner field made private on `web::Payload`. [#2384]
|
* Inner field made private on `web::Payload`. [#2384]
|
||||||
|
* `Data::into_inner` and `Data::get_ref` no longer require T: Sized. [#2403]
|
||||||
|
* Updated rustls to v0.20. [#2414]
|
||||||
|
* Minimum supported Rust version (MSRV) is now 1.52.
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
* Useless `ServiceResponse::checked_expr` method. [#2401]
|
||||||
|
|
||||||
[#2233]: https://github.com/actix/actix-web/pull/2233
|
[#2233]: https://github.com/actix/actix-web/pull/2233
|
||||||
[#2362]: https://github.com/actix/actix-web/pull/2362
|
[#2362]: https://github.com/actix/actix-web/pull/2362
|
||||||
[#2384]: https://github.com/actix/actix-web/pull/2384
|
[#2384]: https://github.com/actix/actix-web/pull/2384
|
||||||
|
[#2401]: https://github.com/actix/actix-web/pull/2401
|
||||||
|
[#2403]: https://github.com/actix/actix-web/pull/2403
|
||||||
|
[#2409]: https://github.com/actix/actix-web/pull/2409
|
||||||
|
[#2414]: https://github.com/actix/actix-web/pull/2414
|
||||||
|
|
||||||
|
|
||||||
## 4.0.0-beta.9 - 2021-09-09
|
## 4.0.0-beta.9 - 2021-09-09
|
||||||
|
|
26
Cargo.toml
26
Cargo.toml
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "actix-web"
|
name = "actix-web"
|
||||||
version = "4.0.0-beta.9"
|
version = "4.0.0-beta.10"
|
||||||
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
||||||
description = "Actix Web is a powerful, pragmatic, and extremely fast web framework for Rust"
|
description = "Actix Web is a powerful, pragmatic, and extremely fast web framework for Rust"
|
||||||
keywords = ["actix", "http", "web", "framework", "async"]
|
keywords = ["actix", "http", "web", "framework", "async"]
|
||||||
|
@ -11,7 +11,7 @@ categories = [
|
||||||
"web-programming::websocket"
|
"web-programming::websocket"
|
||||||
]
|
]
|
||||||
homepage = "https://actix.rs"
|
homepage = "https://actix.rs"
|
||||||
repository = "https://github.com/actix/actix-web"
|
repository = "https://github.com/actix/actix-web.git"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
|
@ -38,8 +38,6 @@ members = [
|
||||||
"actix-test",
|
"actix-test",
|
||||||
"actix-router",
|
"actix-router",
|
||||||
]
|
]
|
||||||
# enable when MSRV is 1.51+
|
|
||||||
# resolver = "2"
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["compress-brotli", "compress-gzip", "compress-zstd", "cookies"]
|
default = ["compress-brotli", "compress-gzip", "compress-zstd", "cookies"]
|
||||||
|
@ -63,22 +61,22 @@ openssl = ["actix-http/openssl", "actix-tls/accept", "actix-tls/openssl"]
|
||||||
# rustls
|
# rustls
|
||||||
rustls = ["actix-http/rustls", "actix-tls/accept", "actix-tls/rustls"]
|
rustls = ["actix-http/rustls", "actix-tls/accept", "actix-tls/rustls"]
|
||||||
|
|
||||||
# Internal (PRIVATE!) features used to aid testing and cheking feature status.
|
# Internal (PRIVATE!) features used to aid testing and checking feature status.
|
||||||
# Don't rely on these whatsoever. They may disappear at anytime.
|
# Don't rely on these whatsoever. They may disappear at anytime.
|
||||||
__compress = []
|
__compress = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix-codec = "0.4.0"
|
actix-codec = "0.4.0"
|
||||||
actix-macros = "0.2.1"
|
actix-macros = "0.2.3"
|
||||||
actix-router = "0.5.0-beta.2"
|
|
||||||
actix-rt = "2.2"
|
actix-rt = "2.2"
|
||||||
actix-server = "2.0.0-beta.3"
|
actix-server = "2.0.0-beta.3"
|
||||||
actix-service = "2.0.0"
|
actix-service = "2.0.0"
|
||||||
actix-utils = "3.0.0"
|
actix-utils = "3.0.0"
|
||||||
actix-tls = { version = "3.0.0-beta.5", default-features = false, optional = true }
|
actix-tls = { version = "3.0.0-beta.7", default-features = false, optional = true }
|
||||||
|
|
||||||
actix-web-codegen = "0.5.0-beta.4"
|
actix-http = "3.0.0-beta.11"
|
||||||
actix-http = "3.0.0-beta.10"
|
actix-router = "0.5.0-beta.2"
|
||||||
|
actix-web-codegen = "0.5.0-beta.5"
|
||||||
|
|
||||||
ahash = "0.7"
|
ahash = "0.7"
|
||||||
bytes = "1"
|
bytes = "1"
|
||||||
|
@ -107,17 +105,19 @@ url = "2.1"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
actix-test = { version = "0.1.0-beta.3", features = ["openssl", "rustls"] }
|
actix-test = { version = "0.1.0-beta.3", features = ["openssl", "rustls"] }
|
||||||
awc = { version = "3.0.0-beta.8", features = ["openssl"] }
|
awc = { version = "3.0.0-beta.9", features = ["openssl"] }
|
||||||
|
|
||||||
brotli2 = "0.3.2"
|
brotli2 = "0.3.2"
|
||||||
criterion = { version = "0.3", features = ["html_reports"] }
|
criterion = { version = "0.3", features = ["html_reports"] }
|
||||||
env_logger = "0.8"
|
env_logger = "0.8"
|
||||||
flate2 = "1.0.13"
|
flate2 = "1.0.13"
|
||||||
zstd = "0.7"
|
futures-util = { version = "0.3.7", default-features = false, features = ["std"] }
|
||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
rcgen = "0.8"
|
rcgen = "0.8"
|
||||||
|
rustls-pemfile = "0.2"
|
||||||
tls-openssl = { package = "openssl", version = "0.10.9" }
|
tls-openssl = { package = "openssl", version = "0.10.9" }
|
||||||
tls-rustls = { package = "rustls", version = "0.19.0" }
|
tls-rustls = { package = "rustls", version = "0.20.0" }
|
||||||
|
zstd = "0.7"
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
# Disabling debug info speeds up builds a bunch and we don't rely on it for debugging that much.
|
# Disabling debug info speeds up builds a bunch and we don't rely on it for debugging that much.
|
||||||
|
|
|
@ -6,10 +6,10 @@
|
||||||
<p>
|
<p>
|
||||||
|
|
||||||
[](https://crates.io/crates/actix-web)
|
[](https://crates.io/crates/actix-web)
|
||||||
[](https://docs.rs/actix-web/4.0.0-beta.9)
|
[](https://docs.rs/actix-web/4.0.0-beta.10)
|
||||||
[](https://blog.rust-lang.org/2020/03/12/Rust-1.51.html)
|
[](https://blog.rust-lang.org/2021/05/06/Rust-1.52.0.html)
|
||||||

|

|
||||||
[](https://deps.rs/crate/actix-web/4.0.0-beta.9)
|
[](https://deps.rs/crate/actix-web/4.0.0-beta.10)
|
||||||
<br />
|
<br />
|
||||||
[](https://github.com/actix/actix-web/actions)
|
[](https://github.com/actix/actix-web/actions)
|
||||||
[](https://codecov.io/gh/actix/actix-web)
|
[](https://codecov.io/gh/actix/actix-web)
|
||||||
|
@ -32,7 +32,7 @@
|
||||||
* SSL support using OpenSSL or Rustls
|
* SSL support using OpenSSL or Rustls
|
||||||
* Middlewares ([Logger, Session, CORS, etc](https://actix.rs/docs/middleware/))
|
* Middlewares ([Logger, Session, CORS, etc](https://actix.rs/docs/middleware/))
|
||||||
* Includes an async [HTTP client](https://docs.rs/awc/)
|
* Includes an async [HTTP client](https://docs.rs/awc/)
|
||||||
* Runs on stable Rust 1.51+
|
* Runs on stable Rust 1.52+
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,10 @@
|
||||||
## Unreleased - 2021-xx-xx
|
## Unreleased - 2021-xx-xx
|
||||||
|
|
||||||
|
|
||||||
|
## 0.6.0-beta.8 - 2021-10-20
|
||||||
|
* Minimum supported Rust version (MSRV) is now 1.52.
|
||||||
|
|
||||||
|
|
||||||
## 0.6.0-beta.7 - 2021-09-09
|
## 0.6.0-beta.7 - 2021-09-09
|
||||||
* Minimum supported Rust version (MSRV) is now 1.51.
|
* Minimum supported Rust version (MSRV) is now 1.51.
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "actix-files"
|
name = "actix-files"
|
||||||
version = "0.6.0-beta.7"
|
version = "0.6.0-beta.8"
|
||||||
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
||||||
description = "Static file serving for Actix Web"
|
description = "Static file serving for Actix Web"
|
||||||
keywords = ["actix", "http", "async", "futures"]
|
keywords = ["actix", "http", "async", "futures"]
|
||||||
|
@ -15,8 +15,8 @@ name = "actix_files"
|
||||||
path = "src/lib.rs"
|
path = "src/lib.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix-web = { version = "4.0.0-beta.9", default-features = false }
|
actix-web = { version = "4.0.0-beta.10", default-features = false }
|
||||||
actix-http = "3.0.0-beta.10"
|
actix-http = "3.0.0-beta.11"
|
||||||
actix-service = "2.0.0"
|
actix-service = "2.0.0"
|
||||||
actix-utils = "3.0.0"
|
actix-utils = "3.0.0"
|
||||||
|
|
||||||
|
@ -33,6 +33,6 @@ percent-encoding = "2.1"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
actix-rt = "2.2"
|
actix-rt = "2.2"
|
||||||
actix-web = "4.0.0-beta.9"
|
actix-web = "4.0.0-beta.10"
|
||||||
actix-test = "0.1.0-beta.3"
|
actix-test = "0.1.0-beta.5"
|
||||||
tempfile = "3.2"
|
tempfile = "3.2"
|
||||||
|
|
|
@ -3,11 +3,11 @@
|
||||||
> Static file serving for Actix Web
|
> Static file serving for Actix Web
|
||||||
|
|
||||||
[](https://crates.io/crates/actix-files)
|
[](https://crates.io/crates/actix-files)
|
||||||
[](https://docs.rs/actix-files/0.6.0-beta.7)
|
[](https://docs.rs/actix-files/0.6.0-beta.8)
|
||||||
[](https://blog.rust-lang.org/2020/03/12/Rust-1.51.html)
|
[](https://blog.rust-lang.org/2021/05/06/Rust-1.52.0.html)
|
||||||

|

|
||||||
<br />
|
<br />
|
||||||
[](https://deps.rs/crate/actix-files/0.6.0-beta.7)
|
[](https://deps.rs/crate/actix-files/0.6.0-beta.8)
|
||||||
[](https://crates.io/crates/actix-files)
|
[](https://crates.io/crates/actix-files)
|
||||||
[](https://discord.gg/NWpN5mmg3x)
|
[](https://discord.gg/NWpN5mmg3x)
|
||||||
|
|
||||||
|
@ -15,4 +15,4 @@
|
||||||
|
|
||||||
- [API Documentation](https://docs.rs/actix-files/)
|
- [API Documentation](https://docs.rs/actix-files/)
|
||||||
- [Example Project](https://github.com/actix/examples/tree/master/basics/static_index)
|
- [Example Project](https://github.com/actix/examples/tree/master/basics/static_index)
|
||||||
- Minimum supported Rust version: 1.51 or later
|
- Minimum Supported Rust Version (MSRV): 1.52
|
||||||
|
|
|
@ -83,7 +83,7 @@ mod tests {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_web::test]
|
||||||
async fn test_file_extension_to_mime() {
|
async fn test_file_extension_to_mime() {
|
||||||
let m = file_extension_to_mime("");
|
let m = file_extension_to_mime("");
|
||||||
assert_eq!(m, mime::APPLICATION_OCTET_STREAM);
|
assert_eq!(m, mime::APPLICATION_OCTET_STREAM);
|
||||||
|
|
|
@ -8,7 +8,7 @@ use actix_web::{
|
||||||
App,
|
App,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_web::test]
|
||||||
async fn test_utf8_file_contents() {
|
async fn test_utf8_file_contents() {
|
||||||
// use default ISO-8859-1 encoding
|
// use default ISO-8859-1 encoding
|
||||||
let srv = test::init_service(App::new().service(Files::new("/", "./tests"))).await;
|
let srv = test::init_service(App::new().service(Files::new("/", "./tests"))).await;
|
||||||
|
|
|
@ -7,7 +7,7 @@ use actix_web::{
|
||||||
};
|
};
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_web::test]
|
||||||
async fn test_guard_filter() {
|
async fn test_guard_filter() {
|
||||||
let srv = test::init_service(
|
let srv = test::init_service(
|
||||||
App::new()
|
App::new()
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
# Changes
|
# Changes
|
||||||
|
|
||||||
## Unreleased - 2021-xx-xx
|
## Unreleased - 2021-xx-xx
|
||||||
|
* Minimum supported Rust version (MSRV) is now 1.52.
|
||||||
|
|
||||||
|
|
||||||
## 3.0.0-beta.5 - 2021-09-09
|
## 3.0.0-beta.5 - 2021-09-09
|
||||||
|
|
|
@ -31,11 +31,11 @@ openssl = ["tls-openssl", "awc/openssl"]
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix-service = "2.0.0"
|
actix-service = "2.0.0"
|
||||||
actix-codec = "0.4.0"
|
actix-codec = "0.4.0"
|
||||||
actix-tls = "3.0.0-beta.5"
|
actix-tls = "3.0.0-beta.7"
|
||||||
actix-utils = "3.0.0"
|
actix-utils = "3.0.0"
|
||||||
actix-rt = "2.2"
|
actix-rt = "2.2"
|
||||||
actix-server = "2.0.0-beta.3"
|
actix-server = "2.0.0-beta.3"
|
||||||
awc = { version = "3.0.0-beta.8", default-features = false }
|
awc = { version = "3.0.0-beta.9", default-features = false }
|
||||||
|
|
||||||
base64 = "0.13"
|
base64 = "0.13"
|
||||||
bytes = "1"
|
bytes = "1"
|
||||||
|
@ -50,5 +50,5 @@ serde_urlencoded = "0.7"
|
||||||
tls-openssl = { version = "0.10.9", package = "openssl", optional = true }
|
tls-openssl = { version = "0.10.9", package = "openssl", optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
actix-web = { version = "4.0.0-beta.9", default-features = false, features = ["cookies"] }
|
actix-web = { version = "4.0.0-beta.10", default-features = false, features = ["cookies"] }
|
||||||
actix-http = "3.0.0-beta.10"
|
actix-http = "3.0.0-beta.11"
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
[](https://crates.io/crates/actix-http-test)
|
[](https://crates.io/crates/actix-http-test)
|
||||||
[](https://docs.rs/actix-http-test/3.0.0-beta.5)
|
[](https://docs.rs/actix-http-test/3.0.0-beta.5)
|
||||||
[](https://blog.rust-lang.org/2020/03/12/Rust-1.51.html)
|
[](https://blog.rust-lang.org/2021/05/06/Rust-1.52.0.html)
|
||||||

|

|
||||||
<br>
|
<br>
|
||||||
[](https://deps.rs/crate/actix-http-test/3.0.0-beta.5)
|
[](https://deps.rs/crate/actix-http-test/3.0.0-beta.5)
|
||||||
|
@ -14,4 +14,4 @@
|
||||||
## Documentation & Resources
|
## Documentation & Resources
|
||||||
|
|
||||||
- [API Documentation](https://docs.rs/actix-http-test)
|
- [API Documentation](https://docs.rs/actix-http-test)
|
||||||
- Minimum Supported Rust Version (MSRV): 1.51.0
|
- Minimum Supported Rust Version (MSRV): 1.52
|
||||||
|
|
|
@ -36,7 +36,7 @@ use socket2::{Domain, Protocol, Socket, Type};
|
||||||
/// Ok(HttpResponse::Ok().into())
|
/// Ok(HttpResponse::Ok().into())
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// #[actix_rt::test]
|
/// #[actix_web::test]
|
||||||
/// async fn test_example() {
|
/// async fn test_example() {
|
||||||
/// let mut srv = TestServer::start(
|
/// let mut srv = TestServer::start(
|
||||||
/// || HttpService::new(
|
/// || HttpService::new(
|
||||||
|
|
|
@ -1,6 +1,19 @@
|
||||||
# Changes
|
# Changes
|
||||||
|
|
||||||
## Unreleased - 2021-xx-xx
|
## Unreleased - 2021-xx-xx
|
||||||
|
### Removed
|
||||||
|
* `client` module. [#2425]
|
||||||
|
* `trust-dns` feature. [#2425]
|
||||||
|
|
||||||
|
[#2425]: https://github.com/actix/actix-web/pull/2425
|
||||||
|
|
||||||
|
|
||||||
|
## 3.0.0-beta.11 - 2021-10-20
|
||||||
|
### Changed
|
||||||
|
* Updated rustls to v0.20. [#2414]
|
||||||
|
* Minimum supported Rust version (MSRV) is now 1.52.
|
||||||
|
|
||||||
|
[#2414]: https://github.com/actix/actix-web/pull/2414
|
||||||
|
|
||||||
|
|
||||||
## 3.0.0-beta.10 - 2021-09-09
|
## 3.0.0-beta.10 - 2021-09-09
|
||||||
|
|
|
@ -1,14 +1,17 @@
|
||||||
[package]
|
[package]
|
||||||
name = "actix-http"
|
name = "actix-http"
|
||||||
version = "3.0.0-beta.10"
|
version = "3.0.0-beta.11"
|
||||||
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
||||||
description = "HTTP primitives for the Actix ecosystem"
|
description = "HTTP primitives for the Actix ecosystem"
|
||||||
keywords = ["actix", "http", "framework", "async", "futures"]
|
keywords = ["actix", "http", "framework", "async", "futures"]
|
||||||
homepage = "https://actix.rs"
|
homepage = "https://actix.rs"
|
||||||
repository = "https://github.com/actix/actix-web"
|
repository = "https://github.com/actix/actix-web.git"
|
||||||
categories = ["network-programming", "asynchronous",
|
categories = [
|
||||||
"web-programming::http-server",
|
"network-programming",
|
||||||
"web-programming::websocket"]
|
"asynchronous",
|
||||||
|
"web-programming::http-server",
|
||||||
|
"web-programming::websocket",
|
||||||
|
]
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
|
@ -24,19 +27,16 @@ path = "src/lib.rs"
|
||||||
default = []
|
default = []
|
||||||
|
|
||||||
# openssl
|
# openssl
|
||||||
openssl = ["actix-tls/openssl"]
|
openssl = ["actix-tls/accept", "actix-tls/openssl"]
|
||||||
|
|
||||||
# rustls support
|
# rustls support
|
||||||
rustls = ["actix-tls/rustls"]
|
rustls = ["actix-tls/accept", "actix-tls/rustls"]
|
||||||
|
|
||||||
# enable compression support
|
# enable compression support
|
||||||
compress-brotli = ["brotli2", "__compress"]
|
compress-brotli = ["brotli2", "__compress"]
|
||||||
compress-gzip = ["flate2", "__compress"]
|
compress-gzip = ["flate2", "__compress"]
|
||||||
compress-zstd = ["zstd", "__compress"]
|
compress-zstd = ["zstd", "__compress"]
|
||||||
|
|
||||||
# trust-dns as client dns resolver
|
|
||||||
trust-dns = ["trust-dns-resolver"]
|
|
||||||
|
|
||||||
# Internal (PRIVATE!) features used to aid testing and cheking feature status.
|
# Internal (PRIVATE!) features used to aid testing and cheking feature status.
|
||||||
# Don't rely on these whatsoever. They may disappear at anytime.
|
# Don't rely on these whatsoever. They may disappear at anytime.
|
||||||
__compress = []
|
__compress = []
|
||||||
|
@ -46,7 +46,6 @@ actix-service = "2.0.0"
|
||||||
actix-codec = "0.4.0"
|
actix-codec = "0.4.0"
|
||||||
actix-utils = "3.0.0"
|
actix-utils = "3.0.0"
|
||||||
actix-rt = "2.2"
|
actix-rt = "2.2"
|
||||||
actix-tls = { version = "3.0.0-beta.5", features = ["accept", "connect"] }
|
|
||||||
|
|
||||||
ahash = "0.7"
|
ahash = "0.7"
|
||||||
base64 = "0.13"
|
base64 = "0.13"
|
||||||
|
@ -64,7 +63,6 @@ httpdate = "1.0.1"
|
||||||
itoa = "0.4"
|
itoa = "0.4"
|
||||||
language-tags = "0.3"
|
language-tags = "0.3"
|
||||||
local-channel = "0.1"
|
local-channel = "0.1"
|
||||||
once_cell = "1.5"
|
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
mime = "0.3"
|
mime = "0.3"
|
||||||
percent-encoding = "2.1"
|
percent-encoding = "2.1"
|
||||||
|
@ -75,27 +73,28 @@ sha-1 = "0.9"
|
||||||
smallvec = "1.6.1"
|
smallvec = "1.6.1"
|
||||||
tokio = { version = "1.2", features = ["sync"] }
|
tokio = { version = "1.2", features = ["sync"] }
|
||||||
|
|
||||||
|
# tls
|
||||||
|
actix-tls = { version = "3.0.0-beta.7", default-features = false, optional = true }
|
||||||
|
|
||||||
# compression
|
# compression
|
||||||
brotli2 = { version="0.3.2", optional = true }
|
brotli2 = { version="0.3.2", optional = true }
|
||||||
flate2 = { version = "1.0.13", optional = true }
|
flate2 = { version = "1.0.13", optional = true }
|
||||||
zstd = { version = "0.7", optional = true }
|
zstd = { version = "0.7", optional = true }
|
||||||
|
|
||||||
trust-dns-resolver = { version = "0.20.0", optional = true }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
actix-server = "2.0.0-beta.3"
|
actix-server = "2.0.0-beta.3"
|
||||||
actix-http-test = { version = "3.0.0-beta.5", features = ["openssl"] }
|
actix-http-test = { version = "3.0.0-beta.5", features = ["openssl"] }
|
||||||
actix-tls = { version = "3.0.0-beta.5", features = ["openssl"] }
|
actix-tls = { version = "3.0.0-beta.7", features = ["openssl"] }
|
||||||
async-stream = "0.3"
|
async-stream = "0.3"
|
||||||
criterion = { version = "0.3", features = ["html_reports"] }
|
criterion = { version = "0.3", features = ["html_reports"] }
|
||||||
env_logger = "0.8"
|
env_logger = "0.8"
|
||||||
rcgen = "0.8"
|
rcgen = "0.8"
|
||||||
regex = "1.3"
|
regex = "1.3"
|
||||||
|
rustls-pemfile = "0.2"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
tls-openssl = { version = "0.10", package = "openssl" }
|
tls-openssl = { package = "openssl", version = "0.10.9" }
|
||||||
tls-rustls = { version = "0.19", package = "rustls" }
|
tls-rustls = { package = "rustls", version = "0.20.0" }
|
||||||
webpki = { version = "0.21" }
|
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "ws"
|
name = "ws"
|
||||||
|
|
|
@ -3,18 +3,18 @@
|
||||||
> HTTP primitives for the Actix ecosystem.
|
> HTTP primitives for the Actix ecosystem.
|
||||||
|
|
||||||
[](https://crates.io/crates/actix-http)
|
[](https://crates.io/crates/actix-http)
|
||||||
[](https://docs.rs/actix-http/3.0.0-beta.10)
|
[](https://docs.rs/actix-http/3.0.0-beta.11)
|
||||||
[](https://blog.rust-lang.org/2020/03/12/Rust-1.51.html)
|
[](https://blog.rust-lang.org/2021/05/06/Rust-1.52.0.html)
|
||||||

|

|
||||||
<br />
|
<br />
|
||||||
[](https://deps.rs/crate/actix-http/3.0.0-beta.10)
|
[](https://deps.rs/crate/actix-http/3.0.0-beta.11)
|
||||||
[](https://crates.io/crates/actix-http)
|
[](https://crates.io/crates/actix-http)
|
||||||
[](https://discord.gg/NWpN5mmg3x)
|
[](https://discord.gg/NWpN5mmg3x)
|
||||||
|
|
||||||
## Documentation & Resources
|
## Documentation & Resources
|
||||||
|
|
||||||
- [API Documentation](https://docs.rs/actix-http)
|
- [API Documentation](https://docs.rs/actix-http)
|
||||||
- Minimum Supported Rust Version (MSRV): 1.51.0
|
- Minimum Supported Rust Version (MSRV): 1.52
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
|
|
||||||
|
|
|
@ -85,22 +85,31 @@ impl Stream for Heartbeat {
|
||||||
fn tls_config() -> rustls::ServerConfig {
|
fn tls_config() -> rustls::ServerConfig {
|
||||||
use std::io::BufReader;
|
use std::io::BufReader;
|
||||||
|
|
||||||
use rustls::{
|
use rustls::{Certificate, PrivateKey};
|
||||||
internal::pemfile::{certs, pkcs8_private_keys},
|
use rustls_pemfile::{certs, pkcs8_private_keys};
|
||||||
NoClientAuth, ServerConfig,
|
|
||||||
};
|
|
||||||
|
|
||||||
let cert = rcgen::generate_simple_self_signed(vec!["localhost".to_owned()]).unwrap();
|
let cert = rcgen::generate_simple_self_signed(vec!["localhost".to_owned()]).unwrap();
|
||||||
let cert_file = cert.serialize_pem().unwrap();
|
let cert_file = cert.serialize_pem().unwrap();
|
||||||
let key_file = cert.serialize_private_key_pem();
|
let key_file = cert.serialize_private_key_pem();
|
||||||
|
|
||||||
let mut config = ServerConfig::new(NoClientAuth::new());
|
|
||||||
let cert_file = &mut BufReader::new(cert_file.as_bytes());
|
let cert_file = &mut BufReader::new(cert_file.as_bytes());
|
||||||
let key_file = &mut BufReader::new(key_file.as_bytes());
|
let key_file = &mut BufReader::new(key_file.as_bytes());
|
||||||
|
|
||||||
let cert_chain = certs(cert_file).unwrap();
|
let cert_chain = certs(cert_file)
|
||||||
|
.unwrap()
|
||||||
|
.into_iter()
|
||||||
|
.map(Certificate)
|
||||||
|
.collect();
|
||||||
let mut keys = pkcs8_private_keys(key_file).unwrap();
|
let mut keys = pkcs8_private_keys(key_file).unwrap();
|
||||||
config.set_single_cert(cert_chain, keys.remove(0)).unwrap();
|
|
||||||
|
let mut config = rustls::ServerConfig::builder()
|
||||||
|
.with_safe_defaults()
|
||||||
|
.with_no_client_auth()
|
||||||
|
.with_single_cert(cert_chain, PrivateKey(keys.remove(0)))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
config.alpn_protocols.push(b"http/1.1".to_vec());
|
||||||
|
config.alpn_protocols.push(b"h2".to_vec());
|
||||||
|
|
||||||
config
|
config
|
||||||
}
|
}
|
||||||
|
|
|
@ -303,9 +303,9 @@ where
|
||||||
body: &impl MessageBody,
|
body: &impl MessageBody,
|
||||||
) -> Result<BodySize, DispatchError> {
|
) -> Result<BodySize, DispatchError> {
|
||||||
let size = body.size();
|
let size = body.size();
|
||||||
let mut this = self.project();
|
let this = self.project();
|
||||||
this.codec
|
this.codec
|
||||||
.encode(Message::Item((message, size)), &mut this.write_buf)
|
.encode(Message::Item((message, size)), this.write_buf)
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
if let Some(mut payload) = this.payload.take() {
|
if let Some(mut payload) = this.payload.take() {
|
||||||
payload.set_error(PayloadError::Incomplete(None));
|
payload.set_error(PayloadError::Incomplete(None));
|
||||||
|
@ -425,13 +425,13 @@ where
|
||||||
Poll::Ready(Some(Ok(item))) => {
|
Poll::Ready(Some(Ok(item))) => {
|
||||||
this.codec.encode(
|
this.codec.encode(
|
||||||
Message::Chunk(Some(item)),
|
Message::Chunk(Some(item)),
|
||||||
&mut this.write_buf,
|
this.write_buf,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Poll::Ready(None) => {
|
Poll::Ready(None) => {
|
||||||
this.codec
|
this.codec
|
||||||
.encode(Message::Chunk(None), &mut this.write_buf)?;
|
.encode(Message::Chunk(None), this.write_buf)?;
|
||||||
// payload stream finished.
|
// payload stream finished.
|
||||||
// set state to None and handle next message
|
// set state to None and handle next message
|
||||||
this.state.set(State::None);
|
this.state.set(State::None);
|
||||||
|
@ -460,13 +460,13 @@ where
|
||||||
Poll::Ready(Some(Ok(item))) => {
|
Poll::Ready(Some(Ok(item))) => {
|
||||||
this.codec.encode(
|
this.codec.encode(
|
||||||
Message::Chunk(Some(item)),
|
Message::Chunk(Some(item)),
|
||||||
&mut this.write_buf,
|
this.write_buf,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Poll::Ready(None) => {
|
Poll::Ready(None) => {
|
||||||
this.codec
|
this.codec
|
||||||
.encode(Message::Chunk(None), &mut this.write_buf)?;
|
.encode(Message::Chunk(None), this.write_buf)?;
|
||||||
// payload stream finished.
|
// payload stream finished.
|
||||||
// set state to None and handle next message
|
// set state to None and handle next message
|
||||||
this.state.set(State::None);
|
this.state.set(State::None);
|
||||||
|
@ -592,7 +592,7 @@ where
|
||||||
let mut updated = false;
|
let mut updated = false;
|
||||||
let mut this = self.as_mut().project();
|
let mut this = self.as_mut().project();
|
||||||
loop {
|
loop {
|
||||||
match this.codec.decode(&mut this.read_buf) {
|
match this.codec.decode(this.read_buf) {
|
||||||
Ok(Some(msg)) => {
|
Ok(Some(msg)) => {
|
||||||
updated = true;
|
updated = true;
|
||||||
this.flags.insert(Flags::STARTED);
|
this.flags.insert(Flags::STARTED);
|
||||||
|
|
|
@ -20,6 +20,7 @@ const AVERAGE_HEADER_SIZE: usize = 30;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct MessageEncoder<T: MessageType> {
|
pub(crate) struct MessageEncoder<T: MessageType> {
|
||||||
|
#[allow(dead_code)]
|
||||||
pub length: BodySize,
|
pub length: BodySize,
|
||||||
pub te: TransferEncoding,
|
pub te: TransferEncoding,
|
||||||
_phantom: PhantomData<T>,
|
_phantom: PhantomData<T>,
|
||||||
|
|
|
@ -177,7 +177,7 @@ mod rustls {
|
||||||
> {
|
> {
|
||||||
let mut protos = vec![b"h2".to_vec()];
|
let mut protos = vec![b"h2".to_vec()];
|
||||||
protos.extend_from_slice(&config.alpn_protocols);
|
protos.extend_from_slice(&config.alpn_protocols);
|
||||||
config.set_protocols(&protos);
|
config.alpn_protocols = protos;
|
||||||
|
|
||||||
Acceptor::new(config)
|
Acceptor::new(config)
|
||||||
.map_err(TlsError::Tls)
|
.map_err(TlsError::Tls)
|
||||||
|
|
|
@ -29,7 +29,6 @@ extern crate log;
|
||||||
|
|
||||||
pub mod body;
|
pub mod body;
|
||||||
mod builder;
|
mod builder;
|
||||||
pub mod client;
|
|
||||||
mod config;
|
mod config;
|
||||||
|
|
||||||
#[cfg(feature = "__compress")]
|
#[cfg(feature = "__compress")]
|
||||||
|
@ -103,14 +102,9 @@ type ConnectCallback<IO> = dyn Fn(&IO, &mut Extensions);
|
||||||
///
|
///
|
||||||
/// # Implementation Details
|
/// # Implementation Details
|
||||||
/// Uses Option to reduce necessary allocations when merging with request extensions.
|
/// Uses Option to reduce necessary allocations when merging with request extensions.
|
||||||
|
#[derive(Default)]
|
||||||
pub(crate) struct OnConnectData(Option<Extensions>);
|
pub(crate) struct OnConnectData(Option<Extensions>);
|
||||||
|
|
||||||
impl Default for OnConnectData {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl OnConnectData {
|
impl OnConnectData {
|
||||||
/// Construct by calling the on-connect callback with the underlying transport I/O.
|
/// Construct by calling the on-connect callback with the underlying transport I/O.
|
||||||
pub(crate) fn from_io<T>(
|
pub(crate) fn from_io<T>(
|
||||||
|
|
|
@ -263,7 +263,7 @@ mod openssl {
|
||||||
mod rustls {
|
mod rustls {
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
use actix_tls::accept::rustls::{Acceptor, ServerConfig, Session, TlsStream};
|
use actix_tls::accept::rustls::{Acceptor, ServerConfig, TlsStream};
|
||||||
use actix_tls::accept::TlsError;
|
use actix_tls::accept::TlsError;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -308,14 +308,13 @@ mod rustls {
|
||||||
> {
|
> {
|
||||||
let mut protos = vec![b"h2".to_vec(), b"http/1.1".to_vec()];
|
let mut protos = vec![b"h2".to_vec(), b"http/1.1".to_vec()];
|
||||||
protos.extend_from_slice(&config.alpn_protocols);
|
protos.extend_from_slice(&config.alpn_protocols);
|
||||||
config.set_protocols(&protos);
|
config.alpn_protocols = protos;
|
||||||
|
|
||||||
Acceptor::new(config)
|
Acceptor::new(config)
|
||||||
.map_err(TlsError::Tls)
|
.map_err(TlsError::Tls)
|
||||||
.map_init_err(|_| panic!())
|
.map_init_err(|_| panic!())
|
||||||
.and_then(|io: TlsStream<TcpStream>| async {
|
.and_then(|io: TlsStream<TcpStream>| async {
|
||||||
let proto = if let Some(protos) = io.get_ref().1.get_alpn_protocol()
|
let proto = if let Some(protos) = io.get_ref().1.alpn_protocol() {
|
||||||
{
|
|
||||||
if protos.windows(2).any(|window| window == b"h2") {
|
if protos.windows(2).any(|window| window == b"h2") {
|
||||||
Protocol::Http2
|
Protocol::Http2
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -25,8 +25,8 @@ pub fn apply_mask_fast32(buf: &mut [u8], mask: [u8; 4]) {
|
||||||
//
|
//
|
||||||
// un aligned prefix and suffix would be mask/unmask per byte.
|
// un aligned prefix and suffix would be mask/unmask per byte.
|
||||||
// proper aligned middle slice goes into fast path and operates on 4-byte blocks.
|
// proper aligned middle slice goes into fast path and operates on 4-byte blocks.
|
||||||
let (mut prefix, words, mut suffix) = unsafe { buf.align_to_mut::<u32>() };
|
let (prefix, words, suffix) = unsafe { buf.align_to_mut::<u32>() };
|
||||||
apply_mask_fallback(&mut prefix, mask);
|
apply_mask_fallback(prefix, mask);
|
||||||
let head = prefix.len() & 3;
|
let head = prefix.len() & 3;
|
||||||
let mask_u32 = if head > 0 {
|
let mask_u32 = if head > 0 {
|
||||||
if cfg!(target_endian = "big") {
|
if cfg!(target_endian = "big") {
|
||||||
|
@ -40,7 +40,7 @@ pub fn apply_mask_fast32(buf: &mut [u8], mask: [u8; 4]) {
|
||||||
for word in words.iter_mut() {
|
for word in words.iter_mut() {
|
||||||
*word ^= mask_u32;
|
*word ^= mask_u32;
|
||||||
}
|
}
|
||||||
apply_mask_fallback(&mut suffix, mask_u32.to_ne_bytes());
|
apply_mask_fallback(suffix, mask_u32.to_ne_bytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
extern crate tls_rustls as rustls;
|
extern crate tls_rustls as rustls;
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
convert::Infallible,
|
convert::{Infallible, TryFrom},
|
||||||
io::{self, BufReader, Write},
|
io::{self, BufReader, Write},
|
||||||
net::{SocketAddr, TcpStream as StdTcpStream},
|
net::{SocketAddr, TcpStream as StdTcpStream},
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
|
@ -20,16 +20,17 @@ use actix_http::{
|
||||||
};
|
};
|
||||||
use actix_http_test::test_server;
|
use actix_http_test::test_server;
|
||||||
use actix_service::{fn_factory_with_config, fn_service};
|
use actix_service::{fn_factory_with_config, fn_service};
|
||||||
|
use actix_tls::connect::tls::rustls::webpki_roots_cert_store;
|
||||||
use actix_utils::future::{err, ok};
|
use actix_utils::future::{err, ok};
|
||||||
use bytes::{Bytes, BytesMut};
|
use bytes::{Bytes, BytesMut};
|
||||||
use derive_more::{Display, Error};
|
use derive_more::{Display, Error};
|
||||||
use futures_core::Stream;
|
use futures_core::Stream;
|
||||||
use futures_util::stream::{once, StreamExt as _};
|
use futures_util::stream::{once, StreamExt as _};
|
||||||
use rustls::{
|
use rustls::{
|
||||||
internal::pemfile::{certs, pkcs8_private_keys},
|
Certificate, OwnedTrustAnchor, PrivateKey, RootCertStore,
|
||||||
NoClientAuth, ServerConfig as RustlsServerConfig, Session,
|
ServerConfig as RustlsServerConfig, ServerName,
|
||||||
};
|
};
|
||||||
use webpki::DNSNameRef;
|
use rustls_pemfile::{certs, pkcs8_private_keys};
|
||||||
|
|
||||||
async fn load_body<S>(mut stream: S) -> Result<BytesMut, PayloadError>
|
async fn load_body<S>(mut stream: S) -> Result<BytesMut, PayloadError>
|
||||||
where
|
where
|
||||||
|
@ -47,13 +48,24 @@ fn tls_config() -> RustlsServerConfig {
|
||||||
let cert_file = cert.serialize_pem().unwrap();
|
let cert_file = cert.serialize_pem().unwrap();
|
||||||
let key_file = cert.serialize_private_key_pem();
|
let key_file = cert.serialize_private_key_pem();
|
||||||
|
|
||||||
let mut config = RustlsServerConfig::new(NoClientAuth::new());
|
|
||||||
let cert_file = &mut BufReader::new(cert_file.as_bytes());
|
let cert_file = &mut BufReader::new(cert_file.as_bytes());
|
||||||
let key_file = &mut BufReader::new(key_file.as_bytes());
|
let key_file = &mut BufReader::new(key_file.as_bytes());
|
||||||
|
|
||||||
let cert_chain = certs(cert_file).unwrap();
|
let cert_chain = certs(cert_file)
|
||||||
|
.unwrap()
|
||||||
|
.into_iter()
|
||||||
|
.map(Certificate)
|
||||||
|
.collect();
|
||||||
let mut keys = pkcs8_private_keys(key_file).unwrap();
|
let mut keys = pkcs8_private_keys(key_file).unwrap();
|
||||||
config.set_single_cert(cert_chain, keys.remove(0)).unwrap();
|
|
||||||
|
let mut config = RustlsServerConfig::builder()
|
||||||
|
.with_safe_defaults()
|
||||||
|
.with_no_client_auth()
|
||||||
|
.with_single_cert(cert_chain, PrivateKey(keys.remove(0)))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
config.alpn_protocols.push(HTTP1_1_ALPN_PROTOCOL.to_vec());
|
||||||
|
config.alpn_protocols.push(H2_ALPN_PROTOCOL.to_vec());
|
||||||
|
|
||||||
config
|
config
|
||||||
}
|
}
|
||||||
|
@ -62,19 +74,28 @@ pub fn get_negotiated_alpn_protocol(
|
||||||
addr: SocketAddr,
|
addr: SocketAddr,
|
||||||
client_alpn_protocol: &[u8],
|
client_alpn_protocol: &[u8],
|
||||||
) -> Option<Vec<u8>> {
|
) -> Option<Vec<u8>> {
|
||||||
let mut config = rustls::ClientConfig::new();
|
let mut config = rustls::ClientConfig::builder()
|
||||||
|
.with_safe_defaults()
|
||||||
|
.with_root_certificates(webpki_roots_cert_store())
|
||||||
|
.with_no_client_auth();
|
||||||
|
|
||||||
config.alpn_protocols.push(client_alpn_protocol.to_vec());
|
config.alpn_protocols.push(client_alpn_protocol.to_vec());
|
||||||
let mut sess = rustls::ClientSession::new(
|
|
||||||
&Arc::new(config),
|
let mut sess = rustls::ClientConnection::new(
|
||||||
DNSNameRef::try_from_ascii_str("localhost").unwrap(),
|
Arc::new(config),
|
||||||
);
|
ServerName::try_from("localhost").unwrap(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let mut sock = StdTcpStream::connect(addr).unwrap();
|
let mut sock = StdTcpStream::connect(addr).unwrap();
|
||||||
let mut stream = rustls::Stream::new(&mut sess, &mut sock);
|
let mut stream = rustls::Stream::new(&mut sess, &mut sock);
|
||||||
|
|
||||||
// The handshake will fails because the client will not be able to verify the server
|
// The handshake will fails because the client will not be able to verify the server
|
||||||
// certificate, but it doesn't matter here as we are just interested in the negotiated ALPN
|
// certificate, but it doesn't matter here as we are just interested in the negotiated ALPN
|
||||||
// protocol
|
// protocol
|
||||||
let _ = stream.flush();
|
let _ = stream.flush();
|
||||||
sess.get_alpn_protocol().map(|proto| proto.to_vec())
|
|
||||||
|
sess.alpn_protocol().map(|proto| proto.to_vec())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
|
|
|
@ -3,6 +3,10 @@
|
||||||
## Unreleased - 2021-xx-xx
|
## Unreleased - 2021-xx-xx
|
||||||
|
|
||||||
|
|
||||||
|
## 0.4.0-beta.7 - 2021-10-20
|
||||||
|
* Minimum supported Rust version (MSRV) is now 1.52.
|
||||||
|
|
||||||
|
|
||||||
## 0.4.0-beta.6 - 2021-09-09
|
## 0.4.0-beta.6 - 2021-09-09
|
||||||
* Minimum supported Rust version (MSRV) is now 1.51.
|
* Minimum supported Rust version (MSRV) is now 1.51.
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "actix-multipart"
|
name = "actix-multipart"
|
||||||
version = "0.4.0-beta.6"
|
version = "0.4.0-beta.7"
|
||||||
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
||||||
description = "Multipart form support for Actix Web"
|
description = "Multipart form support for Actix Web"
|
||||||
keywords = ["http", "web", "framework", "async", "futures"]
|
keywords = ["http", "web", "framework", "async", "futures"]
|
||||||
|
@ -14,7 +14,7 @@ name = "actix_multipart"
|
||||||
path = "src/lib.rs"
|
path = "src/lib.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix-web = { version = "4.0.0-beta.9", default-features = false }
|
actix-web = { version = "4.0.0-beta.10", default-features = false }
|
||||||
actix-utils = "3.0.0"
|
actix-utils = "3.0.0"
|
||||||
|
|
||||||
bytes = "1"
|
bytes = "1"
|
||||||
|
@ -29,6 +29,6 @@ twoway = "0.2"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
actix-rt = "2.2"
|
actix-rt = "2.2"
|
||||||
actix-http = "3.0.0-beta.10"
|
actix-http = "3.0.0-beta.11"
|
||||||
tokio = { version = "1", features = ["sync"] }
|
tokio = { version = "1", features = ["sync"] }
|
||||||
tokio-stream = "0.1"
|
tokio-stream = "0.1"
|
||||||
|
|
|
@ -3,15 +3,15 @@
|
||||||
> Multipart form support for Actix Web.
|
> Multipart form support for Actix Web.
|
||||||
|
|
||||||
[](https://crates.io/crates/actix-multipart)
|
[](https://crates.io/crates/actix-multipart)
|
||||||
[](https://docs.rs/actix-multipart/0.4.0-beta.6)
|
[](https://docs.rs/actix-multipart/0.4.0-beta.7)
|
||||||
[](https://blog.rust-lang.org/2020/03/12/Rust-1.51.html)
|
[](https://blog.rust-lang.org/2021/05/06/Rust-1.52.0.html)
|
||||||

|

|
||||||
<br />
|
<br />
|
||||||
[](https://deps.rs/crate/actix-multipart/0.4.0-beta.6)
|
[](https://deps.rs/crate/actix-multipart/0.4.0-beta.7)
|
||||||
[](https://crates.io/crates/actix-multipart)
|
[](https://crates.io/crates/actix-multipart)
|
||||||
[](https://discord.gg/NWpN5mmg3x)
|
[](https://discord.gg/NWpN5mmg3x)
|
||||||
|
|
||||||
## Documentation & Resources
|
## Documentation & Resources
|
||||||
|
|
||||||
- [API Documentation](https://docs.rs/actix-multipart)
|
- [API Documentation](https://docs.rs/actix-multipart)
|
||||||
- Minimum Supported Rust Version (MSRV): 1.51.0
|
- Minimum Supported Rust Version (MSRV): 1.52
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
# Changes
|
# Changes
|
||||||
|
|
||||||
## Unreleased - 2021-xx-xx
|
## Unreleased - 2021-xx-xx
|
||||||
|
* Minimum supported Rust version (MSRV) is now 1.52.
|
||||||
|
|
||||||
|
|
||||||
## 0.5.0-beta.2 - 2021-09-09
|
## 0.5.0-beta.2 - 2021-09-09
|
||||||
|
|
|
@ -394,9 +394,7 @@ impl ResourceDef {
|
||||||
pub fn set_name(&mut self, name: impl Into<String>) {
|
pub fn set_name(&mut self, name: impl Into<String>) {
|
||||||
let name = name.into();
|
let name = name.into();
|
||||||
|
|
||||||
if name.is_empty() {
|
assert!(!name.is_empty(), "resource name should not be empty");
|
||||||
panic!("resource name should not be empty");
|
|
||||||
}
|
|
||||||
|
|
||||||
self.name = Some(name)
|
self.name = Some(name)
|
||||||
}
|
}
|
||||||
|
@ -978,9 +976,7 @@ impl ResourceDef {
|
||||||
|
|
||||||
let (name, pattern) = match param.find(':') {
|
let (name, pattern) = match param.find(':') {
|
||||||
Some(idx) => {
|
Some(idx) => {
|
||||||
if tail {
|
assert!(!tail, "custom regex is not supported for tail match");
|
||||||
panic!("custom regex is not supported for tail match");
|
|
||||||
}
|
|
||||||
|
|
||||||
let (name, pattern) = param.split_at(idx);
|
let (name, pattern) = param.split_at(idx);
|
||||||
(name, &pattern[1..])
|
(name, &pattern[1..])
|
||||||
|
@ -1087,12 +1083,12 @@ impl ResourceDef {
|
||||||
re.push_str(&escape(unprocessed));
|
re.push_str(&escape(unprocessed));
|
||||||
}
|
}
|
||||||
|
|
||||||
if dyn_segment_count > MAX_DYNAMIC_SEGMENTS {
|
assert!(
|
||||||
panic!(
|
dyn_segment_count <= MAX_DYNAMIC_SEGMENTS,
|
||||||
"Only {} dynamic segments are allowed, provided: {}",
|
"Only {} dynamic segments are allowed, provided: {}",
|
||||||
MAX_DYNAMIC_SEGMENTS, dyn_segment_count
|
MAX_DYNAMIC_SEGMENTS,
|
||||||
);
|
dyn_segment_count
|
||||||
}
|
);
|
||||||
|
|
||||||
// Store the pattern in capture group #1 to have context info outside it
|
// Store the pattern in capture group #1 to have context info outside it
|
||||||
let mut re = format!("({})", re);
|
let mut re = format!("({})", re);
|
||||||
|
|
|
@ -6,8 +6,9 @@ use crate::{IntoPatterns, Resource, ResourceDef, ResourcePath};
|
||||||
pub struct ResourceId(pub u16);
|
pub struct ResourceId(pub u16);
|
||||||
|
|
||||||
/// Information about current resource
|
/// Information about current resource
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ResourceInfo {
|
pub struct ResourceInfo {
|
||||||
|
#[allow(dead_code)]
|
||||||
resource: ResourceId,
|
resource: ResourceId,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,13 @@
|
||||||
## Unreleased - 2021-xx-xx
|
## Unreleased - 2021-xx-xx
|
||||||
|
|
||||||
|
|
||||||
|
## 0.1.0-beta.5 - 2021-10-20
|
||||||
|
* Updated rustls to v0.20. [#2414]
|
||||||
|
* Minimum supported Rust version (MSRV) is now 1.52.
|
||||||
|
|
||||||
|
[#2414]: https://github.com/actix/actix-web/pull/2414
|
||||||
|
|
||||||
|
|
||||||
## 0.1.0-beta.4 - 2021-09-09
|
## 0.1.0-beta.4 - 2021-09-09
|
||||||
* Minimum supported Rust version (MSRV) is now 1.51.
|
* Minimum supported Rust version (MSRV) is now 1.51.
|
||||||
|
|
||||||
|
|
|
@ -1,32 +1,41 @@
|
||||||
[package]
|
[package]
|
||||||
name = "actix-test"
|
name = "actix-test"
|
||||||
version = "0.1.0-beta.4"
|
version = "0.1.0-beta.5"
|
||||||
authors = [
|
authors = [
|
||||||
"Nikolay Kim <fafhrd91@gmail.com>",
|
"Nikolay Kim <fafhrd91@gmail.com>",
|
||||||
"Rob Ede <robjtede@icloud.com>",
|
"Rob Ede <robjtede@icloud.com>",
|
||||||
]
|
]
|
||||||
edition = "2018"
|
|
||||||
description = "Integration testing tools for Actix Web applications"
|
description = "Integration testing tools for Actix Web applications"
|
||||||
|
keywords = ["http", "web", "framework", "async", "futures"]
|
||||||
|
homepage = "https://actix.rs"
|
||||||
|
repository = "https://github.com/actix/actix-web.git"
|
||||||
|
categories = [
|
||||||
|
"network-programming",
|
||||||
|
"asynchronous",
|
||||||
|
"web-programming::http-server",
|
||||||
|
"web-programming::websocket",
|
||||||
|
]
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
|
|
||||||
# rustls
|
# rustls
|
||||||
rustls = ["tls-rustls", "actix-http/rustls"]
|
rustls = ["tls-rustls", "actix-http/rustls", "awc/rustls"]
|
||||||
|
|
||||||
# openssl
|
# openssl
|
||||||
openssl = ["tls-openssl", "actix-http/openssl"]
|
openssl = ["tls-openssl", "actix-http/openssl", "awc/openssl"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix-codec = "0.4.0"
|
actix-codec = "0.4.0"
|
||||||
actix-http = "3.0.0-beta.10"
|
actix-http = "3.0.0-beta.11"
|
||||||
actix-http-test = "3.0.0-beta.5"
|
actix-http-test = "3.0.0-beta.5"
|
||||||
actix-service = "2.0.0"
|
actix-service = "2.0.0"
|
||||||
actix-utils = "3.0.0"
|
actix-utils = "3.0.0"
|
||||||
actix-web = { version = "4.0.0-beta.9", default-features = false, features = ["cookies"] }
|
actix-web = { version = "4.0.0-beta.10", default-features = false, features = ["cookies"] }
|
||||||
actix-rt = "2.1"
|
actix-rt = "2.1"
|
||||||
awc = { version = "3.0.0-beta.8", default-features = false, features = ["cookies"] }
|
awc = { version = "3.0.0-beta.9", default-features = false, features = ["cookies"] }
|
||||||
|
|
||||||
futures-core = { version = "0.3.7", default-features = false, features = ["std"] }
|
futures-core = { version = "0.3.7", default-features = false, features = ["std"] }
|
||||||
futures-util = { version = "0.3.7", default-features = false, features = [] }
|
futures-util = { version = "0.3.7", default-features = false, features = [] }
|
||||||
|
@ -35,4 +44,4 @@ serde = { version = "1", features = ["derive"] }
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
serde_urlencoded = "0.7"
|
serde_urlencoded = "0.7"
|
||||||
tls-openssl = { package = "openssl", version = "0.10.9", optional = true }
|
tls-openssl = { package = "openssl", version = "0.10.9", optional = true }
|
||||||
tls-rustls = { package = "rustls", version = "0.19.0", optional = true }
|
tls-rustls = { package = "rustls", version = "0.20.0", optional = true }
|
||||||
|
|
|
@ -64,7 +64,7 @@ pub use actix_web::test::{
|
||||||
/// Ok(HttpResponse::Ok())
|
/// Ok(HttpResponse::Ok())
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// #[actix_rt::test]
|
/// #[actix_web::test]
|
||||||
/// async fn test_example() {
|
/// async fn test_example() {
|
||||||
/// let srv = actix_test::start(||
|
/// let srv = actix_test::start(||
|
||||||
/// App::new().service(my_handler)
|
/// App::new().service(my_handler)
|
||||||
|
@ -104,7 +104,7 @@ where
|
||||||
/// Ok(HttpResponse::Ok())
|
/// Ok(HttpResponse::Ok())
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// #[actix_rt::test]
|
/// #[actix_web::test]
|
||||||
/// async fn test_example() {
|
/// async fn test_example() {
|
||||||
/// let srv = actix_test::start_with(actix_test::config().h1(), ||
|
/// let srv = actix_test::start_with(actix_test::config().h1(), ||
|
||||||
/// App::new().service(my_handler)
|
/// App::new().service(my_handler)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
# Changes
|
# Changes
|
||||||
|
|
||||||
## Unreleased - 2021-xx-xx
|
## Unreleased - 2021-xx-xx
|
||||||
|
* Minimum supported Rust version (MSRV) is now 1.52.
|
||||||
|
|
||||||
|
|
||||||
## 4.0.0-beta.7 - 2021-09-09
|
## 4.0.0-beta.7 - 2021-09-09
|
||||||
|
|
|
@ -16,8 +16,8 @@ path = "src/lib.rs"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix = { version = "0.12.0", default-features = false }
|
actix = { version = "0.12.0", default-features = false }
|
||||||
actix-codec = "0.4.0"
|
actix-codec = "0.4.0"
|
||||||
actix-http = "3.0.0-beta.10"
|
actix-http = "3.0.0-beta.11"
|
||||||
actix-web = { version = "4.0.0-beta.9", default-features = false }
|
actix-web = { version = "4.0.0-beta.10", default-features = false }
|
||||||
|
|
||||||
bytes = "1"
|
bytes = "1"
|
||||||
bytestring = "1"
|
bytestring = "1"
|
||||||
|
@ -27,8 +27,8 @@ tokio = { version = "1", features = ["sync"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
actix-rt = "2.2"
|
actix-rt = "2.2"
|
||||||
actix-test = "0.1.0-beta.3"
|
actix-test = "0.1.0-beta.5"
|
||||||
|
|
||||||
awc = { version = "3.0.0-beta.8", default-features = false }
|
awc = { version = "3.0.0-beta.9", default-features = false }
|
||||||
env_logger = "0.8"
|
env_logger = "0.8"
|
||||||
futures-util = { version = "0.3.7", default-features = false }
|
futures-util = { version = "0.3.7", default-features = false }
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
[](https://crates.io/crates/actix-web-actors)
|
[](https://crates.io/crates/actix-web-actors)
|
||||||
[](https://docs.rs/actix-web-actors/4.0.0-beta.7)
|
[](https://docs.rs/actix-web-actors/4.0.0-beta.7)
|
||||||
[](https://blog.rust-lang.org/2020/03/12/Rust-1.51.html)
|
[](https://blog.rust-lang.org/2021/05/06/Rust-1.52.0.html)
|
||||||

|

|
||||||
<br />
|
<br />
|
||||||
[](https://deps.rs/crate/actix-web-actors/4.0.0-beta.7)
|
[](https://deps.rs/crate/actix-web-actors/4.0.0-beta.7)
|
||||||
|
@ -14,4 +14,4 @@
|
||||||
## Documentation & Resources
|
## Documentation & Resources
|
||||||
|
|
||||||
- [API Documentation](https://docs.rs/actix-web-actors)
|
- [API Documentation](https://docs.rs/actix-web-actors)
|
||||||
- Minimum supported Rust version: 1.51 or later
|
- Minimum Supported Rust Version (MSRV): 1.52
|
||||||
|
|
|
@ -3,6 +3,15 @@
|
||||||
## Unreleased - 2021-xx-xx
|
## Unreleased - 2021-xx-xx
|
||||||
|
|
||||||
|
|
||||||
|
## 0.5.0-beta.5 - 2021-10-20
|
||||||
|
* Improve error recovery potential when macro input is invalid. [#2410]
|
||||||
|
* Add `#[actix_web::test]` macro for setting up tests with a runtime. [#2409]
|
||||||
|
* Minimum supported Rust version (MSRV) is now 1.52.
|
||||||
|
|
||||||
|
[#2410]: https://github.com/actix/actix-web/pull/2410
|
||||||
|
[#2409]: https://github.com/actix/actix-web/pull/2409
|
||||||
|
|
||||||
|
|
||||||
## 0.5.0-beta.4 - 2021-09-09
|
## 0.5.0-beta.4 - 2021-09-09
|
||||||
* In routing macros, paths are now validated at compile time. [#2350]
|
* In routing macros, paths are now validated at compile time. [#2350]
|
||||||
* Minimum supported Rust version (MSRV) is now 1.51.
|
* Minimum supported Rust version (MSRV) is now 1.51.
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
[package]
|
[package]
|
||||||
name = "actix-web-codegen"
|
name = "actix-web-codegen"
|
||||||
version = "0.5.0-beta.4"
|
version = "0.5.0-beta.5"
|
||||||
description = "Routing and runtime macros for Actix Web"
|
description = "Routing and runtime macros for Actix Web"
|
||||||
readme = "README.md"
|
|
||||||
homepage = "https://actix.rs"
|
homepage = "https://actix.rs"
|
||||||
repository = "https://github.com/actix/actix-web"
|
repository = "https://github.com/actix/actix-web.git"
|
||||||
documentation = "https://docs.rs/actix-web-codegen"
|
authors = [
|
||||||
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
"Nikolay Kim <fafhrd91@gmail.com>",
|
||||||
|
"Rob Ede <robjtede@icloud.com>",
|
||||||
|
]
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
|
@ -21,9 +22,10 @@ actix-router = "0.5.0-beta.2"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
actix-rt = "2.2"
|
actix-rt = "2.2"
|
||||||
actix-test = "0.1.0-beta.3"
|
actix-macros = "0.2.3"
|
||||||
|
actix-test = "0.1.0-beta.5"
|
||||||
actix-utils = "3.0.0"
|
actix-utils = "3.0.0"
|
||||||
actix-web = "4.0.0-beta.9"
|
actix-web = "4.0.0-beta.10"
|
||||||
|
|
||||||
futures-core = { version = "0.3.7", default-features = false, features = ["alloc"] }
|
futures-core = { version = "0.3.7", default-features = false, features = ["alloc"] }
|
||||||
trybuild = "1"
|
trybuild = "1"
|
||||||
|
|
|
@ -3,18 +3,18 @@
|
||||||
> Routing and runtime macros for Actix Web.
|
> Routing and runtime macros for Actix Web.
|
||||||
|
|
||||||
[](https://crates.io/crates/actix-web-codegen)
|
[](https://crates.io/crates/actix-web-codegen)
|
||||||
[](https://docs.rs/actix-web-codegen/0.5.0-beta.4)
|
[](https://docs.rs/actix-web-codegen/0.5.0-beta.5)
|
||||||
[](https://blog.rust-lang.org/2020/03/12/Rust-1.51.html)
|
[](https://blog.rust-lang.org/2021/05/06/Rust-1.52.0.html)
|
||||||

|

|
||||||
<br />
|
<br />
|
||||||
[](https://deps.rs/crate/actix-web-codegen/0.5.0-beta.4)
|
[](https://deps.rs/crate/actix-web-codegen/0.5.0-beta.5)
|
||||||
[](https://crates.io/crates/actix-web-codegen)
|
[](https://crates.io/crates/actix-web-codegen)
|
||||||
[](https://discord.gg/NWpN5mmg3x)
|
[](https://discord.gg/NWpN5mmg3x)
|
||||||
|
|
||||||
## Documentation & Resources
|
## Documentation & Resources
|
||||||
|
|
||||||
- [API Documentation](https://docs.rs/actix-web-codegen)
|
- [API Documentation](https://docs.rs/actix-web-codegen)
|
||||||
- Minimum supported Rust version: 1.51 or later.
|
- Minimum Supported Rust Version (MSRV): 1.52
|
||||||
|
|
||||||
## Compile Testing
|
## Compile Testing
|
||||||
|
|
||||||
|
|
|
@ -59,6 +59,7 @@
|
||||||
#![recursion_limit = "512"]
|
#![recursion_limit = "512"]
|
||||||
|
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
|
use quote::quote;
|
||||||
|
|
||||||
mod route;
|
mod route;
|
||||||
|
|
||||||
|
@ -157,24 +158,41 @@ method_macro! {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Marks async main function as the actix system entry-point.
|
/// Marks async main function as the actix system entry-point.
|
||||||
///
|
|
||||||
/// # Actix Web Re-export
|
|
||||||
/// This macro can be applied with `#[actix_web::main]` when used in Actix Web applications.
|
|
||||||
///
|
|
||||||
/// # Examples
|
/// # Examples
|
||||||
/// ```
|
/// ```
|
||||||
/// #[actix_web_codegen::main]
|
/// #[actix_web::main]
|
||||||
/// async fn main() {
|
/// async fn main() {
|
||||||
/// async { println!("Hello world"); }.await
|
/// async { println!("Hello world"); }.await
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
pub fn main(_: TokenStream, item: TokenStream) -> TokenStream {
|
pub fn main(_: TokenStream, item: TokenStream) -> TokenStream {
|
||||||
use quote::quote;
|
let mut output: TokenStream = (quote! {
|
||||||
let input = syn::parse_macro_input!(item as syn::ItemFn);
|
#[::actix_web::rt::main(system = "::actix_web::rt::System")]
|
||||||
(quote! {
|
|
||||||
#[actix_web::rt::main(system = "::actix_web::rt::System")]
|
|
||||||
#input
|
|
||||||
})
|
})
|
||||||
.into()
|
.into();
|
||||||
|
|
||||||
|
output.extend(item);
|
||||||
|
output
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Marks async test functions to use the actix system entry-point.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```
|
||||||
|
/// #[actix_web::test]
|
||||||
|
/// async fn test() {
|
||||||
|
/// assert_eq!(async { "Hello world" }.await, "Hello world");
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
#[proc_macro_attribute]
|
||||||
|
pub fn test(_: TokenStream, item: TokenStream) -> TokenStream {
|
||||||
|
let mut output: TokenStream = (quote! {
|
||||||
|
#[::actix_web::rt::test(system = "::actix_web::rt::System")]
|
||||||
|
})
|
||||||
|
.into();
|
||||||
|
|
||||||
|
output.extend(item);
|
||||||
|
output
|
||||||
}
|
}
|
||||||
|
|
|
@ -220,7 +220,7 @@ fn guess_resource_type(typ: &syn::Type) -> ResourceType {
|
||||||
impl Route {
|
impl Route {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
args: AttributeArgs,
|
args: AttributeArgs,
|
||||||
input: TokenStream,
|
ast: syn::ItemFn,
|
||||||
method: Option<MethodType>,
|
method: Option<MethodType>,
|
||||||
) -> syn::Result<Self> {
|
) -> syn::Result<Self> {
|
||||||
if args.is_empty() {
|
if args.is_empty() {
|
||||||
|
@ -234,14 +234,11 @@ impl Route {
|
||||||
),
|
),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
let ast: syn::ItemFn = syn::parse(input)?;
|
|
||||||
let name = ast.sig.ident.clone();
|
let name = ast.sig.ident.clone();
|
||||||
|
|
||||||
// Try and pull out the doc comments so that we can reapply them to the
|
// Try and pull out the doc comments so that we can reapply them to the generated struct.
|
||||||
// generated struct.
|
// Note that multi line doc comments are converted to multiple doc attributes.
|
||||||
//
|
|
||||||
// Note that multi line doc comments are converted to multiple doc
|
|
||||||
// attributes.
|
|
||||||
let doc_attributes = ast
|
let doc_attributes = ast
|
||||||
.attrs
|
.attrs
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -349,8 +346,28 @@ pub(crate) fn with_method(
|
||||||
input: TokenStream,
|
input: TokenStream,
|
||||||
) -> TokenStream {
|
) -> TokenStream {
|
||||||
let args = parse_macro_input!(args as syn::AttributeArgs);
|
let args = parse_macro_input!(args as syn::AttributeArgs);
|
||||||
match Route::new(args, input, method) {
|
|
||||||
|
let ast = match syn::parse::<syn::ItemFn>(input.clone()) {
|
||||||
|
Ok(ast) => ast,
|
||||||
|
// on parse error, make IDEs happy; see fn docs
|
||||||
|
Err(err) => return input_and_compile_error(input, err),
|
||||||
|
};
|
||||||
|
|
||||||
|
match Route::new(args, ast, method) {
|
||||||
Ok(route) => route.into_token_stream().into(),
|
Ok(route) => route.into_token_stream().into(),
|
||||||
Err(err) => err.to_compile_error().into(),
|
// on macro related error, make IDEs happy; see fn docs
|
||||||
|
Err(err) => input_and_compile_error(input, err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Converts the error to a token stream and appends it to the original input.
|
||||||
|
///
|
||||||
|
/// Returning the original input in addition to the error is good for IDEs which can gracefully
|
||||||
|
/// recover and show more precise errors within the macro body.
|
||||||
|
///
|
||||||
|
/// See <https://github.com/rust-analyzer/rust-analyzer/issues/10468> for more info.
|
||||||
|
fn input_and_compile_error(mut item: TokenStream, err: syn::Error) -> TokenStream {
|
||||||
|
let compile_err = TokenStream::from(err.to_compile_error());
|
||||||
|
item.extend(compile_err);
|
||||||
|
item
|
||||||
|
}
|
||||||
|
|
|
@ -256,7 +256,7 @@ async fn test_auto_async() {
|
||||||
assert!(response.status().is_success());
|
assert!(response.status().is_success());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_web::test]
|
||||||
async fn test_wrap() {
|
async fn test_wrap() {
|
||||||
let srv = actix_test::start(|| App::new().service(get_wrap));
|
let srv = actix_test::start(|| App::new().service(get_wrap));
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#[rustversion::stable(1.51)] // MSRV
|
#[rustversion::stable(1.52)] // MSRV
|
||||||
#[test]
|
#[test]
|
||||||
fn compile_macros() {
|
fn compile_macros() {
|
||||||
let t = trybuild::TestCases::new();
|
let t = trybuild::TestCases::new();
|
||||||
|
@ -13,4 +13,6 @@ fn compile_macros() {
|
||||||
t.compile_fail("tests/trybuild/route-malformed-path-fail.rs");
|
t.compile_fail("tests/trybuild/route-malformed-path-fail.rs");
|
||||||
|
|
||||||
t.pass("tests/trybuild/docstring-ok.rs");
|
t.pass("tests/trybuild/docstring-ok.rs");
|
||||||
|
|
||||||
|
t.pass("tests/trybuild/test-runtime.rs");
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,8 +4,8 @@ error: HTTP method defined more than once: `GET`
|
||||||
3 | #[route("/", method="GET", method="GET")]
|
3 | #[route("/", method="GET", method="GET")]
|
||||||
| ^^^^^
|
| ^^^^^
|
||||||
|
|
||||||
error[E0425]: cannot find value `index` in this scope
|
error[E0277]: the trait bound `fn() -> impl std::future::Future {index}: HttpServiceFactory` is not satisfied
|
||||||
--> $DIR/route-duplicate-method-fail.rs:12:55
|
--> $DIR/route-duplicate-method-fail.rs:12:55
|
||||||
|
|
|
|
||||||
12 | let srv = actix_test::start(|| App::new().service(index));
|
12 | let srv = actix_test::start(|| App::new().service(index));
|
||||||
| ^^^^^ not found in this scope
|
| ^^^^^ the trait `HttpServiceFactory` is not implemented for `fn() -> impl std::future::Future {index}`
|
||||||
|
|
|
@ -6,8 +6,8 @@ error: The #[route(..)] macro requires at least one `method` attribute
|
||||||
|
|
|
|
||||||
= note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
error[E0425]: cannot find value `index` in this scope
|
error[E0277]: the trait bound `fn() -> impl std::future::Future {index}: HttpServiceFactory` is not satisfied
|
||||||
--> $DIR/route-missing-method-fail.rs:12:55
|
--> $DIR/route-missing-method-fail.rs:12:55
|
||||||
|
|
|
|
||||||
12 | let srv = actix_test::start(|| App::new().service(index));
|
12 | let srv = actix_test::start(|| App::new().service(index));
|
||||||
| ^^^^^ not found in this scope
|
| ^^^^^ the trait `HttpServiceFactory` is not implemented for `fn() -> impl std::future::Future {index}`
|
||||||
|
|
|
@ -4,8 +4,8 @@ error: Unexpected HTTP method: `UNEXPECTED`
|
||||||
3 | #[route("/", method="UNEXPECTED")]
|
3 | #[route("/", method="UNEXPECTED")]
|
||||||
| ^^^^^^^^^^^^
|
| ^^^^^^^^^^^^
|
||||||
|
|
||||||
error[E0425]: cannot find value `index` in this scope
|
error[E0277]: the trait bound `fn() -> impl std::future::Future {index}: HttpServiceFactory` is not satisfied
|
||||||
--> $DIR/route-unexpected-method-fail.rs:12:55
|
--> $DIR/route-unexpected-method-fail.rs:12:55
|
||||||
|
|
|
|
||||||
12 | let srv = actix_test::start(|| App::new().service(index));
|
12 | let srv = actix_test::start(|| App::new().service(index));
|
||||||
| ^^^^^ not found in this scope
|
| ^^^^^ the trait `HttpServiceFactory` is not implemented for `fn() -> impl std::future::Future {index}`
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
#[actix_web::test]
|
||||||
|
async fn my_test() {
|
||||||
|
assert!(async { 1 }.await, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -3,6 +3,12 @@
|
||||||
## Unreleased - 2021-xx-xx
|
## Unreleased - 2021-xx-xx
|
||||||
|
|
||||||
|
|
||||||
|
## 3.0.0-beta.9 - 2021-10-20
|
||||||
|
* Updated rustls to v0.20. [#2414]
|
||||||
|
|
||||||
|
[#2414]: https://github.com/actix/actix-web/pull/2414
|
||||||
|
|
||||||
|
|
||||||
## 3.0.0-beta.8 - 2021-09-09
|
## 3.0.0-beta.8 - 2021-09-09
|
||||||
### Changed
|
### Changed
|
||||||
* Send headers within the redirect requests. [#2310]
|
* Send headers within the redirect requests. [#2310]
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "awc"
|
name = "awc"
|
||||||
version = "3.0.0-beta.8"
|
version = "3.0.0-beta.9"
|
||||||
authors = [
|
authors = [
|
||||||
"Nikolay Kim <fafhrd91@gmail.com>",
|
"Nikolay Kim <fafhrd91@gmail.com>",
|
||||||
"fakeshadow <24548779@qq.com>",
|
"fakeshadow <24548779@qq.com>",
|
||||||
|
@ -14,7 +14,7 @@ categories = [
|
||||||
"web-programming::websocket",
|
"web-programming::websocket",
|
||||||
]
|
]
|
||||||
homepage = "https://actix.rs"
|
homepage = "https://actix.rs"
|
||||||
repository = "https://github.com/actix/actix-web"
|
repository = "https://github.com/actix/actix-web.git"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
|
@ -30,10 +30,10 @@ features = ["openssl", "rustls", "compress-brotli", "compress-gzip", "compress-z
|
||||||
default = ["compress-brotli", "compress-gzip", "compress-zstd", "cookies"]
|
default = ["compress-brotli", "compress-gzip", "compress-zstd", "cookies"]
|
||||||
|
|
||||||
# openssl
|
# openssl
|
||||||
openssl = ["tls-openssl", "actix-http/openssl"]
|
openssl = ["tls-openssl", "actix-tls/openssl"]
|
||||||
|
|
||||||
# rustls
|
# rustls
|
||||||
rustls = ["tls-rustls", "actix-http/rustls"]
|
rustls = ["tls-rustls", "actix-tls/rustls"]
|
||||||
|
|
||||||
# Brotli algorithm content-encoding support
|
# Brotli algorithm content-encoding support
|
||||||
compress-brotli = ["actix-http/compress-brotli", "__compress"]
|
compress-brotli = ["actix-http/compress-brotli", "__compress"]
|
||||||
|
@ -46,7 +46,7 @@ compress-zstd = ["actix-http/compress-zstd", "__compress"]
|
||||||
cookies = ["cookie"]
|
cookies = ["cookie"]
|
||||||
|
|
||||||
# trust-dns as dns resolver
|
# trust-dns as dns resolver
|
||||||
trust-dns = ["actix-http/trust-dns"]
|
trust-dns = ["trust-dns-resolver"]
|
||||||
|
|
||||||
# Internal (PRIVATE!) features used to aid testing and cheking feature status.
|
# Internal (PRIVATE!) features used to aid testing and cheking feature status.
|
||||||
# Don't rely on these whatsoever. They may disappear at anytime.
|
# Don't rely on these whatsoever. They may disappear at anytime.
|
||||||
|
@ -55,15 +55,20 @@ __compress = []
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix-codec = "0.4.0"
|
actix-codec = "0.4.0"
|
||||||
actix-service = "2.0.0"
|
actix-service = "2.0.0"
|
||||||
actix-http = "3.0.0-beta.10"
|
actix-http = "3.0.0-beta.11"
|
||||||
actix-rt = { version = "2.1", default-features = false }
|
actix-rt = { version = "2.1", default-features = false }
|
||||||
|
actix-tls = { version = "3.0.0-beta.7", features = ["connect"] }
|
||||||
|
actix-utils = "3.0.0"
|
||||||
|
|
||||||
|
ahash = "0.7"
|
||||||
base64 = "0.13"
|
base64 = "0.13"
|
||||||
bytes = "1"
|
bytes = "1"
|
||||||
cfg-if = "1"
|
cfg-if = "1"
|
||||||
cookie = { version = "0.15", features = ["percent-encode"], optional = true }
|
|
||||||
derive_more = "0.99.5"
|
derive_more = "0.99.5"
|
||||||
futures-core = { version = "0.3.7", default-features = false }
|
futures-core = { version = "0.3.7", default-features = false }
|
||||||
|
futures-util = { version = "0.3.7", default-features = false }
|
||||||
|
h2 = "0.3"
|
||||||
|
http = "0.2"
|
||||||
itoa = "0.4"
|
itoa = "0.4"
|
||||||
log =" 0.4"
|
log =" 0.4"
|
||||||
mime = "0.3"
|
mime = "0.3"
|
||||||
|
@ -73,24 +78,30 @@ rand = "0.8"
|
||||||
serde = "1.0"
|
serde = "1.0"
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
serde_urlencoded = "0.7"
|
serde_urlencoded = "0.7"
|
||||||
tls-openssl = { version = "0.10.9", package = "openssl", optional = true }
|
tokio = { version = "1", features = ["sync"] }
|
||||||
tls-rustls = { version = "0.19.0", package = "rustls", optional = true, features = ["dangerous_configuration"] }
|
|
||||||
|
cookie = { version = "0.15", features = ["percent-encode"], optional = true }
|
||||||
|
|
||||||
|
tls-openssl = { package = "openssl", version = "0.10.9", optional = true }
|
||||||
|
tls-rustls = { package = "rustls", version = "0.20.0", optional = true, features = ["dangerous_configuration"] }
|
||||||
|
|
||||||
|
trust-dns-resolver = { version = "0.20.0", optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
actix-web = { version = "4.0.0-beta.9", features = ["openssl"] }
|
actix-web = { version = "4.0.0-beta.10", features = ["openssl"] }
|
||||||
actix-http = { version = "3.0.0-beta.10", features = ["openssl"] }
|
actix-http = { version = "3.0.0-beta.11", features = ["openssl"] }
|
||||||
actix-http-test = { version = "3.0.0-beta.5", features = ["openssl"] }
|
actix-http-test = { version = "3.0.0-beta.5", features = ["openssl"] }
|
||||||
actix-utils = "3.0.0"
|
actix-utils = "3.0.0"
|
||||||
actix-server = "2.0.0-beta.3"
|
actix-server = "2.0.0-beta.3"
|
||||||
actix-tls = { version = "3.0.0-beta.5", features = ["openssl", "rustls"] }
|
actix-tls = { version = "3.0.0-beta.7", features = ["openssl", "rustls"] }
|
||||||
actix-test = { version = "0.1.0-beta.3", features = ["openssl", "rustls"] }
|
actix-test = { version = "0.1.0-beta.5", features = ["openssl", "rustls"] }
|
||||||
|
|
||||||
brotli2 = "0.3.2"
|
brotli2 = "0.3.2"
|
||||||
env_logger = "0.8"
|
env_logger = "0.8"
|
||||||
flate2 = "1.0.13"
|
flate2 = "1.0.13"
|
||||||
futures-util = { version = "0.3.7", default-features = false }
|
futures-util = { version = "0.3.7", default-features = false }
|
||||||
rcgen = "0.8"
|
rcgen = "0.8"
|
||||||
webpki = "0.21"
|
rustls-pemfile = "0.2"
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "client"
|
name = "client"
|
||||||
|
|
|
@ -3,16 +3,16 @@
|
||||||
> Async HTTP and WebSocket client library.
|
> Async HTTP and WebSocket client library.
|
||||||
|
|
||||||
[](https://crates.io/crates/awc)
|
[](https://crates.io/crates/awc)
|
||||||
[](https://docs.rs/awc/3.0.0-beta.8)
|
[](https://docs.rs/awc/3.0.0-beta.9)
|
||||||

|

|
||||||
[](https://deps.rs/crate/awc/3.0.0-beta.8)
|
[](https://deps.rs/crate/awc/3.0.0-beta.9)
|
||||||
[](https://discord.gg/NWpN5mmg3x)
|
[](https://discord.gg/NWpN5mmg3x)
|
||||||
|
|
||||||
## Documentation & Resources
|
## Documentation & Resources
|
||||||
|
|
||||||
- [API Documentation](https://docs.rs/awc)
|
- [API Documentation](https://docs.rs/awc)
|
||||||
- [Example Project](https://github.com/actix/examples/tree/HEAD/security/awc_https)
|
- [Example Project](https://github.com/actix/examples/tree/HEAD/security/awc_https)
|
||||||
- Minimum Supported Rust Version (MSRV): 1.51.0
|
- Minimum Supported Rust Version (MSRV): 1.52
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
|
|
||||||
|
|
|
@ -4,13 +4,11 @@ use std::net::IpAddr;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use actix_http::{
|
use actix_http::http::{self, header, Error as HttpError, HeaderMap, HeaderName, Uri};
|
||||||
client::{Connector, ConnectorService, TcpConnect, TcpConnectError, TcpConnection},
|
|
||||||
http::{self, header, Error as HttpError, HeaderMap, HeaderName, Uri},
|
|
||||||
};
|
|
||||||
use actix_rt::net::{ActixStream, TcpStream};
|
use actix_rt::net::{ActixStream, TcpStream};
|
||||||
use actix_service::{boxed, Service};
|
use actix_service::{boxed, Service};
|
||||||
|
|
||||||
|
use crate::client::{Connector, ConnectorService, TcpConnect, TcpConnectError, TcpConnection};
|
||||||
use crate::connect::DefaultConnector;
|
use crate::connect::DefaultConnector;
|
||||||
use crate::error::SendRequestError;
|
use crate::error::SendRequestError;
|
||||||
use crate::middleware::{NestTransform, Redirect, Transform};
|
use crate::middleware::{NestTransform, Redirect, Transform};
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use std::net::IpAddr;
|
use std::{net::IpAddr, time::Duration};
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
const DEFAULT_H2_CONN_WINDOW: u32 = 1024 * 1024 * 2; // 2MB
|
const DEFAULT_H2_CONN_WINDOW: u32 = 1024 * 1024 * 2; // 2MB
|
||||||
const DEFAULT_H2_STREAM_WINDOW: u32 = 1024 * 1024; // 1MB
|
const DEFAULT_H2_STREAM_WINDOW: u32 = 1024 * 1024; // 1MB
|
|
@ -12,10 +12,9 @@ use bytes::Bytes;
|
||||||
use futures_core::future::LocalBoxFuture;
|
use futures_core::future::LocalBoxFuture;
|
||||||
use h2::client::SendRequest;
|
use h2::client::SendRequest;
|
||||||
|
|
||||||
use crate::h1::ClientCodec;
|
use actix_http::{
|
||||||
use crate::message::{RequestHeadType, ResponseHead};
|
body::MessageBody, h1::ClientCodec, Error, Payload, RequestHeadType, ResponseHead,
|
||||||
use crate::payload::Payload;
|
};
|
||||||
use crate::{body::MessageBody, Error};
|
|
||||||
|
|
||||||
use super::error::SendRequestError;
|
use super::error::SendRequestError;
|
||||||
use super::pool::Acquired;
|
use super::pool::Acquired;
|
||||||
|
@ -219,11 +218,7 @@ impl<Io: ConnectionIo> ConnectionType<Io> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn from_h1(
|
pub(super) fn from_h1(io: Io, created: time::Instant, acquired: Acquired<Io>) -> Self {
|
||||||
io: Io,
|
|
||||||
created: time::Instant,
|
|
||||||
acquired: Acquired<Io>,
|
|
||||||
) -> Self {
|
|
||||||
Self::H1(H1Connection {
|
Self::H1(H1Connection {
|
||||||
io: Some(io),
|
io: Some(io),
|
||||||
created,
|
created,
|
||||||
|
@ -271,9 +266,7 @@ where
|
||||||
Connection::Tls(ConnectionType::H2(conn)) => {
|
Connection::Tls(ConnectionType::H2(conn)) => {
|
||||||
h2proto::send_request(conn, head.into(), body).await
|
h2proto::send_request(conn, head.into(), body).await
|
||||||
}
|
}
|
||||||
_ => unreachable!(
|
_ => unreachable!("Plain Tcp connection can be used only in Http1 protocol"),
|
||||||
"Plain Tcp connection can be used only in Http1 protocol"
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -301,9 +294,7 @@ where
|
||||||
Err(SendRequestError::TunnelNotSupported)
|
Err(SendRequestError::TunnelNotSupported)
|
||||||
}
|
}
|
||||||
Connection::Tcp(ConnectionType::H2(_)) => {
|
Connection::Tcp(ConnectionType::H2(_)) => {
|
||||||
unreachable!(
|
unreachable!("Plain Tcp connection can be used only in Http1 protocol")
|
||||||
"Plain Tcp connection can be used only in Http1 protocol"
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -321,12 +312,8 @@ where
|
||||||
buf: &mut ReadBuf<'_>,
|
buf: &mut ReadBuf<'_>,
|
||||||
) -> Poll<io::Result<()>> {
|
) -> Poll<io::Result<()>> {
|
||||||
match self.get_mut() {
|
match self.get_mut() {
|
||||||
Connection::Tcp(ConnectionType::H1(conn)) => {
|
Connection::Tcp(ConnectionType::H1(conn)) => Pin::new(conn).poll_read(cx, buf),
|
||||||
Pin::new(conn).poll_read(cx, buf)
|
Connection::Tls(ConnectionType::H1(conn)) => Pin::new(conn).poll_read(cx, buf),
|
||||||
}
|
|
||||||
Connection::Tls(ConnectionType::H1(conn)) => {
|
|
||||||
Pin::new(conn).poll_read(cx, buf)
|
|
||||||
}
|
|
||||||
_ => unreachable!("H2Connection can not impl AsyncRead trait"),
|
_ => unreachable!("H2Connection can not impl AsyncRead trait"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -345,12 +332,8 @@ where
|
||||||
buf: &[u8],
|
buf: &[u8],
|
||||||
) -> Poll<io::Result<usize>> {
|
) -> Poll<io::Result<usize>> {
|
||||||
match self.get_mut() {
|
match self.get_mut() {
|
||||||
Connection::Tcp(ConnectionType::H1(conn)) => {
|
Connection::Tcp(ConnectionType::H1(conn)) => Pin::new(conn).poll_write(cx, buf),
|
||||||
Pin::new(conn).poll_write(cx, buf)
|
Connection::Tls(ConnectionType::H1(conn)) => Pin::new(conn).poll_write(cx, buf),
|
||||||
}
|
|
||||||
Connection::Tls(ConnectionType::H1(conn)) => {
|
|
||||||
Pin::new(conn).poll_write(cx, buf)
|
|
||||||
}
|
|
||||||
_ => unreachable!(H2_UNREACHABLE_WRITE),
|
_ => unreachable!(H2_UNREACHABLE_WRITE),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -363,17 +346,10 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn poll_shutdown(
|
fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||||
self: Pin<&mut Self>,
|
|
||||||
cx: &mut Context<'_>,
|
|
||||||
) -> Poll<io::Result<()>> {
|
|
||||||
match self.get_mut() {
|
match self.get_mut() {
|
||||||
Connection::Tcp(ConnectionType::H1(conn)) => {
|
Connection::Tcp(ConnectionType::H1(conn)) => Pin::new(conn).poll_shutdown(cx),
|
||||||
Pin::new(conn).poll_shutdown(cx)
|
Connection::Tls(ConnectionType::H1(conn)) => Pin::new(conn).poll_shutdown(cx),
|
||||||
}
|
|
||||||
Connection::Tls(ConnectionType::H1(conn)) => {
|
|
||||||
Pin::new(conn).poll_shutdown(cx)
|
|
||||||
}
|
|
||||||
_ => unreachable!(H2_UNREACHABLE_WRITE),
|
_ => unreachable!(H2_UNREACHABLE_WRITE),
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -8,6 +8,7 @@ use std::{
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use actix_http::Protocol;
|
||||||
use actix_rt::{
|
use actix_rt::{
|
||||||
net::{ActixStream, TcpStream},
|
net::{ActixStream, TcpStream},
|
||||||
time::{sleep, Sleep},
|
time::{sleep, Sleep},
|
||||||
|
@ -19,27 +20,21 @@ use actix_tls::connect::{
|
||||||
};
|
};
|
||||||
use futures_core::{future::LocalBoxFuture, ready};
|
use futures_core::{future::LocalBoxFuture, ready};
|
||||||
use http::Uri;
|
use http::Uri;
|
||||||
use pin_project::pin_project;
|
use pin_project_lite::pin_project;
|
||||||
|
|
||||||
use super::config::ConnectorConfig;
|
use super::config::ConnectorConfig;
|
||||||
use super::connection::{Connection, ConnectionIo};
|
use super::connection::{Connection, ConnectionIo};
|
||||||
use super::error::ConnectError;
|
use super::error::ConnectError;
|
||||||
use super::pool::ConnectionPool;
|
use super::pool::ConnectionPool;
|
||||||
use super::Connect;
|
use super::Connect;
|
||||||
use super::Protocol;
|
|
||||||
|
|
||||||
#[cfg(feature = "openssl")]
|
|
||||||
use actix_tls::connect::ssl::openssl::SslConnector as OpensslConnector;
|
|
||||||
#[cfg(feature = "rustls")]
|
|
||||||
use actix_tls::connect::ssl::rustls::ClientConfig;
|
|
||||||
|
|
||||||
enum SslConnector {
|
enum SslConnector {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
None,
|
None,
|
||||||
#[cfg(feature = "openssl")]
|
#[cfg(feature = "openssl")]
|
||||||
Openssl(OpensslConnector),
|
Openssl(actix_tls::connect::ssl::openssl::SslConnector),
|
||||||
#[cfg(feature = "rustls")]
|
#[cfg(feature = "rustls")]
|
||||||
Rustls(std::sync::Arc<ClientConfig>),
|
Rustls(std::sync::Arc<actix_tls::connect::ssl::rustls::ClientConfig>),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Manages HTTP client network connectivity.
|
/// Manages HTTP client network connectivity.
|
||||||
|
@ -78,10 +73,33 @@ impl Connector<()> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build Ssl connector with openssl, based on supplied alpn protocols
|
/// Provides an empty TLS connector when no TLS feature is enabled.
|
||||||
#[cfg(feature = "openssl")]
|
#[cfg(not(any(feature = "openssl", feature = "rustls")))]
|
||||||
|
fn build_ssl(_: Vec<Vec<u8>>) -> SslConnector {
|
||||||
|
SslConnector::None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Build TLS connector with rustls, based on supplied ALPN protocols
|
||||||
|
///
|
||||||
|
/// Note that if both `openssl` and `rustls` features are enabled, rustls will be used.
|
||||||
|
#[cfg(feature = "rustls")]
|
||||||
fn build_ssl(protocols: Vec<Vec<u8>>) -> SslConnector {
|
fn build_ssl(protocols: Vec<Vec<u8>>) -> SslConnector {
|
||||||
use actix_tls::connect::ssl::openssl::SslMethod;
|
use actix_tls::connect::tls::rustls::{webpki_roots_cert_store, ClientConfig};
|
||||||
|
|
||||||
|
let mut config = ClientConfig::builder()
|
||||||
|
.with_safe_defaults()
|
||||||
|
.with_root_certificates(webpki_roots_cert_store())
|
||||||
|
.with_no_client_auth();
|
||||||
|
|
||||||
|
config.alpn_protocols = protocols;
|
||||||
|
|
||||||
|
SslConnector::Rustls(std::sync::Arc::new(config))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Build TLS connector with openssl, based on supplied ALPN protocols
|
||||||
|
#[cfg(all(feature = "openssl", not(feature = "rustls")))]
|
||||||
|
fn build_ssl(protocols: Vec<Vec<u8>>) -> SslConnector {
|
||||||
|
use actix_tls::connect::tls::openssl::{SslConnector as OpensslConnector, SslMethod};
|
||||||
use bytes::{BufMut, BytesMut};
|
use bytes::{BufMut, BytesMut};
|
||||||
|
|
||||||
let mut alpn = BytesMut::with_capacity(20);
|
let mut alpn = BytesMut::with_capacity(20);
|
||||||
|
@ -91,28 +109,12 @@ impl Connector<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut ssl = OpensslConnector::builder(SslMethod::tls()).unwrap();
|
let mut ssl = OpensslConnector::builder(SslMethod::tls()).unwrap();
|
||||||
let _ = ssl
|
if let Err(err) = ssl.set_alpn_protos(&alpn) {
|
||||||
.set_alpn_protos(&alpn)
|
log::error!("Can not set ALPN protocol: {:?}", err);
|
||||||
.map_err(|e| error!("Can not set alpn protocol: {:?}", e));
|
}
|
||||||
|
|
||||||
SslConnector::Openssl(ssl.build())
|
SslConnector::Openssl(ssl.build())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build Ssl connector with rustls, based on supplied alpn protocols
|
|
||||||
#[cfg(all(not(feature = "openssl"), feature = "rustls"))]
|
|
||||||
fn build_ssl(protocols: Vec<Vec<u8>>) -> SslConnector {
|
|
||||||
let mut config = ClientConfig::new();
|
|
||||||
config.set_protocols(&protocols);
|
|
||||||
config.root_store.add_server_trust_anchors(
|
|
||||||
&actix_tls::connect::ssl::rustls::TLS_SERVER_ROOTS,
|
|
||||||
);
|
|
||||||
SslConnector::Rustls(std::sync::Arc::new(config))
|
|
||||||
}
|
|
||||||
|
|
||||||
// ssl turned off, provides empty ssl connector
|
|
||||||
#[cfg(not(any(feature = "openssl", feature = "rustls")))]
|
|
||||||
fn build_ssl(_: Vec<Vec<u8>>) -> SslConnector {
|
|
||||||
SslConnector::None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S> Connector<S> {
|
impl<S> Connector<S> {
|
||||||
|
@ -144,11 +146,8 @@ where
|
||||||
// This remap is to hide ActixStream's trait methods. They are not meant to be called
|
// This remap is to hide ActixStream's trait methods. They are not meant to be called
|
||||||
// from user code.
|
// from user code.
|
||||||
Io: ActixStream + fmt::Debug + 'static,
|
Io: ActixStream + fmt::Debug + 'static,
|
||||||
S: Service<
|
S: Service<TcpConnect<Uri>, Response = TcpConnection<Uri, Io>, Error = TcpConnectError>
|
||||||
TcpConnect<Uri>,
|
+ Clone
|
||||||
Response = TcpConnection<Uri, Io>,
|
|
||||||
Error = TcpConnectError,
|
|
||||||
> + Clone
|
|
||||||
+ 'static,
|
+ 'static,
|
||||||
{
|
{
|
||||||
/// Tcp connection timeout, i.e. max time to connect to remote host including dns name
|
/// Tcp connection timeout, i.e. max time to connect to remote host including dns name
|
||||||
|
@ -167,14 +166,17 @@ where
|
||||||
|
|
||||||
#[cfg(feature = "openssl")]
|
#[cfg(feature = "openssl")]
|
||||||
/// Use custom `SslConnector` instance.
|
/// Use custom `SslConnector` instance.
|
||||||
pub fn ssl(mut self, connector: OpensslConnector) -> Self {
|
pub fn ssl(mut self, connector: actix_tls::connect::ssl::openssl::SslConnector) -> Self {
|
||||||
self.ssl = SslConnector::Openssl(connector);
|
self.ssl = SslConnector::Openssl(connector);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "rustls")]
|
#[cfg(feature = "rustls")]
|
||||||
/// Use custom `SslConnector` instance.
|
/// Use custom `SslConnector` instance.
|
||||||
pub fn rustls(mut self, connector: std::sync::Arc<ClientConfig>) -> Self {
|
pub fn rustls(
|
||||||
|
mut self,
|
||||||
|
connector: std::sync::Arc<actix_tls::connect::ssl::rustls::ClientConfig>,
|
||||||
|
) -> Self {
|
||||||
self.ssl = SslConnector::Rustls(connector);
|
self.ssl = SslConnector::Rustls(connector);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
@ -313,9 +315,7 @@ where
|
||||||
SslConnector::Rustls(tls) => {
|
SslConnector::Rustls(tls) => {
|
||||||
const H2: &[u8] = b"h2";
|
const H2: &[u8] = b"h2";
|
||||||
|
|
||||||
use actix_tls::connect::ssl::rustls::{
|
use actix_tls::connect::ssl::rustls::{RustlsConnector, TlsStream};
|
||||||
RustlsConnector, Session, TlsStream,
|
|
||||||
};
|
|
||||||
|
|
||||||
impl<Io: ConnectionIo> IntoConnectionIo for TcpConnection<Uri, TlsStream<Io>> {
|
impl<Io: ConnectionIo> IntoConnectionIo for TcpConnection<Uri, TlsStream<Io>> {
|
||||||
fn into_connection_io(self) -> (Box<dyn ConnectionIo>, Protocol) {
|
fn into_connection_io(self) -> (Box<dyn ConnectionIo>, Protocol) {
|
||||||
|
@ -323,7 +323,7 @@ where
|
||||||
let h2 = sock
|
let h2 = sock
|
||||||
.get_ref()
|
.get_ref()
|
||||||
.1
|
.1
|
||||||
.get_alpn_protocol()
|
.alpn_protocol()
|
||||||
.map_or(false, |protos| protos.windows(2).any(|w| w == H2));
|
.map_or(false, |protos| protos.windows(2).any(|w| w == H2));
|
||||||
if h2 {
|
if h2 {
|
||||||
(Box::new(sock), Protocol::Http2)
|
(Box::new(sock), Protocol::Http2)
|
||||||
|
@ -350,8 +350,8 @@ where
|
||||||
let tcp_pool = ConnectionPool::new(tcp_service, tcp_config);
|
let tcp_pool = ConnectionPool::new(tcp_service, tcp_config);
|
||||||
|
|
||||||
let tls_config = self.config;
|
let tls_config = self.config;
|
||||||
let tls_pool = tls_service
|
let tls_pool =
|
||||||
.map(move |tls_service| ConnectionPool::new(tls_service, tls_config));
|
tls_service.map(move |tls_service| ConnectionPool::new(tls_service, tls_config));
|
||||||
|
|
||||||
ConnectorServicePriv { tcp_pool, tls_pool }
|
ConnectorServicePriv { tcp_pool, tls_pool }
|
||||||
}
|
}
|
||||||
|
@ -382,10 +382,12 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pin_project]
|
pin_project! {
|
||||||
pub struct TcpConnectorFuture<Fut> {
|
#[project = TcpConnectorFutureProj]
|
||||||
#[pin]
|
pub struct TcpConnectorFuture<Fut> {
|
||||||
fut: Fut,
|
#[pin]
|
||||||
|
fut: Fut,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Fut, Io> Future for TcpConnectorFuture<Fut>
|
impl<Fut, Io> Future for TcpConnectorFuture<Fut>
|
||||||
|
@ -444,23 +446,25 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pin_project(project = TlsConnectorProj)]
|
pin_project! {
|
||||||
#[allow(clippy::large_enum_variant)]
|
#[project = TlsConnectorProj]
|
||||||
enum TlsConnectorFuture<S, Fut1, Fut2> {
|
#[allow(clippy::large_enum_variant)]
|
||||||
TcpConnect {
|
enum TlsConnectorFuture<S, Fut1, Fut2> {
|
||||||
#[pin]
|
TcpConnect {
|
||||||
fut: Fut1,
|
#[pin]
|
||||||
tls_service: Option<S>,
|
fut: Fut1,
|
||||||
timeout: Duration,
|
tls_service: Option<S>,
|
||||||
},
|
timeout: Duration,
|
||||||
TlsConnect {
|
},
|
||||||
#[pin]
|
TlsConnect {
|
||||||
fut: Fut2,
|
#[pin]
|
||||||
#[pin]
|
fut: Fut2,
|
||||||
timeout: Sleep,
|
#[pin]
|
||||||
},
|
timeout: Sleep,
|
||||||
}
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
/// helper trait for generic over different TlsStream types between tls crates.
|
/// helper trait for generic over different TlsStream types between tls crates.
|
||||||
trait IntoConnectionIo {
|
trait IntoConnectionIo {
|
||||||
fn into_connection_io(self) -> (Box<dyn ConnectionIo>, Protocol);
|
fn into_connection_io(self) -> (Box<dyn ConnectionIo>, Protocol);
|
||||||
|
@ -468,12 +472,7 @@ trait IntoConnectionIo {
|
||||||
|
|
||||||
impl<S, Io, Fut1, Fut2, Res> Future for TlsConnectorFuture<S, Fut1, Fut2>
|
impl<S, Io, Fut1, Fut2, Res> Future for TlsConnectorFuture<S, Fut1, Fut2>
|
||||||
where
|
where
|
||||||
S: Service<
|
S: Service<TcpConnection<Uri, Io>, Response = Res, Error = std::io::Error, Future = Fut2>,
|
||||||
TcpConnection<Uri, Io>,
|
|
||||||
Response = Res,
|
|
||||||
Error = std::io::Error,
|
|
||||||
Future = Fut2,
|
|
||||||
>,
|
|
||||||
S::Response: IntoConnectionIo,
|
S::Response: IntoConnectionIo,
|
||||||
Fut1: Future<Output = Result<TcpConnection<Uri, Io>, ConnectError>>,
|
Fut1: Future<Output = Result<TcpConnection<Uri, Io>, ConnectError>>,
|
||||||
Fut2: Future<Output = Result<S::Response, S::Error>>,
|
Fut2: Future<Output = Result<S::Response, S::Error>>,
|
||||||
|
@ -515,11 +514,7 @@ pub struct TcpConnectorInnerService<S: Clone> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: Clone> TcpConnectorInnerService<S> {
|
impl<S: Clone> TcpConnectorInnerService<S> {
|
||||||
fn new(
|
fn new(service: S, timeout: Duration, local_address: Option<std::net::IpAddr>) -> Self {
|
||||||
service: S,
|
|
||||||
timeout: Duration,
|
|
||||||
local_address: Option<std::net::IpAddr>,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
service,
|
service,
|
||||||
timeout,
|
timeout,
|
||||||
|
@ -530,11 +525,8 @@ impl<S: Clone> TcpConnectorInnerService<S> {
|
||||||
|
|
||||||
impl<S, Io> Service<Connect> for TcpConnectorInnerService<S>
|
impl<S, Io> Service<Connect> for TcpConnectorInnerService<S>
|
||||||
where
|
where
|
||||||
S: Service<
|
S: Service<TcpConnect<Uri>, Response = TcpConnection<Uri, Io>, Error = TcpConnectError>
|
||||||
TcpConnect<Uri>,
|
+ Clone
|
||||||
Response = TcpConnection<Uri, Io>,
|
|
||||||
Error = TcpConnectError,
|
|
||||||
> + Clone
|
|
||||||
+ 'static,
|
+ 'static,
|
||||||
{
|
{
|
||||||
type Response = S::Response;
|
type Response = S::Response;
|
||||||
|
@ -557,12 +549,14 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pin_project]
|
pin_project! {
|
||||||
pub struct TcpConnectorInnerFuture<Fut> {
|
#[project = TcpConnectorInnerFutureProj]
|
||||||
#[pin]
|
pub struct TcpConnectorInnerFuture<Fut> {
|
||||||
fut: Fut,
|
#[pin]
|
||||||
#[pin]
|
fut: Fut,
|
||||||
timeout: Sleep,
|
#[pin]
|
||||||
|
timeout: Sleep,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Fut, Io> Future for TcpConnectorInnerFuture<Fut>
|
impl<Fut, Io> Future for TcpConnectorInnerFuture<Fut>
|
||||||
|
@ -611,12 +605,8 @@ where
|
||||||
|
|
||||||
impl<S1, S2, Io1, Io2> Service<Connect> for ConnectorServicePriv<S1, S2, Io1, Io2>
|
impl<S1, S2, Io1, Io2> Service<Connect> for ConnectorServicePriv<S1, S2, Io1, Io2>
|
||||||
where
|
where
|
||||||
S1: Service<Connect, Response = (Io1, Protocol), Error = ConnectError>
|
S1: Service<Connect, Response = (Io1, Protocol), Error = ConnectError> + Clone + 'static,
|
||||||
+ Clone
|
S2: Service<Connect, Response = (Io2, Protocol), Error = ConnectError> + Clone + 'static,
|
||||||
+ 'static,
|
|
||||||
S2: Service<Connect, Response = (Io2, Protocol), Error = ConnectError>
|
|
||||||
+ Clone
|
|
||||||
+ 'static,
|
|
||||||
Io1: ConnectionIo,
|
Io1: ConnectionIo,
|
||||||
Io2: ConnectionIo,
|
Io2: ConnectionIo,
|
||||||
{
|
{
|
||||||
|
@ -636,38 +626,46 @@ where
|
||||||
match req.uri.scheme_str() {
|
match req.uri.scheme_str() {
|
||||||
Some("https") | Some("wss") => match self.tls_pool {
|
Some("https") | Some("wss") => match self.tls_pool {
|
||||||
None => ConnectorServiceFuture::SslIsNotSupported,
|
None => ConnectorServiceFuture::SslIsNotSupported,
|
||||||
Some(ref pool) => ConnectorServiceFuture::Tls(pool.call(req)),
|
Some(ref pool) => ConnectorServiceFuture::Tls {
|
||||||
|
fut: pool.call(req),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
_ => ConnectorServiceFuture::Tcp {
|
||||||
|
fut: self.tcp_pool.call(req),
|
||||||
},
|
},
|
||||||
_ => ConnectorServiceFuture::Tcp(self.tcp_pool.call(req)),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pin_project(project = ConnectorServiceProj)]
|
pin_project! {
|
||||||
pub enum ConnectorServiceFuture<S1, S2, Io1, Io2>
|
#[project = ConnectorServiceFutureProj]
|
||||||
where
|
pub enum ConnectorServiceFuture<S1, S2, Io1, Io2>
|
||||||
S1: Service<Connect, Response = (Io1, Protocol), Error = ConnectError>
|
where
|
||||||
+ Clone
|
S1: Service<Connect, Response = (Io1, Protocol), Error = ConnectError>,
|
||||||
+ 'static,
|
S1: Clone,
|
||||||
S2: Service<Connect, Response = (Io2, Protocol), Error = ConnectError>
|
S1: 'static,
|
||||||
+ Clone
|
S2: Service<Connect, Response = (Io2, Protocol), Error = ConnectError>,
|
||||||
+ 'static,
|
S2: Clone,
|
||||||
Io1: ConnectionIo,
|
S2: 'static,
|
||||||
Io2: ConnectionIo,
|
Io1: ConnectionIo,
|
||||||
{
|
Io2: ConnectionIo,
|
||||||
Tcp(#[pin] <ConnectionPool<S1, Io1> as Service<Connect>>::Future),
|
{
|
||||||
Tls(#[pin] <ConnectionPool<S2, Io2> as Service<Connect>>::Future),
|
Tcp {
|
||||||
SslIsNotSupported,
|
#[pin]
|
||||||
|
fut: <ConnectionPool<S1, Io1> as Service<Connect>>::Future
|
||||||
|
},
|
||||||
|
Tls {
|
||||||
|
#[pin]
|
||||||
|
fut: <ConnectionPool<S2, Io2> as Service<Connect>>::Future
|
||||||
|
},
|
||||||
|
SslIsNotSupported
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S1, S2, Io1, Io2> Future for ConnectorServiceFuture<S1, S2, Io1, Io2>
|
impl<S1, S2, Io1, Io2> Future for ConnectorServiceFuture<S1, S2, Io1, Io2>
|
||||||
where
|
where
|
||||||
S1: Service<Connect, Response = (Io1, Protocol), Error = ConnectError>
|
S1: Service<Connect, Response = (Io1, Protocol), Error = ConnectError> + Clone + 'static,
|
||||||
+ Clone
|
S2: Service<Connect, Response = (Io2, Protocol), Error = ConnectError> + Clone + 'static,
|
||||||
+ 'static,
|
|
||||||
S2: Service<Connect, Response = (Io2, Protocol), Error = ConnectError>
|
|
||||||
+ Clone
|
|
||||||
+ 'static,
|
|
||||||
Io1: ConnectionIo,
|
Io1: ConnectionIo,
|
||||||
Io2: ConnectionIo,
|
Io2: ConnectionIo,
|
||||||
{
|
{
|
||||||
|
@ -675,9 +673,9 @@ where
|
||||||
|
|
||||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
match self.project() {
|
match self.project() {
|
||||||
ConnectorServiceProj::Tcp(fut) => fut.poll(cx).map_ok(Connection::Tcp),
|
ConnectorServiceFutureProj::Tcp { fut } => fut.poll(cx).map_ok(Connection::Tcp),
|
||||||
ConnectorServiceProj::Tls(fut) => fut.poll(cx).map_ok(Connection::Tls),
|
ConnectorServiceFutureProj::Tls { fut } => fut.poll(cx).map_ok(Connection::Tls),
|
||||||
ConnectorServiceProj::SslIsNotSupported => {
|
ConnectorServiceFutureProj::SslIsNotSupported => {
|
||||||
Poll::Ready(Err(ConnectError::SslIsNotSupported))
|
Poll::Ready(Err(ConnectError::SslIsNotSupported))
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,12 +2,13 @@ use std::{error::Error as StdError, fmt, io};
|
||||||
|
|
||||||
use derive_more::{Display, From};
|
use derive_more::{Display, From};
|
||||||
|
|
||||||
|
use actix_http::{
|
||||||
|
error::{Error, ParseError},
|
||||||
|
http::Error as HttpError,
|
||||||
|
};
|
||||||
#[cfg(feature = "openssl")]
|
#[cfg(feature = "openssl")]
|
||||||
use actix_tls::accept::openssl::SslError;
|
use actix_tls::accept::openssl::SslError;
|
||||||
|
|
||||||
use crate::error::{Error, ParseError};
|
|
||||||
use crate::http::Error as HttpError;
|
|
||||||
|
|
||||||
/// A set of errors that can occur while connecting to an HTTP host
|
/// A set of errors that can occur while connecting to an HTTP host
|
||||||
#[derive(Debug, Display, From)]
|
#[derive(Debug, Display, From)]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
|
@ -5,24 +5,25 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use actix_codec::Framed;
|
use actix_codec::Framed;
|
||||||
|
use actix_http::{
|
||||||
|
body::{BodySize, MessageBody},
|
||||||
|
error::PayloadError,
|
||||||
|
h1,
|
||||||
|
http::{
|
||||||
|
header::{HeaderMap, IntoHeaderValue, EXPECT, HOST},
|
||||||
|
StatusCode,
|
||||||
|
},
|
||||||
|
Error, Payload, RequestHeadType, ResponseHead,
|
||||||
|
};
|
||||||
use actix_utils::future::poll_fn;
|
use actix_utils::future::poll_fn;
|
||||||
use bytes::buf::BufMut;
|
use bytes::buf::BufMut;
|
||||||
use bytes::{Bytes, BytesMut};
|
use bytes::{Bytes, BytesMut};
|
||||||
use futures_core::{ready, Stream};
|
use futures_core::{ready, Stream};
|
||||||
use futures_util::SinkExt as _;
|
use futures_util::SinkExt as _;
|
||||||
|
use pin_project_lite::pin_project;
|
||||||
use crate::h1;
|
|
||||||
use crate::http::{
|
|
||||||
header::{HeaderMap, IntoHeaderValue, EXPECT, HOST},
|
|
||||||
StatusCode,
|
|
||||||
};
|
|
||||||
use crate::message::{RequestHeadType, ResponseHead};
|
|
||||||
use crate::payload::Payload;
|
|
||||||
use crate::{error::PayloadError, Error};
|
|
||||||
|
|
||||||
use super::connection::{ConnectionIo, H1Connection};
|
use super::connection::{ConnectionIo, H1Connection};
|
||||||
use super::error::{ConnectError, SendRequestError};
|
use super::error::{ConnectError, SendRequestError};
|
||||||
use crate::body::{BodySize, MessageBody};
|
|
||||||
|
|
||||||
pub(crate) async fn send_request<Io, B>(
|
pub(crate) async fn send_request<Io, B>(
|
||||||
io: H1Connection<Io>,
|
io: H1Connection<Io>,
|
||||||
|
@ -194,10 +195,11 @@ where
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pin_project::pin_project]
|
pin_project! {
|
||||||
pub(crate) struct PlStream<Io: ConnectionIo> {
|
pub(crate) struct PlStream<Io: ConnectionIo> {
|
||||||
#[pin]
|
#[pin]
|
||||||
framed: Framed<H1Connection<Io>, h1::ClientPayloadCodec>,
|
framed: Framed<H1Connection<Io>, h1::ClientPayloadCodec>,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Io: ConnectionIo> PlStream<Io> {
|
impl<Io: ConnectionIo> PlStream<Io> {
|
||||||
|
@ -211,10 +213,7 @@ impl<Io: ConnectionIo> PlStream<Io> {
|
||||||
impl<Io: ConnectionIo> Stream for PlStream<Io> {
|
impl<Io: ConnectionIo> Stream for PlStream<Io> {
|
||||||
type Item = Result<Bytes, PayloadError>;
|
type Item = Result<Bytes, PayloadError>;
|
||||||
|
|
||||||
fn poll_next(
|
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||||
self: Pin<&mut Self>,
|
|
||||||
cx: &mut Context<'_>,
|
|
||||||
) -> Poll<Option<Self::Item>> {
|
|
||||||
let mut this = self.project();
|
let mut this = self.project();
|
||||||
|
|
||||||
match ready!(this.framed.as_mut().next_item(cx)?) {
|
match ready!(this.framed.as_mut().next_item(cx)?) {
|
|
@ -8,13 +8,12 @@ use h2::{
|
||||||
};
|
};
|
||||||
use http::header::{HeaderValue, CONNECTION, CONTENT_LENGTH, TRANSFER_ENCODING};
|
use http::header::{HeaderValue, CONNECTION, CONTENT_LENGTH, TRANSFER_ENCODING};
|
||||||
use http::{request::Request, Method, Version};
|
use http::{request::Request, Method, Version};
|
||||||
|
use log::trace;
|
||||||
|
|
||||||
use crate::{
|
use actix_http::{
|
||||||
body::{BodySize, MessageBody},
|
body::{BodySize, MessageBody},
|
||||||
header::HeaderMap,
|
header::HeaderMap,
|
||||||
message::{RequestHeadType, ResponseHead},
|
Error, Payload, RequestHeadType, ResponseHead,
|
||||||
payload::Payload,
|
|
||||||
Error,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
|
@ -131,10 +130,7 @@ where
|
||||||
Ok((head, payload))
|
Ok((head, payload))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn send_body<B>(
|
async fn send_body<B>(body: B, mut send: SendStream<Bytes>) -> Result<(), SendRequestError>
|
||||||
body: B,
|
|
||||||
mut send: SendStream<Bytes>,
|
|
||||||
) -> Result<(), SendRequestError>
|
|
||||||
where
|
where
|
||||||
B: MessageBody,
|
B: MessageBody,
|
||||||
B::Error: Into<Error>,
|
B::Error: Into<Error>,
|
||||||
|
@ -184,8 +180,7 @@ where
|
||||||
pub(crate) fn handshake<Io: ConnectionIo>(
|
pub(crate) fn handshake<Io: ConnectionIo>(
|
||||||
io: Io,
|
io: Io,
|
||||||
config: &ConnectorConfig,
|
config: &ConnectorConfig,
|
||||||
) -> impl Future<Output = Result<(SendRequest<Bytes>, Connection<Io, Bytes>), h2::Error>>
|
) -> impl Future<Output = Result<(SendRequest<Bytes>, Connection<Io, Bytes>), h2::Error>> {
|
||||||
{
|
|
||||||
let mut builder = Builder::new();
|
let mut builder = Builder::new();
|
||||||
builder
|
builder
|
||||||
.initial_window_size(config.stream_window_size)
|
.initial_window_size(config.stream_window_size)
|
|
@ -17,7 +17,6 @@ pub use actix_tls::connect::{
|
||||||
pub use self::connection::{Connection, ConnectionIo};
|
pub use self::connection::{Connection, ConnectionIo};
|
||||||
pub use self::connector::{Connector, ConnectorService};
|
pub use self::connector::{Connector, ConnectorService};
|
||||||
pub use self::error::{ConnectError, FreezeRequestError, InvalidUrl, SendRequestError};
|
pub use self::error::{ConnectError, FreezeRequestError, InvalidUrl, SendRequestError};
|
||||||
pub use crate::Protocol;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Connect {
|
pub struct Connect {
|
|
@ -14,22 +14,20 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use actix_codec::{AsyncRead, AsyncWrite, ReadBuf};
|
use actix_codec::{AsyncRead, AsyncWrite, ReadBuf};
|
||||||
|
use actix_http::Protocol;
|
||||||
use actix_rt::time::{sleep, Sleep};
|
use actix_rt::time::{sleep, Sleep};
|
||||||
use actix_service::Service;
|
use actix_service::Service;
|
||||||
use ahash::AHashMap;
|
use ahash::AHashMap;
|
||||||
use futures_core::future::LocalBoxFuture;
|
use futures_core::future::LocalBoxFuture;
|
||||||
use http::uri::Authority;
|
use http::uri::Authority;
|
||||||
use pin_project::pin_project;
|
use pin_project_lite::pin_project;
|
||||||
use tokio::sync::{OwnedSemaphorePermit, Semaphore};
|
use tokio::sync::{OwnedSemaphorePermit, Semaphore};
|
||||||
|
|
||||||
use super::config::ConnectorConfig;
|
use super::config::ConnectorConfig;
|
||||||
use super::connection::{
|
use super::connection::{ConnectionInnerType, ConnectionIo, ConnectionType, H2ConnectionInner};
|
||||||
ConnectionInnerType, ConnectionIo, ConnectionType, H2ConnectionInner,
|
|
||||||
};
|
|
||||||
use super::error::ConnectError;
|
use super::error::ConnectError;
|
||||||
use super::h2proto::handshake;
|
use super::h2proto::handshake;
|
||||||
use super::Connect;
|
use super::Connect;
|
||||||
use super::Protocol;
|
|
||||||
|
|
||||||
#[derive(Hash, Eq, PartialEq, Clone, Debug)]
|
#[derive(Hash, Eq, PartialEq, Clone, Debug)]
|
||||||
pub struct Key {
|
pub struct Key {
|
||||||
|
@ -152,9 +150,7 @@ where
|
||||||
|
|
||||||
impl<S, Io> Service<Connect> for ConnectionPool<S, Io>
|
impl<S, Io> Service<Connect> for ConnectionPool<S, Io>
|
||||||
where
|
where
|
||||||
S: Service<Connect, Response = (Io, Protocol), Error = ConnectError>
|
S: Service<Connect, Response = (Io, Protocol), Error = ConnectError> + Clone + 'static,
|
||||||
+ Clone
|
|
||||||
+ 'static,
|
|
||||||
Io: ConnectionIo,
|
Io: ConnectionIo,
|
||||||
{
|
{
|
||||||
type Response = ConnectionType<Io>;
|
type Response = ConnectionType<Io>;
|
||||||
|
@ -195,8 +191,8 @@ where
|
||||||
let config = &inner.config;
|
let config = &inner.config;
|
||||||
let idle_dur = now - c.used;
|
let idle_dur = now - c.used;
|
||||||
let age = now - c.created;
|
let age = now - c.created;
|
||||||
let conn_ineligible = idle_dur > config.conn_keep_alive
|
let conn_ineligible =
|
||||||
|| age > config.conn_lifetime;
|
idle_dur > config.conn_keep_alive || age > config.conn_lifetime;
|
||||||
|
|
||||||
if conn_ineligible {
|
if conn_ineligible {
|
||||||
// drop connections that are too old
|
// drop connections that are too old
|
||||||
|
@ -231,9 +227,7 @@ where
|
||||||
|
|
||||||
// match the connection and spawn new one if did not get anything.
|
// match the connection and spawn new one if did not get anything.
|
||||||
match conn {
|
match conn {
|
||||||
Some(conn) => {
|
Some(conn) => Ok(ConnectionType::from_pool(conn.conn, conn.created, acquired)),
|
||||||
Ok(ConnectionType::from_pool(conn.conn, conn.created, acquired))
|
|
||||||
}
|
|
||||||
None => {
|
None => {
|
||||||
let (io, proto) = connector.call(req).await?;
|
let (io, proto) = connector.call(req).await?;
|
||||||
|
|
||||||
|
@ -284,9 +278,7 @@ where
|
||||||
let mut read_buf = ReadBuf::new(&mut buf);
|
let mut read_buf = ReadBuf::new(&mut buf);
|
||||||
|
|
||||||
let state = match Pin::new(&mut this.io).poll_read(cx, &mut read_buf) {
|
let state = match Pin::new(&mut this.io).poll_read(cx, &mut read_buf) {
|
||||||
Poll::Ready(Ok(())) if !read_buf.filled().is_empty() => {
|
Poll::Ready(Ok(())) if !read_buf.filled().is_empty() => ConnectionState::Tainted,
|
||||||
ConnectionState::Tainted
|
|
||||||
}
|
|
||||||
|
|
||||||
Poll::Pending => ConnectionState::Live,
|
Poll::Pending => ConnectionState::Live,
|
||||||
_ => ConnectionState::Skip,
|
_ => ConnectionState::Skip,
|
||||||
|
@ -302,11 +294,13 @@ struct PooledConnection<Io> {
|
||||||
created: Instant,
|
created: Instant,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pin_project]
|
pin_project! {
|
||||||
struct CloseConnection<Io> {
|
#[project = CloseConnectionProj]
|
||||||
io: Io,
|
struct CloseConnection<Io> {
|
||||||
#[pin]
|
io: Io,
|
||||||
timeout: Sleep,
|
#[pin]
|
||||||
|
timeout: Sleep,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Io> CloseConnection<Io>
|
impl<Io> CloseConnection<Io>
|
||||||
|
@ -413,17 +407,11 @@ mod test {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn poll_flush(
|
fn poll_flush(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||||
self: Pin<&mut Self>,
|
|
||||||
_: &mut Context<'_>,
|
|
||||||
) -> Poll<io::Result<()>> {
|
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn poll_shutdown(
|
fn poll_shutdown(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||||
self: Pin<&mut Self>,
|
|
||||||
_: &mut Context<'_>,
|
|
||||||
) -> Poll<io::Result<()>> {
|
|
||||||
Poll::Ready(Ok(()))
|
Poll::Ready(Ok(()))
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -8,16 +8,14 @@ use std::{
|
||||||
|
|
||||||
use actix_codec::Framed;
|
use actix_codec::Framed;
|
||||||
use actix_http::{
|
use actix_http::{
|
||||||
body::Body,
|
body::Body, h1::ClientCodec, Payload, RequestHead, RequestHeadType, ResponseHead,
|
||||||
client::{
|
|
||||||
Connect as ClientConnect, ConnectError, Connection, ConnectionIo, SendRequestError,
|
|
||||||
},
|
|
||||||
h1::ClientCodec,
|
|
||||||
Payload, RequestHead, RequestHeadType, ResponseHead,
|
|
||||||
};
|
};
|
||||||
use actix_service::Service;
|
use actix_service::Service;
|
||||||
use futures_core::{future::LocalBoxFuture, ready};
|
use futures_core::{future::LocalBoxFuture, ready};
|
||||||
|
|
||||||
|
use crate::client::{
|
||||||
|
Connect as ClientConnect, ConnectError, Connection, ConnectionIo, SendRequestError,
|
||||||
|
};
|
||||||
use crate::response::ClientResponse;
|
use crate::response::ClientResponse;
|
||||||
|
|
||||||
pub type BoxConnectorService = Rc<
|
pub type BoxConnectorService = Rc<
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
//! HTTP client errors
|
//! HTTP client errors
|
||||||
|
|
||||||
pub use actix_http::client::{ConnectError, FreezeRequestError, InvalidUrl, SendRequestError};
|
pub use actix_http::{
|
||||||
pub use actix_http::error::PayloadError;
|
error::PayloadError,
|
||||||
pub use actix_http::http::Error as HttpError;
|
http::{header::HeaderValue, Error as HttpError, StatusCode},
|
||||||
pub use actix_http::ws::HandshakeError as WsHandshakeError;
|
ws::{HandshakeError as WsHandshakeError, ProtocolError as WsProtocolError},
|
||||||
pub use actix_http::ws::ProtocolError as WsProtocolError;
|
};
|
||||||
|
|
||||||
|
use derive_more::{Display, From};
|
||||||
use serde_json::error::Error as JsonError;
|
use serde_json::error::Error as JsonError;
|
||||||
|
|
||||||
use actix_http::http::{header::HeaderValue, StatusCode};
|
pub use crate::client::{ConnectError, FreezeRequestError, InvalidUrl, SendRequestError};
|
||||||
use derive_more::{Display, From};
|
|
||||||
|
|
||||||
/// Websocket client error
|
/// Websocket client error
|
||||||
#[derive(Debug, Display, From)]
|
#[derive(Debug, Display, From)]
|
||||||
|
|
|
@ -104,22 +104,8 @@
|
||||||
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
||||||
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
||||||
|
|
||||||
use std::{convert::TryFrom, rc::Rc, time::Duration};
|
|
||||||
|
|
||||||
#[cfg(feature = "cookies")]
|
|
||||||
pub use cookie;
|
|
||||||
|
|
||||||
pub use actix_http::{client::Connector, http};
|
|
||||||
|
|
||||||
use actix_http::{
|
|
||||||
client::{TcpConnect, TcpConnectError, TcpConnection},
|
|
||||||
http::{Error as HttpError, HeaderMap, Method, Uri},
|
|
||||||
RequestHead,
|
|
||||||
};
|
|
||||||
use actix_rt::net::TcpStream;
|
|
||||||
use actix_service::Service;
|
|
||||||
|
|
||||||
mod builder;
|
mod builder;
|
||||||
|
mod client;
|
||||||
mod connect;
|
mod connect;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
mod frozen;
|
mod frozen;
|
||||||
|
@ -130,13 +116,29 @@ mod sender;
|
||||||
pub mod test;
|
pub mod test;
|
||||||
pub mod ws;
|
pub mod ws;
|
||||||
|
|
||||||
|
pub use actix_http::http;
|
||||||
|
#[cfg(feature = "cookies")]
|
||||||
|
pub use cookie;
|
||||||
|
|
||||||
pub use self::builder::ClientBuilder;
|
pub use self::builder::ClientBuilder;
|
||||||
|
pub use self::client::Connector;
|
||||||
pub use self::connect::{BoxConnectorService, BoxedSocket, ConnectRequest, ConnectResponse};
|
pub use self::connect::{BoxConnectorService, BoxedSocket, ConnectRequest, ConnectResponse};
|
||||||
pub use self::frozen::{FrozenClientRequest, FrozenSendBuilder};
|
pub use self::frozen::{FrozenClientRequest, FrozenSendBuilder};
|
||||||
pub use self::request::ClientRequest;
|
pub use self::request::ClientRequest;
|
||||||
pub use self::response::{ClientResponse, JsonBody, MessageBody};
|
pub use self::response::{ClientResponse, JsonBody, MessageBody};
|
||||||
pub use self::sender::SendClientRequest;
|
pub use self::sender::SendClientRequest;
|
||||||
|
|
||||||
|
use std::{convert::TryFrom, rc::Rc, time::Duration};
|
||||||
|
|
||||||
|
use actix_http::{
|
||||||
|
http::{Error as HttpError, HeaderMap, Method, Uri},
|
||||||
|
RequestHead,
|
||||||
|
};
|
||||||
|
use actix_rt::net::TcpStream;
|
||||||
|
use actix_service::Service;
|
||||||
|
|
||||||
|
use self::client::{TcpConnect, TcpConnectError, TcpConnection};
|
||||||
|
|
||||||
/// An asynchronous HTTP and WebSocket client.
|
/// An asynchronous HTTP and WebSocket client.
|
||||||
///
|
///
|
||||||
/// You should take care to create, at most, one `Client` per thread. Otherwise, expect higher CPU
|
/// You should take care to create, at most, one `Client` per thread. Otherwise, expect higher CPU
|
||||||
|
|
|
@ -9,7 +9,6 @@ use std::{
|
||||||
|
|
||||||
use actix_http::{
|
use actix_http::{
|
||||||
body::Body,
|
body::Body,
|
||||||
client::{InvalidUrl, SendRequestError},
|
|
||||||
http::{header, Method, StatusCode, Uri},
|
http::{header, Method, StatusCode, Uri},
|
||||||
RequestHead, RequestHeadType,
|
RequestHead, RequestHeadType,
|
||||||
};
|
};
|
||||||
|
@ -19,6 +18,7 @@ use futures_core::ready;
|
||||||
|
|
||||||
use super::Transform;
|
use super::Transform;
|
||||||
|
|
||||||
|
use crate::client::{InvalidUrl, SendRequestError};
|
||||||
use crate::connect::{ConnectRequest, ConnectResponse};
|
use crate::connect::{ConnectRequest, ConnectResponse};
|
||||||
use crate::ClientResponse;
|
use crate::ClientResponse;
|
||||||
|
|
||||||
|
|
|
@ -8,44 +8,60 @@ use std::{
|
||||||
atomic::{AtomicUsize, Ordering},
|
atomic::{AtomicUsize, Ordering},
|
||||||
Arc,
|
Arc,
|
||||||
},
|
},
|
||||||
|
time::SystemTime,
|
||||||
};
|
};
|
||||||
|
|
||||||
use actix_http::HttpService;
|
use actix_http::HttpService;
|
||||||
use actix_http_test::test_server;
|
use actix_http_test::test_server;
|
||||||
use actix_service::{fn_service, map_config, ServiceFactoryExt};
|
use actix_service::{fn_service, map_config, ServiceFactoryExt};
|
||||||
|
use actix_tls::connect::tls::rustls::webpki_roots_cert_store;
|
||||||
use actix_utils::future::ok;
|
use actix_utils::future::ok;
|
||||||
use actix_web::{dev::AppConfig, http::Version, web, App, HttpResponse};
|
use actix_web::{dev::AppConfig, http::Version, web, App, HttpResponse};
|
||||||
use rustls::internal::pemfile::{certs, pkcs8_private_keys};
|
use rustls::{
|
||||||
use rustls::{ClientConfig, NoClientAuth, ServerConfig};
|
client::{ServerCertVerified, ServerCertVerifier},
|
||||||
|
Certificate, ClientConfig, OwnedTrustAnchor, PrivateKey, RootCertStore, ServerConfig,
|
||||||
|
ServerName,
|
||||||
|
};
|
||||||
|
use rustls_pemfile::{certs, pkcs8_private_keys};
|
||||||
|
|
||||||
fn tls_config() -> ServerConfig {
|
fn tls_config() -> ServerConfig {
|
||||||
let cert = rcgen::generate_simple_self_signed(vec!["localhost".to_owned()]).unwrap();
|
let cert = rcgen::generate_simple_self_signed(vec!["localhost".to_owned()]).unwrap();
|
||||||
let cert_file = cert.serialize_pem().unwrap();
|
let cert_file = cert.serialize_pem().unwrap();
|
||||||
let key_file = cert.serialize_private_key_pem();
|
let key_file = cert.serialize_private_key_pem();
|
||||||
|
|
||||||
let mut config = ServerConfig::new(NoClientAuth::new());
|
|
||||||
let cert_file = &mut BufReader::new(cert_file.as_bytes());
|
let cert_file = &mut BufReader::new(cert_file.as_bytes());
|
||||||
let key_file = &mut BufReader::new(key_file.as_bytes());
|
let key_file = &mut BufReader::new(key_file.as_bytes());
|
||||||
|
|
||||||
let cert_chain = certs(cert_file).unwrap();
|
let cert_chain = certs(cert_file)
|
||||||
|
.unwrap()
|
||||||
|
.into_iter()
|
||||||
|
.map(Certificate)
|
||||||
|
.collect();
|
||||||
let mut keys = pkcs8_private_keys(key_file).unwrap();
|
let mut keys = pkcs8_private_keys(key_file).unwrap();
|
||||||
config.set_single_cert(cert_chain, keys.remove(0)).unwrap();
|
|
||||||
|
|
||||||
config
|
ServerConfig::builder()
|
||||||
|
.with_safe_defaults()
|
||||||
|
.with_no_client_auth()
|
||||||
|
.with_single_cert(cert_chain, PrivateKey(keys.remove(0)))
|
||||||
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
mod danger {
|
mod danger {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
pub struct NoCertificateVerification;
|
pub struct NoCertificateVerification;
|
||||||
|
|
||||||
impl rustls::ServerCertVerifier for NoCertificateVerification {
|
impl ServerCertVerifier for NoCertificateVerification {
|
||||||
fn verify_server_cert(
|
fn verify_server_cert(
|
||||||
&self,
|
&self,
|
||||||
_roots: &rustls::RootCertStore,
|
_end_entity: &Certificate,
|
||||||
_presented_certs: &[rustls::Certificate],
|
_intermediates: &[Certificate],
|
||||||
_dns_name: webpki::DNSNameRef<'_>,
|
_server_name: &ServerName,
|
||||||
_ocsp: &[u8],
|
_scts: &mut dyn Iterator<Item = &[u8]>,
|
||||||
) -> Result<rustls::ServerCertVerified, rustls::TLSError> {
|
_ocsp_response: &[u8],
|
||||||
Ok(rustls::ServerCertVerified::assertion())
|
_now: SystemTime,
|
||||||
|
) -> Result<ServerCertVerified, rustls::Error> {
|
||||||
|
Ok(ServerCertVerified::assertion())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -73,10 +89,15 @@ async fn test_connection_reuse_h2() {
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
// disable TLS verification
|
let mut config = ClientConfig::builder()
|
||||||
let mut config = ClientConfig::new();
|
.with_safe_defaults()
|
||||||
|
.with_root_certificates(webpki_roots_cert_store())
|
||||||
|
.with_no_client_auth();
|
||||||
|
|
||||||
let protos = vec![b"h2".to_vec(), b"http/1.1".to_vec()];
|
let protos = vec![b"h2".to_vec(), b"http/1.1".to_vec()];
|
||||||
config.set_protocols(&protos);
|
config.alpn_protocols = protos;
|
||||||
|
|
||||||
|
// disable TLS verification
|
||||||
config
|
config
|
||||||
.dangerous()
|
.dangerous()
|
||||||
.set_certificate_verifier(Arc::new(danger::NoCertificateVerification));
|
.set_certificate_verifier(Arc::new(danger::NoCertificateVerification));
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
msrv = "1.51"
|
msrv = "1.52"
|
||||||
|
|
|
@ -35,7 +35,7 @@ async fn main() -> std::io::Result<()> {
|
||||||
)
|
)
|
||||||
.service(web::resource("/test1.html").to(|| async { "Test\r\n" }))
|
.service(web::resource("/test1.html").to(|| async { "Test\r\n" }))
|
||||||
})
|
})
|
||||||
.bind("127.0.0.1:8080")?
|
.bind(("127.0.0.1", 8080))?
|
||||||
.workers(1)
|
.workers(1)
|
||||||
.run()
|
.run()
|
||||||
.await
|
.await
|
||||||
|
|
36
src/data.rs
36
src/data.rs
|
@ -75,7 +75,9 @@ impl<T> Data<T> {
|
||||||
pub fn new(state: T) -> Data<T> {
|
pub fn new(state: T) -> Data<T> {
|
||||||
Data(Arc::new(state))
|
Data(Arc::new(state))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized> Data<T> {
|
||||||
/// Get reference to inner app data.
|
/// Get reference to inner app data.
|
||||||
pub fn get_ref(&self) -> &T {
|
pub fn get_ref(&self) -> &T {
|
||||||
self.0.as_ref()
|
self.0.as_ref()
|
||||||
|
@ -304,4 +306,38 @@ mod tests {
|
||||||
let data_arc = Data::from(dyn_arc);
|
let data_arc = Data::from(dyn_arc);
|
||||||
assert_eq!(data_arc_box.get_num(), data_arc.get_num())
|
assert_eq!(data_arc_box.get_num(), data_arc.get_num())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn test_dyn_data_into_arc() {
|
||||||
|
trait TestTrait {
|
||||||
|
fn get_num(&self) -> i32;
|
||||||
|
}
|
||||||
|
struct A {}
|
||||||
|
impl TestTrait for A {
|
||||||
|
fn get_num(&self) -> i32 {
|
||||||
|
42
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let dyn_arc: Arc<dyn TestTrait> = Arc::new(A {});
|
||||||
|
let data_arc = Data::from(dyn_arc);
|
||||||
|
let arc_from_data = data_arc.clone().into_inner();
|
||||||
|
assert_eq!(data_arc.get_num(), arc_from_data.get_num())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn test_get_ref_from_dyn_data() {
|
||||||
|
trait TestTrait {
|
||||||
|
fn get_num(&self) -> i32;
|
||||||
|
}
|
||||||
|
struct A {}
|
||||||
|
impl TestTrait for A {
|
||||||
|
fn get_num(&self) -> i32 {
|
||||||
|
42
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let dyn_arc: Arc<dyn TestTrait> = Arc::new(A {});
|
||||||
|
let data_arc = Data::from(dyn_arc);
|
||||||
|
let ref_data = data_arc.get_ref();
|
||||||
|
assert_eq!(data_arc.get_num(), ref_data.get_num())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,52 +60,53 @@ crate::http::header::common_header! {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ContentType {
|
impl ContentType {
|
||||||
/// A constructor to easily create a `Content-Type: application/json`
|
/// A constructor to easily create a `Content-Type: application/json`
|
||||||
/// header.
|
/// header.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn json() -> ContentType {
|
pub fn json() -> ContentType {
|
||||||
ContentType(mime::APPLICATION_JSON)
|
ContentType(mime::APPLICATION_JSON)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A constructor to easily create a `Content-Type: text/plain;
|
/// A constructor to easily create a `Content-Type: text/plain;
|
||||||
/// charset=utf-8` header.
|
/// charset=utf-8` header.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn plaintext() -> ContentType {
|
pub fn plaintext() -> ContentType {
|
||||||
ContentType(mime::TEXT_PLAIN_UTF_8)
|
ContentType(mime::TEXT_PLAIN_UTF_8)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A constructor to easily create a `Content-Type: text/html` header.
|
/// A constructor to easily create a `Content-Type: text/html; charset=utf-8`
|
||||||
|
/// header.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn html() -> ContentType {
|
pub fn html() -> ContentType {
|
||||||
ContentType(mime::TEXT_HTML)
|
ContentType(mime::TEXT_HTML_UTF_8)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A constructor to easily create a `Content-Type: text/xml` header.
|
/// A constructor to easily create a `Content-Type: text/xml` header.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn xml() -> ContentType {
|
pub fn xml() -> ContentType {
|
||||||
ContentType(mime::TEXT_XML)
|
ContentType(mime::TEXT_XML)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A constructor to easily create a `Content-Type:
|
/// A constructor to easily create a `Content-Type:
|
||||||
/// application/www-form-url-encoded` header.
|
/// application/www-form-url-encoded` header.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn form_url_encoded() -> ContentType {
|
pub fn form_url_encoded() -> ContentType {
|
||||||
ContentType(mime::APPLICATION_WWW_FORM_URLENCODED)
|
ContentType(mime::APPLICATION_WWW_FORM_URLENCODED)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A constructor to easily create a `Content-Type: image/jpeg` header.
|
/// A constructor to easily create a `Content-Type: image/jpeg` header.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn jpeg() -> ContentType {
|
pub fn jpeg() -> ContentType {
|
||||||
ContentType(mime::IMAGE_JPEG)
|
ContentType(mime::IMAGE_JPEG)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A constructor to easily create a `Content-Type: image/png` header.
|
/// A constructor to easily create a `Content-Type: image/png` header.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn png() -> ContentType {
|
pub fn png() -> ContentType {
|
||||||
ContentType(mime::IMAGE_PNG)
|
ContentType(mime::IMAGE_PNG)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A constructor to easily create a `Content-Type:
|
/// A constructor to easily create a `Content-Type:
|
||||||
/// application/octet-stream` header.
|
/// application/octet-stream` header.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn octet_stream() -> ContentType {
|
pub fn octet_stream() -> ContentType {
|
||||||
|
|
|
@ -53,7 +53,7 @@
|
||||||
//! * SSL support using OpenSSL or Rustls
|
//! * SSL support using OpenSSL or Rustls
|
||||||
//! * Middlewares ([Logger, Session, CORS, etc](https://actix.rs/docs/middleware/))
|
//! * Middlewares ([Logger, Session, CORS, etc](https://actix.rs/docs/middleware/))
|
||||||
//! * Includes an async [HTTP client](https://docs.rs/awc/)
|
//! * Includes an async [HTTP client](https://docs.rs/awc/)
|
||||||
//! * Runs on stable Rust 1.51+
|
//! * Runs on stable Rust 1.52+
|
||||||
//!
|
//!
|
||||||
//! # Crate Features
|
//! # Crate Features
|
||||||
//! * `cookies` - cookies support (enabled by default)
|
//! * `cookies` - cookies support (enabled by default)
|
||||||
|
|
|
@ -276,7 +276,7 @@ impl AcceptEncoding {
|
||||||
let mut encodings = raw
|
let mut encodings = raw
|
||||||
.replace(' ', "")
|
.replace(' ', "")
|
||||||
.split(',')
|
.split(',')
|
||||||
.filter_map(|l| AcceptEncoding::new(l))
|
.filter_map(AcceptEncoding::new)
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
encodings.sort();
|
encodings.sort();
|
||||||
|
|
|
@ -393,16 +393,6 @@ impl<B> ServiceResponse<B> {
|
||||||
self.response.headers_mut()
|
self.response.headers_mut()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Execute closure and in case of error convert it to response.
|
|
||||||
pub fn checked_expr<F, E>(mut self, f: F) -> Result<Self, Error>
|
|
||||||
where
|
|
||||||
F: FnOnce(&mut Self) -> Result<(), E>,
|
|
||||||
E: Into<Error>,
|
|
||||||
{
|
|
||||||
f(&mut self).map_err(Into::into)?;
|
|
||||||
Ok(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Extract response body
|
/// Extract response body
|
||||||
pub fn into_body(self) -> B {
|
pub fn into_body(self) -> B {
|
||||||
self.response.into_body()
|
self.response.into_body()
|
||||||
|
|
16
src/test.rs
16
src/test.rs
|
@ -52,7 +52,7 @@ pub fn default_service(
|
||||||
/// use actix_service::Service;
|
/// use actix_service::Service;
|
||||||
/// use actix_web::{test, web, App, HttpResponse, http::StatusCode};
|
/// use actix_web::{test, web, App, HttpResponse, http::StatusCode};
|
||||||
///
|
///
|
||||||
/// #[actix_rt::test]
|
/// #[actix_web::test]
|
||||||
/// async fn test_init_service() {
|
/// async fn test_init_service() {
|
||||||
/// let app = test::init_service(
|
/// let app = test::init_service(
|
||||||
/// App::new()
|
/// App::new()
|
||||||
|
@ -98,7 +98,7 @@ where
|
||||||
/// ```
|
/// ```
|
||||||
/// use actix_web::{test, web, App, HttpResponse, http::StatusCode};
|
/// use actix_web::{test, web, App, HttpResponse, http::StatusCode};
|
||||||
///
|
///
|
||||||
/// #[actix_rt::test]
|
/// #[actix_web::test]
|
||||||
/// async fn test_response() {
|
/// async fn test_response() {
|
||||||
/// let app = test::init_service(
|
/// let app = test::init_service(
|
||||||
/// App::new()
|
/// App::new()
|
||||||
|
@ -129,7 +129,7 @@ where
|
||||||
/// use actix_web::{test, web, App, HttpResponse, http::header};
|
/// use actix_web::{test, web, App, HttpResponse, http::header};
|
||||||
/// use bytes::Bytes;
|
/// use bytes::Bytes;
|
||||||
///
|
///
|
||||||
/// #[actix_rt::test]
|
/// #[actix_web::test]
|
||||||
/// async fn test_index() {
|
/// async fn test_index() {
|
||||||
/// let app = test::init_service(
|
/// let app = test::init_service(
|
||||||
/// App::new().service(
|
/// App::new().service(
|
||||||
|
@ -176,7 +176,7 @@ where
|
||||||
/// use actix_web::{test, web, App, HttpResponse, http::header};
|
/// use actix_web::{test, web, App, HttpResponse, http::header};
|
||||||
/// use bytes::Bytes;
|
/// use bytes::Bytes;
|
||||||
///
|
///
|
||||||
/// #[actix_rt::test]
|
/// #[actix_web::test]
|
||||||
/// async fn test_index() {
|
/// async fn test_index() {
|
||||||
/// let app = test::init_service(
|
/// let app = test::init_service(
|
||||||
/// App::new().service(
|
/// App::new().service(
|
||||||
|
@ -224,7 +224,7 @@ where
|
||||||
/// name: String,
|
/// name: String,
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// #[actix_rt::test]
|
/// #[actix_web::test]
|
||||||
/// async fn test_post_person() {
|
/// async fn test_post_person() {
|
||||||
/// let app = test::init_service(
|
/// let app = test::init_service(
|
||||||
/// App::new().service(
|
/// App::new().service(
|
||||||
|
@ -296,7 +296,7 @@ where
|
||||||
/// name: String
|
/// name: String
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// #[actix_rt::test]
|
/// #[actix_web::test]
|
||||||
/// async fn test_add_person() {
|
/// async fn test_add_person() {
|
||||||
/// let app = test::init_service(
|
/// let app = test::init_service(
|
||||||
/// App::new().service(
|
/// App::new().service(
|
||||||
|
@ -356,8 +356,8 @@ where
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// #[test]
|
/// #[actix_web::test]
|
||||||
/// fn test_index() {
|
/// async fn test_index() {
|
||||||
/// let req = test::TestRequest::default().insert_header("content-type", "text/plain")
|
/// let req = test::TestRequest::default().insert_header("content-type", "text/plain")
|
||||||
/// .to_http_request();
|
/// .to_http_request();
|
||||||
///
|
///
|
||||||
|
|
|
@ -102,7 +102,7 @@ where
|
||||||
fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
|
fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
|
||||||
let error_handler = req
|
let error_handler = req
|
||||||
.app_data::<PathConfig>()
|
.app_data::<PathConfig>()
|
||||||
.and_then(|c| c.ehandler.clone());
|
.and_then(|c| c.err_handler.clone());
|
||||||
|
|
||||||
ready(
|
ready(
|
||||||
de::Deserialize::deserialize(PathDeserializer::new(req.match_info()))
|
de::Deserialize::deserialize(PathDeserializer::new(req.match_info()))
|
||||||
|
@ -158,9 +158,9 @@ where
|
||||||
/// );
|
/// );
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Default)]
|
||||||
pub struct PathConfig {
|
pub struct PathConfig {
|
||||||
ehandler: Option<Arc<dyn Fn(PathError, &HttpRequest) -> Error + Send + Sync>>,
|
err_handler: Option<Arc<dyn Fn(PathError, &HttpRequest) -> Error + Send + Sync>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PathConfig {
|
impl PathConfig {
|
||||||
|
@ -169,17 +169,11 @@ impl PathConfig {
|
||||||
where
|
where
|
||||||
F: Fn(PathError, &HttpRequest) -> Error + Send + Sync + 'static,
|
F: Fn(PathError, &HttpRequest) -> Error + Send + Sync + 'static,
|
||||||
{
|
{
|
||||||
self.ehandler = Some(Arc::new(f));
|
self.err_handler = Some(Arc::new(f));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for PathConfig {
|
|
||||||
fn default() -> Self {
|
|
||||||
PathConfig { ehandler: None }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use actix_router::ResourceDef;
|
use actix_router::ResourceDef;
|
||||||
|
|
|
@ -167,7 +167,7 @@ impl<T: DeserializeOwned> FromRequest for Query<T> {
|
||||||
/// .app_data(query_cfg)
|
/// .app_data(query_cfg)
|
||||||
/// .service(index);
|
/// .service(index);
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Default)]
|
||||||
pub struct QueryConfig {
|
pub struct QueryConfig {
|
||||||
err_handler: Option<Arc<dyn Fn(QueryPayloadError, &HttpRequest) -> Error + Send + Sync>>,
|
err_handler: Option<Arc<dyn Fn(QueryPayloadError, &HttpRequest) -> Error + Send + Sync>>,
|
||||||
}
|
}
|
||||||
|
@ -183,12 +183,6 @@ impl QueryConfig {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for QueryConfig {
|
|
||||||
fn default() -> Self {
|
|
||||||
QueryConfig { err_handler: None }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use actix_http::http::StatusCode;
|
use actix_http::http::StatusCode;
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
//! Checks that test macro does not cause problems in the presence of imports named "test" that
|
||||||
|
//! could be either a module with test items or the "test with runtime" macro itself.
|
||||||
|
//!
|
||||||
|
//! Before actix/actix-net#399 was implemented, this macro was running twice. The first run output
|
||||||
|
//! `#[test]` and it got run again and since it was in scope.
|
||||||
|
//!
|
||||||
|
//! Prevented by using the fully-qualified test marker (`#[::core::prelude::v1::test]`).
|
||||||
|
|
||||||
|
use actix_web::test;
|
||||||
|
|
||||||
|
#[actix_web::test]
|
||||||
|
async fn test_macro_naming_conflict() {
|
||||||
|
let _req = test::TestRequest::default();
|
||||||
|
assert_eq!(async { 1 }.await, 1);
|
||||||
|
}
|
|
@ -883,27 +883,31 @@ async fn test_brotli_encoding_large_openssl() {
|
||||||
mod plus_rustls {
|
mod plus_rustls {
|
||||||
use std::io::BufReader;
|
use std::io::BufReader;
|
||||||
|
|
||||||
use rustls::{
|
use rustls::{Certificate, PrivateKey, ServerConfig as RustlsServerConfig};
|
||||||
internal::pemfile::{certs, pkcs8_private_keys},
|
use rustls_pemfile::{certs, pkcs8_private_keys};
|
||||||
NoClientAuth, ServerConfig as RustlsServerConfig,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
fn rustls_config() -> RustlsServerConfig {
|
fn tls_config() -> RustlsServerConfig {
|
||||||
let cert = rcgen::generate_simple_self_signed(vec!["localhost".to_owned()]).unwrap();
|
let cert = rcgen::generate_simple_self_signed(vec!["localhost".to_owned()]).unwrap();
|
||||||
let cert_file = cert.serialize_pem().unwrap();
|
let cert_file = cert.serialize_pem().unwrap();
|
||||||
let key_file = cert.serialize_private_key_pem();
|
let key_file = cert.serialize_private_key_pem();
|
||||||
|
|
||||||
let mut config = RustlsServerConfig::new(NoClientAuth::new());
|
|
||||||
let cert_file = &mut BufReader::new(cert_file.as_bytes());
|
let cert_file = &mut BufReader::new(cert_file.as_bytes());
|
||||||
let key_file = &mut BufReader::new(key_file.as_bytes());
|
let key_file = &mut BufReader::new(key_file.as_bytes());
|
||||||
|
|
||||||
let cert_chain = certs(cert_file).unwrap();
|
let cert_chain = certs(cert_file)
|
||||||
|
.unwrap()
|
||||||
|
.into_iter()
|
||||||
|
.map(Certificate)
|
||||||
|
.collect();
|
||||||
let mut keys = pkcs8_private_keys(key_file).unwrap();
|
let mut keys = pkcs8_private_keys(key_file).unwrap();
|
||||||
config.set_single_cert(cert_chain, keys.remove(0)).unwrap();
|
|
||||||
|
|
||||||
config
|
RustlsServerConfig::builder()
|
||||||
|
.with_safe_defaults()
|
||||||
|
.with_no_client_auth()
|
||||||
|
.with_single_cert(cert_chain, PrivateKey(keys.remove(0)))
|
||||||
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
|
@ -914,7 +918,7 @@ mod plus_rustls {
|
||||||
.map(char::from)
|
.map(char::from)
|
||||||
.collect::<String>();
|
.collect::<String>();
|
||||||
|
|
||||||
let srv = actix_test::start_with(actix_test::config().rustls(rustls_config()), || {
|
let srv = actix_test::start_with(actix_test::config().rustls(tls_config()), || {
|
||||||
App::new().service(web::resource("/").route(web::to(|bytes: Bytes| {
|
App::new().service(web::resource("/").route(web::to(|bytes: Bytes| {
|
||||||
HttpResponse::Ok()
|
HttpResponse::Ok()
|
||||||
.encoding(actix_web::http::ContentEncoding::Identity)
|
.encoding(actix_web::http::ContentEncoding::Identity)
|
||||||
|
|
Loading…
Reference in New Issue