mirror of
https://github.com/fafhrd91/actix-web
synced 2025-08-22 13:45:13 +02:00
Compare commits
1 Commits
actors-v4.
...
remove-eit
Author | SHA1 | Date | |
---|---|---|---|
|
2ee953a118 |
3
.github/FUNDING.yml
vendored
3
.github/FUNDING.yml
vendored
@@ -1,3 +0,0 @@
|
|||||||
# These are supported funding model platforms
|
|
||||||
|
|
||||||
github: [robjtede]
|
|
4
.github/ISSUE_TEMPLATE/bug_report.md
vendored
4
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -33,5 +33,5 @@ Please search on the [Actix Web issue tracker](https://github.com/actix/actix-we
|
|||||||
## Your Environment
|
## Your Environment
|
||||||
<!--- Include as many relevant details about the environment you experienced the bug in -->
|
<!--- Include as many relevant details about the environment you experienced the bug in -->
|
||||||
|
|
||||||
- Rust Version (I.e, output of `rustc -V`):
|
* Rust Version (I.e, output of `rustc -V`):
|
||||||
- Actix Web Version:
|
* Actix Web Version:
|
||||||
|
66
.github/workflows/ci-master.yml
vendored
66
.github/workflows/ci-master.yml
vendored
@@ -1,66 +0,0 @@
|
|||||||
name: CI (master only)
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [master]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
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
|
|
||||||
uses: actions-rs/cargo@v1
|
|
||||||
with: { command: ci-check-all-feature-powerset }
|
|
||||||
|
|
||||||
- name: check feature combinations
|
|
||||||
uses: actions-rs/cargo@v1
|
|
||||||
with: { command: ci-check-all-feature-powerset-linux }
|
|
||||||
|
|
||||||
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
|
|
||||||
run: |
|
|
||||||
cargo install cargo-tarpaulin --vers "^0.13"
|
|
||||||
cargo tarpaulin --workspace --features=rustls,openssl --out Xml --verbose
|
|
||||||
- name: Upload to Codecov
|
|
||||||
uses: codecov/codecov-action@v1
|
|
||||||
with: { file: cobertura.xml }
|
|
62
.github/workflows/ci.yml
vendored
62
.github/workflows/ci.yml
vendored
@@ -96,6 +96,68 @@ jobs:
|
|||||||
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
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with: { command: ci-check-all-feature-powerset }
|
||||||
|
|
||||||
|
- name: check feature combinations
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with: { command: ci-check-all-feature-powerset-linux }
|
||||||
|
|
||||||
|
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 --workspace --features=rustls,openssl --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: doc tests
|
name: doc tests
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
565
CHANGES.md
565
CHANGES.md
File diff suppressed because it is too large
Load Diff
@@ -8,19 +8,19 @@ In the interest of fostering an open and welcoming environment, we as contributo
|
|||||||
|
|
||||||
Examples of behavior that contributes to creating a positive environment include:
|
Examples of behavior that contributes to creating a positive environment include:
|
||||||
|
|
||||||
- Using welcoming and inclusive language
|
* Using welcoming and inclusive language
|
||||||
- Being respectful of differing viewpoints and experiences
|
* Being respectful of differing viewpoints and experiences
|
||||||
- Gracefully accepting constructive criticism
|
* Gracefully accepting constructive criticism
|
||||||
- Focusing on what is best for the community
|
* Focusing on what is best for the community
|
||||||
- Showing empathy towards other community members
|
* Showing empathy towards other community members
|
||||||
|
|
||||||
Examples of unacceptable behavior by participants include:
|
Examples of unacceptable behavior by participants include:
|
||||||
|
|
||||||
- The use of sexualized language or imagery and unwelcome sexual attention or advances
|
* The use of sexualized language or imagery and unwelcome sexual attention or advances
|
||||||
- Trolling, insulting/derogatory comments, and personal or political attacks
|
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||||
- Public or private harassment
|
* Public or private harassment
|
||||||
- Publishing others' private information, such as a physical or electronic address, without explicit permission
|
* Publishing others' private information, such as a physical or electronic address, without explicit permission
|
||||||
- Other conduct which could reasonably be considered inappropriate in a professional setting
|
* Other conduct which could reasonably be considered inappropriate in a professional setting
|
||||||
|
|
||||||
## Our Responsibilities
|
## Our Responsibilities
|
||||||
|
|
||||||
|
16
Cargo.toml
16
Cargo.toml
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "actix-web"
|
name = "actix-web"
|
||||||
version = "4.0.0-beta.16"
|
version = "4.0.0-beta.14"
|
||||||
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"]
|
||||||
@@ -72,13 +72,13 @@ experimental-io-uring = ["actix-server/io-uring"]
|
|||||||
actix-codec = "0.4.1"
|
actix-codec = "0.4.1"
|
||||||
actix-macros = "0.2.3"
|
actix-macros = "0.2.3"
|
||||||
actix-rt = "2.3"
|
actix-rt = "2.3"
|
||||||
actix-server = "2.0.0-rc.2"
|
actix-server = "2.0.0-rc.1"
|
||||||
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", default-features = false, optional = true }
|
actix-tls = { version = "3.0.0-rc.1", default-features = false, optional = true }
|
||||||
|
|
||||||
actix-http = "3.0.0-beta.17"
|
actix-http = "3.0.0-beta.15"
|
||||||
actix-router = "0.5.0-beta.3"
|
actix-router = "0.5.0-beta.2"
|
||||||
actix-web-codegen = "0.5.0-beta.6"
|
actix-web-codegen = "0.5.0-beta.6"
|
||||||
|
|
||||||
ahash = "0.7"
|
ahash = "0.7"
|
||||||
@@ -89,7 +89,7 @@ derive_more = "0.99.5"
|
|||||||
encoding_rs = "0.8"
|
encoding_rs = "0.8"
|
||||||
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 }
|
futures-util = { version = "0.3.7", default-features = false }
|
||||||
itoa = "1"
|
itoa = "0.4"
|
||||||
language-tags = "0.3"
|
language-tags = "0.3"
|
||||||
once_cell = "1.5"
|
once_cell = "1.5"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
@@ -106,8 +106,8 @@ time = { version = "0.3", default-features = false, features = ["formatting"] }
|
|||||||
url = "2.1"
|
url = "2.1"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
actix-test = { version = "0.1.0-beta.10", features = ["openssl", "rustls"] }
|
actix-test = { version = "0.1.0-beta.8", features = ["openssl", "rustls"] }
|
||||||
awc = { version = "3.0.0-beta.15", features = ["openssl"] }
|
awc = { version = "3.0.0-beta.13", features = ["openssl"] }
|
||||||
|
|
||||||
brotli2 = "0.3.2"
|
brotli2 = "0.3.2"
|
||||||
criterion = { version = "0.3", features = ["html_reports"] }
|
criterion = { version = "0.3", features = ["html_reports"] }
|
||||||
|
156
MIGRATION.md
156
MIGRATION.md
@@ -1,6 +1,6 @@
|
|||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
- The default `NormalizePath` behavior now strips trailing slashes by default. This was
|
* The default `NormalizePath` behavior now strips trailing slashes by default. This was
|
||||||
previously documented to be the case in v3 but the behavior now matches. The effect is that
|
previously documented to be the case in v3 but the behavior now matches. The effect is that
|
||||||
routes defined with trailing slashes will become inaccessible when
|
routes defined with trailing slashes will become inaccessible when
|
||||||
using `NormalizePath::default()`. As such, calling `NormalizePath::default()` will log a warning.
|
using `NormalizePath::default()`. As such, calling `NormalizePath::default()` will log a warning.
|
||||||
@@ -11,9 +11,9 @@
|
|||||||
|
|
||||||
Alternatively, explicitly require trailing slashes: `NormalizePath::new(TrailingSlash::Always)`.
|
Alternatively, explicitly require trailing slashes: `NormalizePath::new(TrailingSlash::Always)`.
|
||||||
|
|
||||||
- The `type Config` of `FromRequest` was removed.
|
* The `type Config` of `FromRequest` was removed.
|
||||||
|
|
||||||
- Feature flag `compress` has been split into its supported algorithm (brotli, gzip, zstd).
|
* Feature flag `compress` has been split into its supported algorithm (brotli, gzip, zstd).
|
||||||
By default all compression algorithms are enabled.
|
By default all compression algorithms are enabled.
|
||||||
To select algorithm you want to include with `middleware::Compress` use following flags:
|
To select algorithm you want to include with `middleware::Compress` use following flags:
|
||||||
- `compress-brotli`
|
- `compress-brotli`
|
||||||
@@ -28,30 +28,30 @@
|
|||||||
|
|
||||||
## 3.0.0
|
## 3.0.0
|
||||||
|
|
||||||
- The return type for `ServiceRequest::app_data::<T>()` was changed from returning a `Data<T>` to
|
* The return type for `ServiceRequest::app_data::<T>()` was changed from returning a `Data<T>` to
|
||||||
simply a `T`. To access a `Data<T>` use `ServiceRequest::app_data::<Data<T>>()`.
|
simply a `T`. To access a `Data<T>` use `ServiceRequest::app_data::<Data<T>>()`.
|
||||||
|
|
||||||
- Cookie handling has been offloaded to the `cookie` crate:
|
* Cookie handling has been offloaded to the `cookie` crate:
|
||||||
* `USERINFO_ENCODE_SET` is no longer exposed. Percent-encoding is still supported; check docs.
|
* `USERINFO_ENCODE_SET` is no longer exposed. Percent-encoding is still supported; check docs.
|
||||||
* Some types now require lifetime parameters.
|
* Some types now require lifetime parameters.
|
||||||
|
|
||||||
- The time crate was updated to `v0.2`, a major breaking change to the time crate, which affects
|
* The time crate was updated to `v0.2`, a major breaking change to the time crate, which affects
|
||||||
any `actix-web` method previously expecting a time v0.1 input.
|
any `actix-web` method previously expecting a time v0.1 input.
|
||||||
|
|
||||||
- Setting a cookie's SameSite property, explicitly, to `SameSite::None` will now
|
* Setting a cookie's SameSite property, explicitly, to `SameSite::None` will now
|
||||||
result in `SameSite=None` being sent with the response Set-Cookie header.
|
result in `SameSite=None` being sent with the response Set-Cookie header.
|
||||||
To create a cookie without a SameSite attribute, remove any calls setting same_site.
|
To create a cookie without a SameSite attribute, remove any calls setting same_site.
|
||||||
|
|
||||||
- actix-http support for Actors messages was moved to actix-http crate and is enabled
|
* actix-http support for Actors messages was moved to actix-http crate and is enabled
|
||||||
with feature `actors`
|
with feature `actors`
|
||||||
|
|
||||||
- content_length function is removed from actix-http.
|
* content_length function is removed from actix-http.
|
||||||
You can set Content-Length by normally setting the response body or calling no_chunking function.
|
You can set Content-Length by normally setting the response body or calling no_chunking function.
|
||||||
|
|
||||||
- `BodySize::Sized64` variant has been removed. `BodySize::Sized` now receives a
|
* `BodySize::Sized64` variant has been removed. `BodySize::Sized` now receives a
|
||||||
`u64` instead of a `usize`.
|
`u64` instead of a `usize`.
|
||||||
|
|
||||||
- Code that was using `path.<index>` to access a `web::Path<(A, B, C)>`s elements now needs to use
|
* Code that was using `path.<index>` to access a `web::Path<(A, B, C)>`s elements now needs to use
|
||||||
destructuring or `.into_inner()`. For example:
|
destructuring or `.into_inner()`. For example:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
@@ -71,35 +71,35 @@
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- `middleware::NormalizePath` can now also be configured to trim trailing slashes instead of always keeping one.
|
* `middleware::NormalizePath` can now also be configured to trim trailing slashes instead of always keeping one.
|
||||||
It will need `middleware::normalize::TrailingSlash` when being constructed with `NormalizePath::new(...)`,
|
It will need `middleware::normalize::TrailingSlash` when being constructed with `NormalizePath::new(...)`,
|
||||||
or for an easier migration you can replace `wrap(middleware::NormalizePath)` with `wrap(middleware::NormalizePath::new(TrailingSlash::MergeOnly))`.
|
or for an easier migration you can replace `wrap(middleware::NormalizePath)` with `wrap(middleware::NormalizePath::new(TrailingSlash::MergeOnly))`.
|
||||||
|
|
||||||
- `HttpServer::maxconn` is renamed to the more expressive `HttpServer::max_connections`.
|
* `HttpServer::maxconn` is renamed to the more expressive `HttpServer::max_connections`.
|
||||||
|
|
||||||
- `HttpServer::maxconnrate` is renamed to the more expressive `HttpServer::max_connection_rate`.
|
* `HttpServer::maxconnrate` is renamed to the more expressive `HttpServer::max_connection_rate`.
|
||||||
|
|
||||||
|
|
||||||
## 2.0.0
|
## 2.0.0
|
||||||
|
|
||||||
- `HttpServer::start()` renamed to `HttpServer::run()`. It also possible to
|
* `HttpServer::start()` renamed to `HttpServer::run()`. It also possible to
|
||||||
`.await` on `run` method result, in that case it awaits server exit.
|
`.await` on `run` method result, in that case it awaits server exit.
|
||||||
|
|
||||||
- `App::register_data()` renamed to `App::app_data()` and accepts any type `T: 'static`.
|
* `App::register_data()` renamed to `App::app_data()` and accepts any type `T: 'static`.
|
||||||
Stored data is available via `HttpRequest::app_data()` method at runtime.
|
Stored data is available via `HttpRequest::app_data()` method at runtime.
|
||||||
|
|
||||||
- Extractor configuration must be registered with `App::app_data()` instead of `App::data()`
|
* Extractor configuration must be registered with `App::app_data()` instead of `App::data()`
|
||||||
|
|
||||||
- Sync handlers has been removed. `.to_async()` method has been renamed to `.to()`
|
* Sync handlers has been removed. `.to_async()` method has been renamed to `.to()`
|
||||||
replace `fn` with `async fn` to convert sync handler to async
|
replace `fn` with `async fn` to convert sync handler to async
|
||||||
|
|
||||||
- `actix_http_test::TestServer` moved to `actix_web::test` module. To start
|
* `actix_http_test::TestServer` moved to `actix_web::test` module. To start
|
||||||
test server use `test::start()` or `test_start_with_config()` methods
|
test server use `test::start()` or `test_start_with_config()` methods
|
||||||
|
|
||||||
- `ResponseError` trait has been reafctored. `ResponseError::error_response()` renders
|
* `ResponseError` trait has been reafctored. `ResponseError::error_response()` renders
|
||||||
http response.
|
http response.
|
||||||
|
|
||||||
- Feature `rust-tls` renamed to `rustls`
|
* Feature `rust-tls` renamed to `rustls`
|
||||||
|
|
||||||
instead of
|
instead of
|
||||||
|
|
||||||
@@ -113,7 +113,7 @@
|
|||||||
actix-web = { version = "2.0.0", features = ["rustls"] }
|
actix-web = { version = "2.0.0", features = ["rustls"] }
|
||||||
```
|
```
|
||||||
|
|
||||||
- Feature `ssl` renamed to `openssl`
|
* Feature `ssl` renamed to `openssl`
|
||||||
|
|
||||||
instead of
|
instead of
|
||||||
|
|
||||||
@@ -126,11 +126,11 @@
|
|||||||
```rust
|
```rust
|
||||||
actix-web = { version = "2.0.0", features = ["openssl"] }
|
actix-web = { version = "2.0.0", features = ["openssl"] }
|
||||||
```
|
```
|
||||||
- `Cors` builder now requires that you call `.finish()` to construct the middleware
|
* `Cors` builder now requires that you call `.finish()` to construct the middleware
|
||||||
|
|
||||||
## 1.0.1
|
## 1.0.1
|
||||||
|
|
||||||
- Cors middleware has been moved to `actix-cors` crate
|
* Cors middleware has been moved to `actix-cors` crate
|
||||||
|
|
||||||
instead of
|
instead of
|
||||||
|
|
||||||
@@ -144,7 +144,7 @@
|
|||||||
use actix_cors::Cors;
|
use actix_cors::Cors;
|
||||||
```
|
```
|
||||||
|
|
||||||
- Identity middleware has been moved to `actix-identity` crate
|
* Identity middleware has been moved to `actix-identity` crate
|
||||||
|
|
||||||
instead of
|
instead of
|
||||||
|
|
||||||
@@ -161,7 +161,7 @@
|
|||||||
|
|
||||||
## 1.0.0
|
## 1.0.0
|
||||||
|
|
||||||
- Extractor configuration. In version 1.0 this is handled with the new `Data` mechanism for both setting and retrieving the configuration
|
* Extractor configuration. In version 1.0 this is handled with the new `Data` mechanism for both setting and retrieving the configuration
|
||||||
|
|
||||||
instead of
|
instead of
|
||||||
|
|
||||||
@@ -219,7 +219,7 @@
|
|||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
- Resource registration. 1.0 version uses generalized resource
|
* Resource registration. 1.0 version uses generalized resource
|
||||||
registration via `.service()` method.
|
registration via `.service()` method.
|
||||||
|
|
||||||
instead of
|
instead of
|
||||||
@@ -239,7 +239,7 @@
|
|||||||
.route(web::post().to(post_handler))
|
.route(web::post().to(post_handler))
|
||||||
```
|
```
|
||||||
|
|
||||||
- Scope registration.
|
* Scope registration.
|
||||||
|
|
||||||
instead of
|
instead of
|
||||||
|
|
||||||
@@ -263,7 +263,7 @@
|
|||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
- `.with()`, `.with_async()` registration methods have been renamed to `.to()` and `.to_async()`.
|
* `.with()`, `.with_async()` registration methods have been renamed to `.to()` and `.to_async()`.
|
||||||
|
|
||||||
instead of
|
instead of
|
||||||
|
|
||||||
@@ -277,7 +277,7 @@
|
|||||||
App.new().service(web::resource("/welcome").to(welcome))
|
App.new().service(web::resource("/welcome").to(welcome))
|
||||||
```
|
```
|
||||||
|
|
||||||
- Passing arguments to handler with extractors, multiple arguments are allowed
|
* Passing arguments to handler with extractors, multiple arguments are allowed
|
||||||
|
|
||||||
instead of
|
instead of
|
||||||
|
|
||||||
@@ -295,7 +295,7 @@
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- `.f()`, `.a()` and `.h()` handler registration methods have been removed.
|
* `.f()`, `.a()` and `.h()` handler registration methods have been removed.
|
||||||
Use `.to()` for handlers and `.to_async()` for async handlers. Handler function
|
Use `.to()` for handlers and `.to_async()` for async handlers. Handler function
|
||||||
must use extractors.
|
must use extractors.
|
||||||
|
|
||||||
@@ -311,7 +311,7 @@
|
|||||||
App.new().service(web::resource("/welcome").to(welcome))
|
App.new().service(web::resource("/welcome").to(welcome))
|
||||||
```
|
```
|
||||||
|
|
||||||
- `HttpRequest` does not provide access to request's payload stream.
|
* `HttpRequest` does not provide access to request's payload stream.
|
||||||
|
|
||||||
instead of
|
instead of
|
||||||
|
|
||||||
@@ -341,7 +341,7 @@
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- `State` is now `Data`. You register Data during the App initialization process
|
* `State` is now `Data`. You register Data during the App initialization process
|
||||||
and then access it from handlers either using a Data extractor or using
|
and then access it from handlers either using a Data extractor or using
|
||||||
HttpRequest's api.
|
HttpRequest's api.
|
||||||
|
|
||||||
@@ -377,7 +377,7 @@
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
- AsyncResponder is removed, use `.to_async()` registration method and `impl Future<>` as result type.
|
* AsyncResponder is removed, use `.to_async()` registration method and `impl Future<>` as result type.
|
||||||
|
|
||||||
instead of
|
instead of
|
||||||
|
|
||||||
@@ -393,7 +393,7 @@
|
|||||||
.. simply omit AsyncResponder and the corresponding responder() finish method
|
.. simply omit AsyncResponder and the corresponding responder() finish method
|
||||||
|
|
||||||
|
|
||||||
- Middleware
|
* Middleware
|
||||||
|
|
||||||
instead of
|
instead of
|
||||||
|
|
||||||
@@ -410,7 +410,7 @@
|
|||||||
.route("/index.html", web::get().to(index));
|
.route("/index.html", web::get().to(index));
|
||||||
```
|
```
|
||||||
|
|
||||||
- `HttpRequest::body()`, `HttpRequest::urlencoded()`, `HttpRequest::json()`, `HttpRequest::multipart()`
|
* `HttpRequest::body()`, `HttpRequest::urlencoded()`, `HttpRequest::json()`, `HttpRequest::multipart()`
|
||||||
method have been removed. Use `Bytes`, `String`, `Form`, `Json`, `Multipart` extractors instead.
|
method have been removed. Use `Bytes`, `String`, `Form`, `Json`, `Multipart` extractors instead.
|
||||||
|
|
||||||
instead of
|
instead of
|
||||||
@@ -432,9 +432,9 @@
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- `actix_web::server` module has been removed. To start http server use `actix_web::HttpServer` type
|
* `actix_web::server` module has been removed. To start http server use `actix_web::HttpServer` type
|
||||||
|
|
||||||
- StaticFiles and NamedFile have been moved to a separate crate.
|
* StaticFiles and NamedFile have been moved to a separate crate.
|
||||||
|
|
||||||
instead of `use actix_web::fs::StaticFile`
|
instead of `use actix_web::fs::StaticFile`
|
||||||
|
|
||||||
@@ -444,20 +444,20 @@
|
|||||||
|
|
||||||
use `use actix_files::NamedFile`
|
use `use actix_files::NamedFile`
|
||||||
|
|
||||||
- Multipart has been moved to a separate crate.
|
* Multipart has been moved to a separate crate.
|
||||||
|
|
||||||
instead of `use actix_web::multipart::Multipart`
|
instead of `use actix_web::multipart::Multipart`
|
||||||
|
|
||||||
use `use actix_multipart::Multipart`
|
use `use actix_multipart::Multipart`
|
||||||
|
|
||||||
- Response compression is not enabled by default.
|
* Response compression is not enabled by default.
|
||||||
To enable, use `Compress` middleware, `App::new().wrap(Compress::default())`.
|
To enable, use `Compress` middleware, `App::new().wrap(Compress::default())`.
|
||||||
|
|
||||||
- Session middleware moved to actix-session crate
|
* Session middleware moved to actix-session crate
|
||||||
|
|
||||||
- Actors support have been moved to `actix-web-actors` crate
|
* Actors support have been moved to `actix-web-actors` crate
|
||||||
|
|
||||||
- Custom Error
|
* Custom Error
|
||||||
|
|
||||||
Instead of error_response method alone, ResponseError now provides two methods: error_response and render_response respectively. Where, error_response creates the error response and render_response returns the error response to the caller.
|
Instead of error_response method alone, ResponseError now provides two methods: error_response and render_response respectively. Where, error_response creates the error response and render_response returns the error response to the caller.
|
||||||
|
|
||||||
@@ -471,7 +471,7 @@
|
|||||||
|
|
||||||
## 0.7.15
|
## 0.7.15
|
||||||
|
|
||||||
- The `' '` character is not percent decoded anymore before matching routes. If you need to use it in
|
* The `' '` character is not percent decoded anymore before matching routes. If you need to use it in
|
||||||
your routes, you should use `%20`.
|
your routes, you should use `%20`.
|
||||||
|
|
||||||
instead of
|
instead of
|
||||||
@@ -496,18 +496,18 @@
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- If you used `AsyncResult::async` you need to replace it with `AsyncResult::future`
|
* If you used `AsyncResult::async` you need to replace it with `AsyncResult::future`
|
||||||
|
|
||||||
|
|
||||||
## 0.7.4
|
## 0.7.4
|
||||||
|
|
||||||
- `Route::with_config()`/`Route::with_async_config()` always passes configuration objects as tuple
|
* `Route::with_config()`/`Route::with_async_config()` always passes configuration objects as tuple
|
||||||
even for handler with one parameter.
|
even for handler with one parameter.
|
||||||
|
|
||||||
|
|
||||||
## 0.7
|
## 0.7
|
||||||
|
|
||||||
- `HttpRequest` does not implement `Stream` anymore. If you need to read request payload
|
* `HttpRequest` does not implement `Stream` anymore. If you need to read request payload
|
||||||
use `HttpMessage::payload()` method.
|
use `HttpMessage::payload()` method.
|
||||||
|
|
||||||
instead of
|
instead of
|
||||||
@@ -533,10 +533,10 @@
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- [Middleware](https://actix.rs/actix-web/actix_web/middleware/trait.Middleware.html)
|
* [Middleware](https://actix.rs/actix-web/actix_web/middleware/trait.Middleware.html)
|
||||||
trait uses `&HttpRequest` instead of `&mut HttpRequest`.
|
trait uses `&HttpRequest` instead of `&mut HttpRequest`.
|
||||||
|
|
||||||
- Removed `Route::with2()` and `Route::with3()` use tuple of extractors instead.
|
* Removed `Route::with2()` and `Route::with3()` use tuple of extractors instead.
|
||||||
|
|
||||||
instead of
|
instead of
|
||||||
|
|
||||||
@@ -550,17 +550,17 @@
|
|||||||
fn index((query, json): (Query<..>, Json<MyStruct)) -> impl Responder {}
|
fn index((query, json): (Query<..>, Json<MyStruct)) -> impl Responder {}
|
||||||
```
|
```
|
||||||
|
|
||||||
- `Handler::handle()` uses `&self` instead of `&mut self`
|
* `Handler::handle()` uses `&self` instead of `&mut self`
|
||||||
|
|
||||||
- `Handler::handle()` accepts reference to `HttpRequest<_>` instead of value
|
* `Handler::handle()` accepts reference to `HttpRequest<_>` instead of value
|
||||||
|
|
||||||
- Removed deprecated `HttpServer::threads()`, use
|
* Removed deprecated `HttpServer::threads()`, use
|
||||||
[HttpServer::workers()](https://actix.rs/actix-web/actix_web/server/struct.HttpServer.html#method.workers) instead.
|
[HttpServer::workers()](https://actix.rs/actix-web/actix_web/server/struct.HttpServer.html#method.workers) instead.
|
||||||
|
|
||||||
- Renamed `client::ClientConnectorError::Connector` to
|
* Renamed `client::ClientConnectorError::Connector` to
|
||||||
`client::ClientConnectorError::Resolver`
|
`client::ClientConnectorError::Resolver`
|
||||||
|
|
||||||
- `Route::with()` does not return `ExtractorConfig`, to configure
|
* `Route::with()` does not return `ExtractorConfig`, to configure
|
||||||
extractor use `Route::with_config()`
|
extractor use `Route::with_config()`
|
||||||
|
|
||||||
instead of
|
instead of
|
||||||
@@ -589,26 +589,26 @@
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- `Route::with_async()` does not return `ExtractorConfig`, to configure
|
* `Route::with_async()` does not return `ExtractorConfig`, to configure
|
||||||
extractor use `Route::with_async_config()`
|
extractor use `Route::with_async_config()`
|
||||||
|
|
||||||
|
|
||||||
## 0.6
|
## 0.6
|
||||||
|
|
||||||
- `Path<T>` extractor return `ErrorNotFound` on failure instead of `ErrorBadRequest`
|
* `Path<T>` extractor return `ErrorNotFound` on failure instead of `ErrorBadRequest`
|
||||||
|
|
||||||
- `ws::Message::Close` now includes optional close reason.
|
* `ws::Message::Close` now includes optional close reason.
|
||||||
`ws::CloseCode::Status` and `ws::CloseCode::Empty` have been removed.
|
`ws::CloseCode::Status` and `ws::CloseCode::Empty` have been removed.
|
||||||
|
|
||||||
- `HttpServer::threads()` renamed to `HttpServer::workers()`.
|
* `HttpServer::threads()` renamed to `HttpServer::workers()`.
|
||||||
|
|
||||||
- `HttpServer::start_ssl()` and `HttpServer::start_tls()` deprecated.
|
* `HttpServer::start_ssl()` and `HttpServer::start_tls()` deprecated.
|
||||||
Use `HttpServer::bind_ssl()` and `HttpServer::bind_tls()` instead.
|
Use `HttpServer::bind_ssl()` and `HttpServer::bind_tls()` instead.
|
||||||
|
|
||||||
- `HttpRequest::extensions()` returns read only reference to the request's Extension
|
* `HttpRequest::extensions()` returns read only reference to the request's Extension
|
||||||
`HttpRequest::extensions_mut()` returns mutable reference.
|
`HttpRequest::extensions_mut()` returns mutable reference.
|
||||||
|
|
||||||
- Instead of
|
* Instead of
|
||||||
|
|
||||||
`use actix_web::middleware::{
|
`use actix_web::middleware::{
|
||||||
CookieSessionBackend, CookieSessionError, RequestSession,
|
CookieSessionBackend, CookieSessionError, RequestSession,
|
||||||
@@ -619,15 +619,15 @@
|
|||||||
`use actix_web::middleware::session{CookieSessionBackend, CookieSessionError,
|
`use actix_web::middleware::session{CookieSessionBackend, CookieSessionError,
|
||||||
RequestSession, Session, SessionBackend, SessionImpl, SessionStorage};`
|
RequestSession, Session, SessionBackend, SessionImpl, SessionStorage};`
|
||||||
|
|
||||||
- `FromRequest::from_request()` accepts mutable reference to a request
|
* `FromRequest::from_request()` accepts mutable reference to a request
|
||||||
|
|
||||||
- `FromRequest::Result` has to implement `Into<Reply<Self>>`
|
* `FromRequest::Result` has to implement `Into<Reply<Self>>`
|
||||||
|
|
||||||
- [`Responder::respond_to()`](
|
* [`Responder::respond_to()`](
|
||||||
https://actix.rs/actix-web/actix_web/trait.Responder.html#tymethod.respond_to)
|
https://actix.rs/actix-web/actix_web/trait.Responder.html#tymethod.respond_to)
|
||||||
is generic over `S`
|
is generic over `S`
|
||||||
|
|
||||||
- Use `Query` extractor instead of HttpRequest::query()`.
|
* Use `Query` extractor instead of HttpRequest::query()`.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
fn index(q: Query<HashMap<String, String>>) -> Result<..> {
|
fn index(q: Query<HashMap<String, String>>) -> Result<..> {
|
||||||
@@ -641,37 +641,37 @@
|
|||||||
let q = Query::<HashMap<String, String>>::extract(req);
|
let q = Query::<HashMap<String, String>>::extract(req);
|
||||||
```
|
```
|
||||||
|
|
||||||
- Websocket operations are implemented as `WsWriter` trait.
|
* Websocket operations are implemented as `WsWriter` trait.
|
||||||
you need to use `use actix_web::ws::WsWriter`
|
you need to use `use actix_web::ws::WsWriter`
|
||||||
|
|
||||||
|
|
||||||
## 0.5
|
## 0.5
|
||||||
|
|
||||||
- `HttpResponseBuilder::body()`, `.finish()`, `.json()`
|
* `HttpResponseBuilder::body()`, `.finish()`, `.json()`
|
||||||
methods return `HttpResponse` instead of `Result<HttpResponse>`
|
methods return `HttpResponse` instead of `Result<HttpResponse>`
|
||||||
|
|
||||||
- `actix_web::Method`, `actix_web::StatusCode`, `actix_web::Version`
|
* `actix_web::Method`, `actix_web::StatusCode`, `actix_web::Version`
|
||||||
moved to `actix_web::http` module
|
moved to `actix_web::http` module
|
||||||
|
|
||||||
- `actix_web::header` moved to `actix_web::http::header`
|
* `actix_web::header` moved to `actix_web::http::header`
|
||||||
|
|
||||||
- `NormalizePath` moved to `actix_web::http` module
|
* `NormalizePath` moved to `actix_web::http` module
|
||||||
|
|
||||||
- `HttpServer` moved to `actix_web::server`, added new `actix_web::server::new()` function,
|
* `HttpServer` moved to `actix_web::server`, added new `actix_web::server::new()` function,
|
||||||
shortcut for `actix_web::server::HttpServer::new()`
|
shortcut for `actix_web::server::HttpServer::new()`
|
||||||
|
|
||||||
- `DefaultHeaders` middleware does not use separate builder, all builder methods moved to type itself
|
* `DefaultHeaders` middleware does not use separate builder, all builder methods moved to type itself
|
||||||
|
|
||||||
- `StaticFiles::new()`'s show_index parameter removed, use `show_files_listing()` method instead.
|
* `StaticFiles::new()`'s show_index parameter removed, use `show_files_listing()` method instead.
|
||||||
|
|
||||||
- `CookieSessionBackendBuilder` removed, all methods moved to `CookieSessionBackend` type
|
* `CookieSessionBackendBuilder` removed, all methods moved to `CookieSessionBackend` type
|
||||||
|
|
||||||
- `actix_web::httpcodes` module is deprecated, `HttpResponse::Ok()`, `HttpResponse::Found()` and other `HttpResponse::XXX()`
|
* `actix_web::httpcodes` module is deprecated, `HttpResponse::Ok()`, `HttpResponse::Found()` and other `HttpResponse::XXX()`
|
||||||
functions should be used instead
|
functions should be used instead
|
||||||
|
|
||||||
- `ClientRequestBuilder::body()` returns `Result<_, actix_web::Error>`
|
* `ClientRequestBuilder::body()` returns `Result<_, actix_web::Error>`
|
||||||
instead of `Result<_, http::Error>`
|
instead of `Result<_, http::Error>`
|
||||||
|
|
||||||
- `Application` renamed to a `App`
|
* `Application` renamed to a `App`
|
||||||
|
|
||||||
- `actix_web::Reply`, `actix_web::Resource` moved to `actix_web::dev`
|
* `actix_web::Reply`, `actix_web::Resource` moved to `actix_web::dev`
|
||||||
|
64
README.md
64
README.md
@@ -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.16)
|
[](https://docs.rs/actix-web/4.0.0-beta.14)
|
||||||
[](https://blog.rust-lang.org/2021/05/06/Rust-1.52.0.html)
|
[](https://blog.rust-lang.org/2021/05/06/Rust-1.52.0.html)
|
||||||

|

|
||||||
[](https://deps.rs/crate/actix-web/4.0.0-beta.16)
|
[](https://deps.rs/crate/actix-web/4.0.0-beta.14)
|
||||||
<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)
|
||||||
@@ -21,25 +21,25 @@
|
|||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- Supports *HTTP/1.x* and *HTTP/2*
|
* Supports *HTTP/1.x* and *HTTP/2*
|
||||||
- Streaming and pipelining
|
* Streaming and pipelining
|
||||||
- Keep-alive and slow requests handling
|
* Keep-alive and slow requests handling
|
||||||
- Client/server [WebSockets](https://actix.rs/docs/websockets/) support
|
* Client/server [WebSockets](https://actix.rs/docs/websockets/) support
|
||||||
- Transparent content compression/decompression (br, gzip, deflate, zstd)
|
* Transparent content compression/decompression (br, gzip, deflate, zstd)
|
||||||
- Powerful [request routing](https://actix.rs/docs/url-dispatch/)
|
* Powerful [request routing](https://actix.rs/docs/url-dispatch/)
|
||||||
- Multipart streams
|
* Multipart streams
|
||||||
- Static assets
|
* Static assets
|
||||||
- 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.52+
|
* Runs on stable Rust 1.52+
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
- [Website & User Guide](https://actix.rs)
|
* [Website & User Guide](https://actix.rs)
|
||||||
- [Examples Repository](https://github.com/actix/examples)
|
* [Examples Repository](https://github.com/actix/examples)
|
||||||
- [API Documentation](https://docs.rs/actix-web)
|
* [API Documentation](https://docs.rs/actix-web)
|
||||||
- [API Documentation (master branch)](https://actix.rs/actix-web/actix_web)
|
* [API Documentation (master branch)](https://actix.rs/actix-web/actix_web)
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
|
|
||||||
@@ -71,18 +71,18 @@ async fn main() -> std::io::Result<()> {
|
|||||||
|
|
||||||
### More examples
|
### More examples
|
||||||
|
|
||||||
- [Basic Setup](https://github.com/actix/examples/tree/master/basics/basics/)
|
* [Basic Setup](https://github.com/actix/examples/tree/master/basics/basics/)
|
||||||
- [Application State](https://github.com/actix/examples/tree/master/basics/state/)
|
* [Application State](https://github.com/actix/examples/tree/master/basics/state/)
|
||||||
- [JSON Handling](https://github.com/actix/examples/tree/master/json/json/)
|
* [JSON Handling](https://github.com/actix/examples/tree/master/json/json/)
|
||||||
- [Multipart Streams](https://github.com/actix/examples/tree/master/forms/multipart/)
|
* [Multipart Streams](https://github.com/actix/examples/tree/master/forms/multipart/)
|
||||||
- [Diesel Integration](https://github.com/actix/examples/tree/master/database_interactions/diesel/)
|
* [Diesel Integration](https://github.com/actix/examples/tree/master/database_interactions/diesel/)
|
||||||
- [r2d2 Integration](https://github.com/actix/examples/tree/master/database_interactions/r2d2/)
|
* [r2d2 Integration](https://github.com/actix/examples/tree/master/database_interactions/r2d2/)
|
||||||
- [Simple WebSocket](https://github.com/actix/examples/tree/master/websockets/websocket/)
|
* [Simple WebSocket](https://github.com/actix/examples/tree/master/websockets/websocket/)
|
||||||
- [Tera Templates](https://github.com/actix/examples/tree/master/template_engines/tera/)
|
* [Tera Templates](https://github.com/actix/examples/tree/master/template_engines/tera/)
|
||||||
- [Askama Templates](https://github.com/actix/examples/tree/master/template_engines/askama/)
|
* [Askama Templates](https://github.com/actix/examples/tree/master/template_engines/askama/)
|
||||||
- [HTTPS using Rustls](https://github.com/actix/examples/tree/master/security/rustls/)
|
* [HTTPS using Rustls](https://github.com/actix/examples/tree/master/security/rustls/)
|
||||||
- [HTTPS using OpenSSL](https://github.com/actix/examples/tree/master/security/openssl/)
|
* [HTTPS using OpenSSL](https://github.com/actix/examples/tree/master/security/openssl/)
|
||||||
- [WebSocket Chat](https://github.com/actix/examples/tree/master/websockets/chat/)
|
* [WebSocket Chat](https://github.com/actix/examples/tree/master/websockets/chat/)
|
||||||
|
|
||||||
You may consider checking out
|
You may consider checking out
|
||||||
[this directory](https://github.com/actix/examples/tree/master/) for more examples.
|
[this directory](https://github.com/actix/examples/tree/master/) for more examples.
|
||||||
@@ -96,9 +96,9 @@ One of the fastest web frameworks available according to the
|
|||||||
|
|
||||||
This project is licensed under either of
|
This project is licensed under either of
|
||||||
|
|
||||||
- Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||||
[http://www.apache.org/licenses/LICENSE-2.0])
|
[http://www.apache.org/licenses/LICENSE-2.0])
|
||||||
- MIT license ([LICENSE-MIT](LICENSE-MIT) or
|
* MIT license ([LICENSE-MIT](LICENSE-MIT) or
|
||||||
[http://opensource.org/licenses/MIT])
|
[http://opensource.org/licenses/MIT])
|
||||||
|
|
||||||
at your option.
|
at your option.
|
||||||
|
@@ -3,47 +3,43 @@
|
|||||||
## Unreleased - 2021-xx-xx
|
## Unreleased - 2021-xx-xx
|
||||||
|
|
||||||
|
|
||||||
## 0.6.0-beta.11 - 2021-12-27
|
|
||||||
* No significant changes since `0.6.0-beta.10`.
|
|
||||||
|
|
||||||
|
|
||||||
## 0.6.0-beta.10 - 2021-12-11
|
## 0.6.0-beta.10 - 2021-12-11
|
||||||
- No significant changes since `0.6.0-beta.9`.
|
* No significant changes since `0.6.0-beta.9`.
|
||||||
|
|
||||||
|
|
||||||
## 0.6.0-beta.9 - 2021-11-22
|
## 0.6.0-beta.9 - 2021-11-22
|
||||||
- Add crate feature `experimental-io-uring`, enabling async file I/O to be utilized. This feature is only available on Linux OSes with recent kernel versions. This feature is semver-exempt. [#2408]
|
* Add crate feature `experimental-io-uring`, enabling async file I/O to be utilized. This feature is only available on Linux OSes with recent kernel versions. This feature is semver-exempt. [#2408]
|
||||||
- Add `NamedFile::open_async`. [#2408]
|
* Add `NamedFile::open_async`. [#2408]
|
||||||
- Fix 304 Not Modified responses to omit the Content-Length header, as per the spec. [#2453]
|
* Fix 304 Not Modified responses to omit the Content-Length header, as per the spec. [#2453]
|
||||||
- The `Responder` impl for `NamedFile` now has a boxed future associated type. [#2408]
|
* The `Responder` impl for `NamedFile` now has a boxed future associated type. [#2408]
|
||||||
- The `Service` impl for `NamedFileService` now has a boxed future associated type. [#2408]
|
* The `Service` impl for `NamedFileService` now has a boxed future associated type. [#2408]
|
||||||
- Add `impl Clone` for `FilesService`. [#2408]
|
* Add `impl Clone` for `FilesService`. [#2408]
|
||||||
|
|
||||||
[#2408]: https://github.com/actix/actix-web/pull/2408
|
[#2408]: https://github.com/actix/actix-web/pull/2408
|
||||||
[#2453]: https://github.com/actix/actix-web/pull/2453
|
[#2453]: https://github.com/actix/actix-web/pull/2453
|
||||||
|
|
||||||
|
|
||||||
## 0.6.0-beta.8 - 2021-10-20
|
## 0.6.0-beta.8 - 2021-10-20
|
||||||
- Minimum supported Rust version (MSRV) is now 1.52.
|
* 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.
|
||||||
|
|
||||||
|
|
||||||
## 0.6.0-beta.6 - 2021-06-26
|
## 0.6.0-beta.6 - 2021-06-26
|
||||||
- Added `Files::path_filter()`. [#2274]
|
* Added `Files::path_filter()`. [#2274]
|
||||||
- `Files::show_files_listing()` can now be used with `Files::index_file()` to show files listing as a fallback when the index file is not found. [#2228]
|
* `Files::show_files_listing()` can now be used with `Files::index_file()` to show files listing as a fallback when the index file is not found. [#2228]
|
||||||
|
|
||||||
[#2274]: https://github.com/actix/actix-web/pull/2274
|
[#2274]: https://github.com/actix/actix-web/pull/2274
|
||||||
[#2228]: https://github.com/actix/actix-web/pull/2228
|
[#2228]: https://github.com/actix/actix-web/pull/2228
|
||||||
|
|
||||||
|
|
||||||
## 0.6.0-beta.5 - 2021-06-17
|
## 0.6.0-beta.5 - 2021-06-17
|
||||||
- `NamedFile` now implements `ServiceFactory` and `HttpServiceFactory` making it much more useful in routing. For example, it can be used directly as a default service. [#2135]
|
* `NamedFile` now implements `ServiceFactory` and `HttpServiceFactory` making it much more useful in routing. For example, it can be used directly as a default service. [#2135]
|
||||||
- For symbolic links, `Content-Disposition` header no longer shows the filename of the original file. [#2156]
|
* For symbolic links, `Content-Disposition` header no longer shows the filename of the original file. [#2156]
|
||||||
- `Files::redirect_to_slash_directory()` now works as expected when used with `Files::show_files_listing()`. [#2225]
|
* `Files::redirect_to_slash_directory()` now works as expected when used with `Files::show_files_listing()`. [#2225]
|
||||||
- `application/{javascript, json, wasm}` mime type now have `inline` disposition by default. [#2257]
|
* `application/{javascript, json, wasm}` mime type now have `inline` disposition by default. [#2257]
|
||||||
|
|
||||||
[#2135]: https://github.com/actix/actix-web/pull/2135
|
[#2135]: https://github.com/actix/actix-web/pull/2135
|
||||||
[#2156]: https://github.com/actix/actix-web/pull/2156
|
[#2156]: https://github.com/actix/actix-web/pull/2156
|
||||||
@@ -52,130 +48,130 @@
|
|||||||
|
|
||||||
|
|
||||||
## 0.6.0-beta.4 - 2021-04-02
|
## 0.6.0-beta.4 - 2021-04-02
|
||||||
- Add support for `.guard` in `Files` to selectively filter `Files` services. [#2046]
|
* Add support for `.guard` in `Files` to selectively filter `Files` services. [#2046]
|
||||||
|
|
||||||
[#2046]: https://github.com/actix/actix-web/pull/2046
|
[#2046]: https://github.com/actix/actix-web/pull/2046
|
||||||
|
|
||||||
|
|
||||||
## 0.6.0-beta.3 - 2021-03-09
|
## 0.6.0-beta.3 - 2021-03-09
|
||||||
- No notable changes.
|
* No notable changes.
|
||||||
|
|
||||||
|
|
||||||
## 0.6.0-beta.2 - 2021-02-10
|
## 0.6.0-beta.2 - 2021-02-10
|
||||||
- Fix If-Modified-Since and If-Unmodified-Since to not compare using sub-second timestamps. [#1887]
|
* Fix If-Modified-Since and If-Unmodified-Since to not compare using sub-second timestamps. [#1887]
|
||||||
- Replace `v_htmlescape` with `askama_escape`. [#1953]
|
* Replace `v_htmlescape` with `askama_escape`. [#1953]
|
||||||
|
|
||||||
[#1887]: https://github.com/actix/actix-web/pull/1887
|
[#1887]: https://github.com/actix/actix-web/pull/1887
|
||||||
[#1953]: https://github.com/actix/actix-web/pull/1953
|
[#1953]: https://github.com/actix/actix-web/pull/1953
|
||||||
|
|
||||||
|
|
||||||
## 0.6.0-beta.1 - 2021-01-07
|
## 0.6.0-beta.1 - 2021-01-07
|
||||||
- `HttpRange::parse` now has its own error type.
|
* `HttpRange::parse` now has its own error type.
|
||||||
- Update `bytes` to `1.0`. [#1813]
|
* Update `bytes` to `1.0`. [#1813]
|
||||||
|
|
||||||
[#1813]: https://github.com/actix/actix-web/pull/1813
|
[#1813]: https://github.com/actix/actix-web/pull/1813
|
||||||
|
|
||||||
|
|
||||||
## 0.5.0 - 2020-12-26
|
## 0.5.0 - 2020-12-26
|
||||||
- Optionally support hidden files/directories. [#1811]
|
* Optionally support hidden files/directories. [#1811]
|
||||||
|
|
||||||
[#1811]: https://github.com/actix/actix-web/pull/1811
|
[#1811]: https://github.com/actix/actix-web/pull/1811
|
||||||
|
|
||||||
|
|
||||||
## 0.4.1 - 2020-11-24
|
## 0.4.1 - 2020-11-24
|
||||||
- Clarify order of parameters in `Files::new` and improve docs.
|
* Clarify order of parameters in `Files::new` and improve docs.
|
||||||
|
|
||||||
|
|
||||||
## 0.4.0 - 2020-10-06
|
## 0.4.0 - 2020-10-06
|
||||||
- Add `Files::prefer_utf8` option that adds UTF-8 charset on certain response types. [#1714]
|
* Add `Files::prefer_utf8` option that adds UTF-8 charset on certain response types. [#1714]
|
||||||
|
|
||||||
[#1714]: https://github.com/actix/actix-web/pull/1714
|
[#1714]: https://github.com/actix/actix-web/pull/1714
|
||||||
|
|
||||||
|
|
||||||
## 0.3.0 - 2020-09-11
|
## 0.3.0 - 2020-09-11
|
||||||
- No significant changes from 0.3.0-beta.1.
|
* No significant changes from 0.3.0-beta.1.
|
||||||
|
|
||||||
|
|
||||||
## 0.3.0-beta.1 - 2020-07-15
|
## 0.3.0-beta.1 - 2020-07-15
|
||||||
- Update `v_htmlescape` to 0.10
|
* Update `v_htmlescape` to 0.10
|
||||||
- Update `actix-web` and `actix-http` dependencies to beta.1
|
* Update `actix-web` and `actix-http` dependencies to beta.1
|
||||||
|
|
||||||
|
|
||||||
## 0.3.0-alpha.1 - 2020-05-23
|
## 0.3.0-alpha.1 - 2020-05-23
|
||||||
- Update `actix-web` and `actix-http` dependencies to alpha
|
* Update `actix-web` and `actix-http` dependencies to alpha
|
||||||
- Fix some typos in the docs
|
* Fix some typos in the docs
|
||||||
- Bump minimum supported Rust version to 1.40
|
* Bump minimum supported Rust version to 1.40
|
||||||
- Support sending Content-Length when Content-Range is specified [#1384]
|
* Support sending Content-Length when Content-Range is specified [#1384]
|
||||||
|
|
||||||
[#1384]: https://github.com/actix/actix-web/pull/1384
|
[#1384]: https://github.com/actix/actix-web/pull/1384
|
||||||
|
|
||||||
|
|
||||||
## 0.2.1 - 2019-12-22
|
## 0.2.1 - 2019-12-22
|
||||||
- Use the same format for file URLs regardless of platforms
|
* Use the same format for file URLs regardless of platforms
|
||||||
|
|
||||||
|
|
||||||
## 0.2.0 - 2019-12-20
|
## 0.2.0 - 2019-12-20
|
||||||
- Fix BodyEncoding trait import #1220
|
* Fix BodyEncoding trait import #1220
|
||||||
|
|
||||||
|
|
||||||
## 0.2.0-alpha.1 - 2019-12-07
|
## 0.2.0-alpha.1 - 2019-12-07
|
||||||
- Migrate to `std::future`
|
* Migrate to `std::future`
|
||||||
|
|
||||||
|
|
||||||
## 0.1.7 - 2019-11-06
|
## 0.1.7 - 2019-11-06
|
||||||
- Add an additional `filename*` param in the `Content-Disposition` header of
|
* Add an additional `filename*` param in the `Content-Disposition` header of
|
||||||
`actix_files::NamedFile` to be more compatible. (#1151)
|
`actix_files::NamedFile` to be more compatible. (#1151)
|
||||||
|
|
||||||
## 0.1.6 - 2019-10-14
|
## 0.1.6 - 2019-10-14
|
||||||
- Add option to redirect to a slash-ended path `Files` #1132
|
* Add option to redirect to a slash-ended path `Files` #1132
|
||||||
|
|
||||||
|
|
||||||
## 0.1.5 - 2019-10-08
|
## 0.1.5 - 2019-10-08
|
||||||
- Bump up `mime_guess` crate version to 2.0.1
|
* Bump up `mime_guess` crate version to 2.0.1
|
||||||
- Bump up `percent-encoding` crate version to 2.1
|
* Bump up `percent-encoding` crate version to 2.1
|
||||||
- Allow user defined request guards for `Files` #1113
|
* Allow user defined request guards for `Files` #1113
|
||||||
|
|
||||||
|
|
||||||
## 0.1.4 - 2019-07-20
|
## 0.1.4 - 2019-07-20
|
||||||
- Allow to disable `Content-Disposition` header #686
|
* Allow to disable `Content-Disposition` header #686
|
||||||
|
|
||||||
|
|
||||||
## 0.1.3 - 2019-06-28
|
## 0.1.3 - 2019-06-28
|
||||||
- Do not set `Content-Length` header, let actix-http set it #930
|
* Do not set `Content-Length` header, let actix-http set it #930
|
||||||
|
|
||||||
|
|
||||||
## 0.1.2 - 2019-06-13
|
## 0.1.2 - 2019-06-13
|
||||||
- Content-Length is 0 for NamedFile HEAD request #914
|
* Content-Length is 0 for NamedFile HEAD request #914
|
||||||
- Fix ring dependency from actix-web default features for #741
|
* Fix ring dependency from actix-web default features for #741
|
||||||
|
|
||||||
|
|
||||||
## 0.1.1 - 2019-06-01
|
## 0.1.1 - 2019-06-01
|
||||||
- Static files are incorrectly served as both chunked and with length #812
|
* Static files are incorrectly served as both chunked and with length #812
|
||||||
|
|
||||||
|
|
||||||
## 0.1.0 - 2019-05-25
|
## 0.1.0 - 2019-05-25
|
||||||
- NamedFile last-modified check always fails due to nano-seconds in file modified date #820
|
* NamedFile last-modified check always fails due to nano-seconds in file modified date #820
|
||||||
|
|
||||||
|
|
||||||
## 0.1.0-beta.4 - 2019-05-12
|
## 0.1.0-beta.4 - 2019-05-12
|
||||||
- Update actix-web to beta.4
|
* Update actix-web to beta.4
|
||||||
|
|
||||||
|
|
||||||
## 0.1.0-beta.1 - 2019-04-20
|
## 0.1.0-beta.1 - 2019-04-20
|
||||||
- Update actix-web to beta.1
|
* Update actix-web to beta.1
|
||||||
|
|
||||||
|
|
||||||
## 0.1.0-alpha.6 - 2019-04-14
|
## 0.1.0-alpha.6 - 2019-04-14
|
||||||
- Update actix-web to alpha6
|
* Update actix-web to alpha6
|
||||||
|
|
||||||
|
|
||||||
## 0.1.0-alpha.4 - 2019-04-08
|
## 0.1.0-alpha.4 - 2019-04-08
|
||||||
- Update actix-web to alpha4
|
* Update actix-web to alpha4
|
||||||
|
|
||||||
|
|
||||||
## 0.1.0-alpha.2 - 2019-04-02
|
## 0.1.0-alpha.2 - 2019-04-02
|
||||||
- Add default handler support
|
* Add default handler support
|
||||||
|
|
||||||
|
|
||||||
## 0.1.0-alpha.1 - 2019-03-28
|
## 0.1.0-alpha.1 - 2019-03-28
|
||||||
- Initial impl
|
* Initial impl
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "actix-files"
|
name = "actix-files"
|
||||||
version = "0.6.0-beta.11"
|
version = "0.6.0-beta.10"
|
||||||
authors = [
|
authors = [
|
||||||
"Nikolay Kim <fafhrd91@gmail.com>",
|
"Nikolay Kim <fafhrd91@gmail.com>",
|
||||||
"fakeshadow <24548779@qq.com>",
|
"fakeshadow <24548779@qq.com>",
|
||||||
@@ -22,10 +22,10 @@ path = "src/lib.rs"
|
|||||||
experimental-io-uring = ["actix-web/experimental-io-uring", "tokio-uring"]
|
experimental-io-uring = ["actix-web/experimental-io-uring", "tokio-uring"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix-http = "3.0.0-beta.17"
|
actix-http = "3.0.0-beta.15"
|
||||||
actix-service = "2"
|
actix-service = "2"
|
||||||
actix-utils = "3"
|
actix-utils = "3"
|
||||||
actix-web = { version = "4.0.0-beta.16", default-features = false }
|
actix-web = { version = "4.0.0-beta.14", default-features = false }
|
||||||
|
|
||||||
askama_escape = "0.10"
|
askama_escape = "0.10"
|
||||||
bitflags = "1"
|
bitflags = "1"
|
||||||
@@ -43,5 +43,5 @@ tokio-uring = { version = "0.1", optional = true }
|
|||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
actix-rt = "2.2"
|
actix-rt = "2.2"
|
||||||
actix-test = "0.1.0-beta.10"
|
actix-test = "0.1.0-beta.8"
|
||||||
actix-web = "4.0.0-beta.16"
|
actix-web = "4.0.0-beta.14"
|
||||||
|
@@ -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.11)
|
[](https://docs.rs/actix-files/0.6.0-beta.10)
|
||||||
[](https://blog.rust-lang.org/2021/05/06/Rust-1.52.0.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.11)
|
[](https://deps.rs/crate/actix-files/0.6.0-beta.10)
|
||||||
[](https://crates.io/crates/actix-files)
|
[](https://crates.io/crates/actix-files)
|
||||||
[](https://discord.gg/NWpN5mmg3x)
|
[](https://discord.gg/NWpN5mmg3x)
|
||||||
|
|
||||||
|
@@ -3,132 +3,126 @@
|
|||||||
## Unreleased - 2021-xx-xx
|
## Unreleased - 2021-xx-xx
|
||||||
|
|
||||||
|
|
||||||
## 3.0.0-beta.10 - 2021-12-27
|
|
||||||
- Update `actix-server` to `2.0.0-rc.2`. [#2550]
|
|
||||||
|
|
||||||
[#2550]: https://github.com/actix/actix-web/pull/2550
|
|
||||||
|
|
||||||
|
|
||||||
## 3.0.0-beta.9 - 2021-12-11
|
## 3.0.0-beta.9 - 2021-12-11
|
||||||
- No significant changes since `3.0.0-beta.8`.
|
* No significant changes since `3.0.0-beta.8`.
|
||||||
|
|
||||||
|
|
||||||
## 3.0.0-beta.8 - 2021-11-30
|
## 3.0.0-beta.8 - 2021-11-30
|
||||||
- Update `actix-tls` to `3.0.0-rc.1`. [#2474]
|
* Update `actix-tls` to `3.0.0-rc.1`. [#2474]
|
||||||
|
|
||||||
[#2474]: https://github.com/actix/actix-web/pull/2474
|
[#2474]: https://github.com/actix/actix-web/pull/2474
|
||||||
|
|
||||||
|
|
||||||
## 3.0.0-beta.7 - 2021-11-22
|
## 3.0.0-beta.7 - 2021-11-22
|
||||||
- Fix compatibility with experimental `io-uring` feature of `actix-rt`. [#2408]
|
* Fix compatibility with experimental `io-uring` feature of `actix-rt`. [#2408]
|
||||||
|
|
||||||
[#2408]: https://github.com/actix/actix-web/pull/2408
|
[#2408]: https://github.com/actix/actix-web/pull/2408
|
||||||
|
|
||||||
|
|
||||||
## 3.0.0-beta.6 - 2021-11-15
|
## 3.0.0-beta.6 - 2021-11-15
|
||||||
- `TestServer::stop` is now async and will wait for the server and system to shutdown. [#2442]
|
* `TestServer::stop` is now async and will wait for the server and system to shutdown. [#2442]
|
||||||
- Update `actix-server` to `2.0.0-beta.9`. [#2442]
|
* Update `actix-server` to `2.0.0-beta.9`. [#2442]
|
||||||
- Minimum supported Rust version (MSRV) is now 1.52.
|
* Minimum supported Rust version (MSRV) is now 1.52.
|
||||||
|
|
||||||
[#2442]: https://github.com/actix/actix-web/pull/2442
|
[#2442]: https://github.com/actix/actix-web/pull/2442
|
||||||
|
|
||||||
|
|
||||||
## 3.0.0-beta.5 - 2021-09-09
|
## 3.0.0-beta.5 - 2021-09-09
|
||||||
- Minimum supported Rust version (MSRV) is now 1.51.
|
* Minimum supported Rust version (MSRV) is now 1.51.
|
||||||
|
|
||||||
|
|
||||||
## 3.0.0-beta.4 - 2021-04-02
|
## 3.0.0-beta.4 - 2021-04-02
|
||||||
- Added `TestServer::client_headers` method. [#2097]
|
* Added `TestServer::client_headers` method. [#2097]
|
||||||
|
|
||||||
[#2097]: https://github.com/actix/actix-web/pull/2097
|
[#2097]: https://github.com/actix/actix-web/pull/2097
|
||||||
|
|
||||||
|
|
||||||
## 3.0.0-beta.3 - 2021-03-09
|
## 3.0.0-beta.3 - 2021-03-09
|
||||||
- No notable changes.
|
* No notable changes.
|
||||||
|
|
||||||
|
|
||||||
## 3.0.0-beta.2 - 2021-02-10
|
## 3.0.0-beta.2 - 2021-02-10
|
||||||
- No notable changes.
|
* No notable changes.
|
||||||
|
|
||||||
|
|
||||||
## 3.0.0-beta.1 - 2021-01-07
|
## 3.0.0-beta.1 - 2021-01-07
|
||||||
- Update `bytes` to `1.0`. [#1813]
|
* Update `bytes` to `1.0`. [#1813]
|
||||||
|
|
||||||
[#1813]: https://github.com/actix/actix-web/pull/1813
|
[#1813]: https://github.com/actix/actix-web/pull/1813
|
||||||
|
|
||||||
|
|
||||||
## 2.1.0 - 2020-11-25
|
## 2.1.0 - 2020-11-25
|
||||||
- Add ability to set address for `TestServer`. [#1645]
|
* Add ability to set address for `TestServer`. [#1645]
|
||||||
- Upgrade `base64` to `0.13`.
|
* Upgrade `base64` to `0.13`.
|
||||||
- Upgrade `serde_urlencoded` to `0.7`. [#1773]
|
* Upgrade `serde_urlencoded` to `0.7`. [#1773]
|
||||||
|
|
||||||
[#1773]: https://github.com/actix/actix-web/pull/1773
|
[#1773]: https://github.com/actix/actix-web/pull/1773
|
||||||
[#1645]: https://github.com/actix/actix-web/pull/1645
|
[#1645]: https://github.com/actix/actix-web/pull/1645
|
||||||
|
|
||||||
|
|
||||||
## 2.0.0 - 2020-09-11
|
## 2.0.0 - 2020-09-11
|
||||||
- Update actix-codec and actix-utils dependencies.
|
* Update actix-codec and actix-utils dependencies.
|
||||||
|
|
||||||
|
|
||||||
## 2.0.0-alpha.1 - 2020-05-23
|
## 2.0.0-alpha.1 - 2020-05-23
|
||||||
- Update the `time` dependency to 0.2.7
|
* Update the `time` dependency to 0.2.7
|
||||||
- Update `actix-connect` dependency to 2.0.0-alpha.2
|
* Update `actix-connect` dependency to 2.0.0-alpha.2
|
||||||
- Make `test_server` `async` fn.
|
* Make `test_server` `async` fn.
|
||||||
- Bump minimum supported Rust version to 1.40
|
* Bump minimum supported Rust version to 1.40
|
||||||
- Replace deprecated `net2` crate with `socket2`
|
* Replace deprecated `net2` crate with `socket2`
|
||||||
- Update `base64` dependency to 0.12
|
* Update `base64` dependency to 0.12
|
||||||
- Update `env_logger` dependency to 0.7
|
* Update `env_logger` dependency to 0.7
|
||||||
|
|
||||||
## 1.0.0 - 2019-12-13
|
## 1.0.0 - 2019-12-13
|
||||||
- Replaced `TestServer::start()` with `test_server()`
|
* Replaced `TestServer::start()` with `test_server()`
|
||||||
|
|
||||||
|
|
||||||
## 1.0.0-alpha.3 - 2019-12-07
|
## 1.0.0-alpha.3 - 2019-12-07
|
||||||
- Migrate to `std::future`
|
* Migrate to `std::future`
|
||||||
|
|
||||||
|
|
||||||
## 0.2.5 - 2019-09-17
|
## 0.2.5 - 2019-09-17
|
||||||
- Update serde_urlencoded to "0.6.1"
|
* Update serde_urlencoded to "0.6.1"
|
||||||
- Increase TestServerRuntime timeouts from 500ms to 3000ms
|
* Increase TestServerRuntime timeouts from 500ms to 3000ms
|
||||||
- Do not override current `System`
|
* Do not override current `System`
|
||||||
|
|
||||||
|
|
||||||
## 0.2.4 - 2019-07-18
|
## 0.2.4 - 2019-07-18
|
||||||
- Update actix-server to 0.6
|
* Update actix-server to 0.6
|
||||||
|
|
||||||
|
|
||||||
## 0.2.3 - 2019-07-16
|
## 0.2.3 - 2019-07-16
|
||||||
- Add `delete`, `options`, `patch` methods to `TestServerRunner`
|
* Add `delete`, `options`, `patch` methods to `TestServerRunner`
|
||||||
|
|
||||||
|
|
||||||
## 0.2.2 - 2019-06-16
|
## 0.2.2 - 2019-06-16
|
||||||
- Add .put() and .sput() methods
|
* Add .put() and .sput() methods
|
||||||
|
|
||||||
|
|
||||||
## 0.2.1 - 2019-06-05
|
## 0.2.1 - 2019-06-05
|
||||||
- Add license files
|
* Add license files
|
||||||
|
|
||||||
|
|
||||||
## 0.2.0 - 2019-05-12
|
## 0.2.0 - 2019-05-12
|
||||||
- Update awc and actix-http deps
|
* Update awc and actix-http deps
|
||||||
|
|
||||||
|
|
||||||
## 0.1.1 - 2019-04-24
|
## 0.1.1 - 2019-04-24
|
||||||
- Always make new connection for http client
|
* Always make new connection for http client
|
||||||
|
|
||||||
|
|
||||||
## 0.1.0 - 2019-04-16
|
## 0.1.0 - 2019-04-16
|
||||||
- No changes
|
* No changes
|
||||||
|
|
||||||
|
|
||||||
## 0.1.0-alpha.3 - 2019-04-02
|
## 0.1.0-alpha.3 - 2019-04-02
|
||||||
- Request functions accept path #743
|
* Request functions accept path #743
|
||||||
|
|
||||||
|
|
||||||
## 0.1.0-alpha.2 - 2019-03-29
|
## 0.1.0-alpha.2 - 2019-03-29
|
||||||
- Added TestServerRuntime::load_body() method
|
* Added TestServerRuntime::load_body() method
|
||||||
- Update actix-http and awc libraries
|
* Update actix-http and awc libraries
|
||||||
|
|
||||||
|
|
||||||
## 0.1.0-alpha.1 - 2019-03-28
|
## 0.1.0-alpha.1 - 2019-03-28
|
||||||
- Initial impl
|
* Initial impl
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "actix-http-test"
|
name = "actix-http-test"
|
||||||
version = "3.0.0-beta.10"
|
version = "3.0.0-beta.9"
|
||||||
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
||||||
description = "Various helpers for Actix applications to use during testing"
|
description = "Various helpers for Actix applications to use during testing"
|
||||||
keywords = ["http", "web", "framework", "async", "futures"]
|
keywords = ["http", "web", "framework", "async", "futures"]
|
||||||
@@ -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.1"
|
actix-codec = "0.4.1"
|
||||||
actix-tls = "3.0.0"
|
actix-tls = "3.0.0-rc.1"
|
||||||
actix-utils = "3.0.0"
|
actix-utils = "3.0.0"
|
||||||
actix-rt = "2.2"
|
actix-rt = "2.2"
|
||||||
actix-server = "2.0.0-rc.2"
|
actix-server = "2.0.0-rc.1"
|
||||||
awc = { version = "3.0.0-beta.15", default-features = false }
|
awc = { version = "3.0.0-beta.13", default-features = false }
|
||||||
|
|
||||||
base64 = "0.13"
|
base64 = "0.13"
|
||||||
bytes = "1"
|
bytes = "1"
|
||||||
@@ -48,8 +48,8 @@ serde_json = "1.0"
|
|||||||
slab = "0.4"
|
slab = "0.4"
|
||||||
serde_urlencoded = "0.7"
|
serde_urlencoded = "0.7"
|
||||||
tls-openssl = { version = "0.10.9", package = "openssl", optional = true }
|
tls-openssl = { version = "0.10.9", package = "openssl", optional = true }
|
||||||
tokio = { version = "1.8", features = ["sync"] }
|
tokio = { version = "1.2", features = ["sync"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
actix-web = { version = "4.0.0-beta.16", default-features = false, features = ["cookies"] }
|
actix-web = { version = "4.0.0-beta.14", default-features = false, features = ["cookies"] }
|
||||||
actix-http = "3.0.0-beta.17"
|
actix-http = "3.0.0-beta.15"
|
||||||
|
@@ -3,11 +3,11 @@
|
|||||||
> Various helpers for Actix applications to use during testing.
|
> Various helpers for Actix applications to use during testing.
|
||||||
|
|
||||||
[](https://crates.io/crates/actix-http-test)
|
[](https://crates.io/crates/actix-http-test)
|
||||||
[](https://docs.rs/actix-http-test/3.0.0-beta.10)
|
[](https://docs.rs/actix-http-test/3.0.0-beta.9)
|
||||||
[](https://blog.rust-lang.org/2021/05/06/Rust-1.52.0.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.10)
|
[](https://deps.rs/crate/actix-http-test/3.0.0-beta.9)
|
||||||
[](https://crates.io/crates/actix-http-test)
|
[](https://crates.io/crates/actix-http-test)
|
||||||
[](https://discord.gg/NWpN5mmg3x)
|
[](https://discord.gg/NWpN5mmg3x)
|
||||||
|
|
||||||
|
@@ -12,7 +12,7 @@ use std::{net, thread, time::Duration};
|
|||||||
|
|
||||||
use actix_codec::{AsyncRead, AsyncWrite, Framed};
|
use actix_codec::{AsyncRead, AsyncWrite, Framed};
|
||||||
use actix_rt::{net::TcpStream, System};
|
use actix_rt::{net::TcpStream, System};
|
||||||
use actix_server::{Server, ServerServiceFactory};
|
use actix_server::{Server, ServiceFactory};
|
||||||
use awc::{
|
use awc::{
|
||||||
error::PayloadError, http::header::HeaderMap, ws, Client, ClientRequest, ClientResponse,
|
error::PayloadError, http::header::HeaderMap, ws, Client, ClientRequest, ClientResponse,
|
||||||
Connector,
|
Connector,
|
||||||
@@ -51,13 +51,13 @@ use tokio::sync::mpsc;
|
|||||||
/// assert!(response.status().is_success());
|
/// assert!(response.status().is_success());
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub async fn test_server<F: ServerServiceFactory<TcpStream>>(factory: F) -> TestServer {
|
pub async fn test_server<F: ServiceFactory<TcpStream>>(factory: F) -> TestServer {
|
||||||
let tcp = net::TcpListener::bind("127.0.0.1:0").unwrap();
|
let tcp = net::TcpListener::bind("127.0.0.1:0").unwrap();
|
||||||
test_server_with_addr(tcp, factory).await
|
test_server_with_addr(tcp, factory).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Start [`test server`](test_server()) on an existing address binding.
|
/// Start [`test server`](test_server()) on an existing address binding.
|
||||||
pub async fn test_server_with_addr<F: ServerServiceFactory<TcpStream>>(
|
pub async fn test_server_with_addr<F: ServiceFactory<TcpStream>>(
|
||||||
tcp: net::TcpListener,
|
tcp: net::TcpListener,
|
||||||
factory: F,
|
factory: F,
|
||||||
) -> TestServer {
|
) -> TestServer {
|
||||||
@@ -107,7 +107,7 @@ pub async fn test_server_with_addr<F: ServerServiceFactory<TcpStream>>(
|
|||||||
Connector::new()
|
Connector::new()
|
||||||
.conn_lifetime(Duration::from_secs(0))
|
.conn_lifetime(Duration::from_secs(0))
|
||||||
.timeout(Duration::from_millis(30000))
|
.timeout(Duration::from_millis(30000))
|
||||||
.openssl(builder.build())
|
.ssl(builder.build())
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(not(feature = "openssl"))]
|
#[cfg(not(feature = "openssl"))]
|
||||||
|
@@ -1,79 +1,52 @@
|
|||||||
# Changes
|
# Changes
|
||||||
|
|
||||||
## Unreleased - 2021-xx-xx
|
## Unreleased - 2021-xx-xx
|
||||||
|
|
||||||
|
|
||||||
## 3.0.0-beta.17 - 2021-12-27
|
|
||||||
### Changes
|
|
||||||
- `HeaderMap::get_all` now returns a `std::slice::Iter`. [#2527]
|
|
||||||
- `Payload` inner fields are now named. [#2545]
|
|
||||||
- `impl Stream` for `Payload` no longer requires the `Stream` variant be `Unpin`. [#2545]
|
|
||||||
- `impl Future` for `h1::SendResponse` no longer requires the body type be `Unpin`. [#2545]
|
|
||||||
- `impl Stream` for `encoding::Decoder` no longer requires the stream type be `Unpin`. [#2545]
|
|
||||||
- Rename `PayloadStream` to `BoxedPayloadStream`. [#2545]
|
|
||||||
|
|
||||||
### Removed
|
|
||||||
- `h1::Payload::readany`. [#2545]
|
|
||||||
|
|
||||||
[#2527]: https://github.com/actix/actix-web/pull/2527
|
|
||||||
[#2545]: https://github.com/actix/actix-web/pull/2545
|
|
||||||
|
|
||||||
|
|
||||||
## 3.0.0-beta.16 - 2021-12-17
|
|
||||||
### Added
|
|
||||||
- New method on `MessageBody` trait, `try_into_bytes`, with default implementation, for optimizations on body types that complete in exactly one poll. Replaces `is_complete_body` and `take_complete_body`. [#2522]
|
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- Rename trait `IntoHeaderPair => TryIntoHeaderPair`. [#2510]
|
* Rename trait `IntoHeaderPair => TryIntoHeaderPair`. [#2510]
|
||||||
- Rename `TryIntoHeaderPair::{try_into_header_pair => try_into_pair}`. [#2510]
|
* Rename `TryIntoHeaderPair::{try_into_header_pair => try_into_pair}`. [#2510]
|
||||||
- Rename trait `IntoHeaderValue => TryIntoHeaderValue`. [#2510]
|
* Rename trait `IntoHeaderValue => TryIntoHeaderValue`. [#2510]
|
||||||
|
|
||||||
### Removed
|
|
||||||
- `MessageBody::{is_complete_body,take_complete_body}`. [#2522]
|
|
||||||
|
|
||||||
[#2510]: https://github.com/actix/actix-web/pull/2510
|
[#2510]: https://github.com/actix/actix-web/pull/2510
|
||||||
[#2522]: https://github.com/actix/actix-web/pull/2522
|
|
||||||
|
|
||||||
|
|
||||||
## 3.0.0-beta.15 - 2021-12-11
|
## 3.0.0-beta.15 - 2021-12-11
|
||||||
### Added
|
### Added
|
||||||
- Add timeout for canceling HTTP/2 server side connection handshake. Default to 5 seconds. [#2483]
|
* Add timeout for canceling HTTP/2 server side connection handshake. Default to 5 seconds. [#2483]
|
||||||
- HTTP/2 handshake timeout can be configured with `ServiceConfig::client_timeout`. [#2483]
|
* HTTP/2 handshake timeout can be configured with `ServiceConfig::client_timeout`. [#2483]
|
||||||
- `Response::map_into_boxed_body`. [#2468]
|
* `Response::map_into_boxed_body`. [#2468]
|
||||||
- `body::EitherBody` enum. [#2468]
|
* `body::EitherBody` enum. [#2468]
|
||||||
- `body::None` struct. [#2468]
|
* `body::None` struct. [#2468]
|
||||||
- Impl `MessageBody` for `bytestring::ByteString`. [#2468]
|
* Impl `MessageBody` for `bytestring::ByteString`. [#2468]
|
||||||
- `impl Clone for ws::HandshakeError`. [#2468]
|
* `impl Clone for ws::HandshakeError`. [#2468]
|
||||||
- `#[must_use]` for `ws::Codec` to prevent subtle bugs. [#1920]
|
* `#[must_use]` for `ws::Codec` to prevent subtle bugs. [#1920]
|
||||||
- `impl Default ` for `ws::Codec`. [#1920]
|
* `impl Default ` for `ws::Codec`. [#1920]
|
||||||
- `header::QualityItem::{max, min}`. [#2486]
|
* `header::QualityItem::{max, min}`. [#2486]
|
||||||
- `header::Quality::{MAX, MIN}`. [#2486]
|
* `header::Quality::{MAX, MIN}`. [#2486]
|
||||||
- `impl Display` for `header::Quality`. [#2486]
|
* `impl Display` for `header::Quality`. [#2486]
|
||||||
- Connection data set through the `on_connect_ext` callbacks is now accessible only from the new `Request::conn_data()` method. [#2491]
|
* Connection data set through the `on_connect_ext` callbacks is now accessible only from the new `Request::conn_data()` method. [#2491]
|
||||||
- `Request::take_conn_data()`. [#2491]
|
* `Request::take_conn_data()`. [#2491]
|
||||||
- `Request::take_req_data()`. [#2487]
|
* `Request::take_req_data()`. [#2487]
|
||||||
- `impl Clone` for `RequestHead`. [#2487]
|
* `impl Clone` for `RequestHead`. [#2487]
|
||||||
- New methods on `MessageBody` trait, `is_complete_body` and `take_complete_body`, both with default implementations, for optimizations on body types that are done in exactly one poll/chunk. [#2497]
|
* New methods on `MessageBody` trait, `is_complete_body` and `take_complete_body`, both with default implementations, for optimisations on body types that are done in exactly one poll/chunk. [#2497]
|
||||||
- New `boxed` method on `MessageBody` trait for wrapping body type. [#2520]
|
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- Rename `body::BoxBody::{from_body => new}`. [#2468]
|
* Rename `body::BoxBody::{from_body => new}`. [#2468]
|
||||||
- Body type for `Responses` returned from `Response::{new, ok, etc...}` is now `BoxBody`. [#2468]
|
* Body type for `Responses` returned from `Response::{new, ok, etc...}` is now `BoxBody`. [#2468]
|
||||||
- The `Error` associated type on `MessageBody` type now requires `impl Error` (or similar). [#2468]
|
* The `Error` associated type on `MessageBody` type now requires `impl Error` (or similar). [#2468]
|
||||||
- Error types using in service builders now require `Into<Response<BoxBody>>`. [#2468]
|
* Error types using in service builders now require `Into<Response<BoxBody>>`. [#2468]
|
||||||
- `From` implementations on error types now return a `Response<BoxBody>`. [#2468]
|
* `From` implementations on error types now return a `Response<BoxBody>`. [#2468]
|
||||||
- `ResponseBuilder::body(B)` now returns `Response<EitherBody<B>>`. [#2468]
|
* `ResponseBuilder::body(B)` now returns `Response<EitherBody<B>>`. [#2468]
|
||||||
- `ResponseBuilder::finish()` now returns `Response<EitherBody<()>>`. [#2468]
|
* `ResponseBuilder::finish()` now returns `Response<EitherBody<()>>`. [#2468]
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
- `ResponseBuilder::streaming`. [#2468]
|
* `ResponseBuilder::streaming`. [#2468]
|
||||||
- `impl Future` for `ResponseBuilder`. [#2468]
|
* `impl Future` for `ResponseBuilder`. [#2468]
|
||||||
- Remove unnecessary `MessageBody` bound on types passed to `body::AnyBody::new`. [#2468]
|
* Remove unnecessary `MessageBody` bound on types passed to `body::AnyBody::new`. [#2468]
|
||||||
- Move `body::AnyBody` to `awc`. Replaced with `EitherBody` and `BoxBody`. [#2468]
|
* Move `body::AnyBody` to `awc`. Replaced with `EitherBody` and `BoxBody`. [#2468]
|
||||||
- `impl Copy` for `ws::Codec`. [#1920]
|
* `impl Copy` for `ws::Codec`. [#1920]
|
||||||
- `header::qitem` helper. Replaced with `header::QualityItem::max`. [#2486]
|
* `header::qitem` helper. Replaced with `header::QualityItem::max`. [#2486]
|
||||||
- `impl TryFrom<u16>` for `header::Quality`. [#2486]
|
* `impl TryFrom<u16>` for `header::Quality`. [#2486]
|
||||||
- `http` module. Most everything it contained is exported at the crate root. [#2488]
|
* `http` module. Most everything it contained is exported at the crate root. [#2488]
|
||||||
|
|
||||||
[#2483]: https://github.com/actix/actix-web/pull/2483
|
[#2483]: https://github.com/actix/actix-web/pull/2483
|
||||||
[#2468]: https://github.com/actix/actix-web/pull/2468
|
[#2468]: https://github.com/actix/actix-web/pull/2468
|
||||||
@@ -83,15 +56,14 @@
|
|||||||
[#2488]: https://github.com/actix/actix-web/pull/2488
|
[#2488]: https://github.com/actix/actix-web/pull/2488
|
||||||
[#2491]: https://github.com/actix/actix-web/pull/2491
|
[#2491]: https://github.com/actix/actix-web/pull/2491
|
||||||
[#2497]: https://github.com/actix/actix-web/pull/2497
|
[#2497]: https://github.com/actix/actix-web/pull/2497
|
||||||
[#2520]: https://github.com/actix/actix-web/pull/2520
|
|
||||||
|
|
||||||
|
|
||||||
## 3.0.0-beta.14 - 2021-11-30
|
## 3.0.0-beta.14 - 2021-11-30
|
||||||
### Changed
|
### Changed
|
||||||
- Guarantee ordering of `header::GetAll` iterator to be same as insertion order. [#2467]
|
* Guarantee ordering of `header::GetAll` iterator to be same as insertion order. [#2467]
|
||||||
- Expose `header::map` module. [#2467]
|
* Expose `header::map` module. [#2467]
|
||||||
- Implement `ExactSizeIterator` and `FusedIterator` for all `HeaderMap` iterators. [#2470]
|
* Implement `ExactSizeIterator` and `FusedIterator` for all `HeaderMap` iterators. [#2470]
|
||||||
- Update `actix-tls` to `3.0.0-rc.1`. [#2474]
|
* Update `actix-tls` to `3.0.0-rc.1`. [#2474]
|
||||||
|
|
||||||
[#2467]: https://github.com/actix/actix-web/pull/2467
|
[#2467]: https://github.com/actix/actix-web/pull/2467
|
||||||
[#2470]: https://github.com/actix/actix-web/pull/2470
|
[#2470]: https://github.com/actix/actix-web/pull/2470
|
||||||
@@ -100,24 +72,24 @@
|
|||||||
|
|
||||||
## 3.0.0-beta.13 - 2021-11-22
|
## 3.0.0-beta.13 - 2021-11-22
|
||||||
### Added
|
### Added
|
||||||
- `body::AnyBody::empty` for quickly creating an empty body. [#2446]
|
* `body::AnyBody::empty` for quickly creating an empty body. [#2446]
|
||||||
- `body::AnyBody::none` for quickly creating a "none" body. [#2456]
|
* `body::AnyBody::none` for quickly creating a "none" body. [#2456]
|
||||||
- `impl Clone` for `body::AnyBody<S> where S: Clone`. [#2448]
|
* `impl Clone` for `body::AnyBody<S> where S: Clone`. [#2448]
|
||||||
- `body::AnyBody::into_boxed` for quickly converting to a type-erased, boxed body type. [#2448]
|
* `body::AnyBody::into_boxed` for quickly converting to a type-erased, boxed body type. [#2448]
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- Rename `body::AnyBody::{Message => Body}`. [#2446]
|
* Rename `body::AnyBody::{Message => Body}`. [#2446]
|
||||||
- Rename `body::AnyBody::{from_message => new_boxed}`. [#2448]
|
* Rename `body::AnyBody::{from_message => new_boxed}`. [#2448]
|
||||||
- Rename `body::AnyBody::{from_slice => copy_from_slice}`. [#2448]
|
* Rename `body::AnyBody::{from_slice => copy_from_slice}`. [#2448]
|
||||||
- Rename `body::{BoxAnyBody => BoxBody}`. [#2448]
|
* Rename `body::{BoxAnyBody => BoxBody}`. [#2448]
|
||||||
- Change representation of `AnyBody` to include a type parameter in `Body` variant. Defaults to `BoxBody`. [#2448]
|
* Change representation of `AnyBody` to include a type parameter in `Body` variant. Defaults to `BoxBody`. [#2448]
|
||||||
- `Encoder::response` now returns `AnyBody<Encoder<B>>`. [#2448]
|
* `Encoder::response` now returns `AnyBody<Encoder<B>>`. [#2448]
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
- `body::AnyBody::Empty`; an empty body can now only be represented as a zero-length `Bytes` variant. [#2446]
|
* `body::AnyBody::Empty`; an empty body can now only be represented as a zero-length `Bytes` variant. [#2446]
|
||||||
- `body::BodySize::Empty`; an empty body can now only be represented as a `Sized(0)` variant. [#2446]
|
* `body::BodySize::Empty`; an empty body can now only be represented as a `Sized(0)` variant. [#2446]
|
||||||
- `EncoderError::Boxed`; it is no longer required. [#2446]
|
* `EncoderError::Boxed`; it is no longer required. [#2446]
|
||||||
- `body::ResponseBody`; is function is replaced by the new `body::AnyBody` enum. [#2446]
|
* `body::ResponseBody`; is function is replaced by the new `body::AnyBody` enum. [#2446]
|
||||||
|
|
||||||
[#2446]: https://github.com/actix/actix-web/pull/2446
|
[#2446]: https://github.com/actix/actix-web/pull/2446
|
||||||
[#2448]: https://github.com/actix/actix-web/pull/2448
|
[#2448]: https://github.com/actix/actix-web/pull/2448
|
||||||
@@ -126,11 +98,11 @@
|
|||||||
|
|
||||||
## 3.0.0-beta.12 - 2021-11-15
|
## 3.0.0-beta.12 - 2021-11-15
|
||||||
### Changed
|
### Changed
|
||||||
- Update `actix-server` to `2.0.0-beta.9`. [#2442]
|
* Update `actix-server` to `2.0.0-beta.9`. [#2442]
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
- `client` module. [#2425]
|
* `client` module. [#2425]
|
||||||
- `trust-dns` feature. [#2425]
|
* `trust-dns` feature. [#2425]
|
||||||
|
|
||||||
[#2425]: https://github.com/actix/actix-web/pull/2425
|
[#2425]: https://github.com/actix/actix-web/pull/2425
|
||||||
[#2442]: https://github.com/actix/actix-web/pull/2442
|
[#2442]: https://github.com/actix/actix-web/pull/2442
|
||||||
@@ -138,21 +110,21 @@
|
|||||||
|
|
||||||
## 3.0.0-beta.11 - 2021-10-20
|
## 3.0.0-beta.11 - 2021-10-20
|
||||||
### Changed
|
### Changed
|
||||||
- Updated rustls to v0.20. [#2414]
|
* Updated rustls to v0.20. [#2414]
|
||||||
- Minimum supported Rust version (MSRV) is now 1.52.
|
* Minimum supported Rust version (MSRV) is now 1.52.
|
||||||
|
|
||||||
[#2414]: https://github.com/actix/actix-web/pull/2414
|
[#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
|
||||||
### Changed
|
### Changed
|
||||||
- `ContentEncoding` is now marked `#[non_exhaustive]`. [#2377]
|
* `ContentEncoding` is now marked `#[non_exhaustive]`. [#2377]
|
||||||
- Minimum supported Rust version (MSRV) is now 1.51.
|
* Minimum supported Rust version (MSRV) is now 1.51.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- Remove slice creation pointing to potential uninitialized data on h1 encoder. [#2364]
|
* Remove slice creation pointing to potential uninitialized data on h1 encoder. [#2364]
|
||||||
- Remove `Into<Error>` bound on `Encoder` body types. [#2375]
|
* Remove `Into<Error>` bound on `Encoder` body types. [#2375]
|
||||||
- Fix quality parse error in Accept-Encoding header. [#2344]
|
* Fix quality parse error in Accept-Encoding header. [#2344]
|
||||||
|
|
||||||
[#2364]: https://github.com/actix/actix-web/pull/2364
|
[#2364]: https://github.com/actix/actix-web/pull/2364
|
||||||
[#2375]: https://github.com/actix/actix-web/pull/2375
|
[#2375]: https://github.com/actix/actix-web/pull/2375
|
||||||
@@ -162,15 +134,15 @@
|
|||||||
|
|
||||||
## 3.0.0-beta.9 - 2021-08-09
|
## 3.0.0-beta.9 - 2021-08-09
|
||||||
### Fixed
|
### Fixed
|
||||||
- Potential HTTP request smuggling vulnerabilities. [RUSTSEC-2021-0081](https://github.com/rustsec/advisory-db/pull/977)
|
* Potential HTTP request smuggling vulnerabilities. [RUSTSEC-2021-0081](https://github.com/rustsec/advisory-db/pull/977)
|
||||||
|
|
||||||
|
|
||||||
## 3.0.0-beta.8 - 2021-06-26
|
## 3.0.0-beta.8 - 2021-06-26
|
||||||
### Changed
|
### Changed
|
||||||
- Change compression algorithm features flags. [#2250]
|
* Change compression algorithm features flags. [#2250]
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
- `downcast` and `downcast_get_type_id` macros. [#2291]
|
* `downcast` and `downcast_get_type_id` macros. [#2291]
|
||||||
|
|
||||||
[#2291]: https://github.com/actix/actix-web/pull/2291
|
[#2291]: https://github.com/actix/actix-web/pull/2291
|
||||||
[#2250]: https://github.com/actix/actix-web/pull/2250
|
[#2250]: https://github.com/actix/actix-web/pull/2250
|
||||||
@@ -178,37 +150,37 @@
|
|||||||
|
|
||||||
## 3.0.0-beta.7 - 2021-06-17
|
## 3.0.0-beta.7 - 2021-06-17
|
||||||
### Added
|
### Added
|
||||||
- Alias `body::Body` as `body::AnyBody`. [#2215]
|
* Alias `body::Body` as `body::AnyBody`. [#2215]
|
||||||
- `BoxAnyBody`: a boxed message body with boxed errors. [#2183]
|
* `BoxAnyBody`: a boxed message body with boxed errors. [#2183]
|
||||||
- Re-export `http` crate's `Error` type as `error::HttpError`. [#2171]
|
* Re-export `http` crate's `Error` type as `error::HttpError`. [#2171]
|
||||||
- Re-export `StatusCode`, `Method`, `Version` and `Uri` at the crate root. [#2171]
|
* Re-export `StatusCode`, `Method`, `Version` and `Uri` at the crate root. [#2171]
|
||||||
- Re-export `ContentEncoding` and `ConnectionType` at the crate root. [#2171]
|
* Re-export `ContentEncoding` and `ConnectionType` at the crate root. [#2171]
|
||||||
- `Response::into_body` that consumes response and returns body type. [#2201]
|
* `Response::into_body` that consumes response and returns body type. [#2201]
|
||||||
- `impl Default` for `Response`. [#2201]
|
* `impl Default` for `Response`. [#2201]
|
||||||
- Add zstd support for `ContentEncoding`. [#2244]
|
* Add zstd support for `ContentEncoding`. [#2244]
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- The `MessageBody` trait now has an associated `Error` type. [#2183]
|
* The `MessageBody` trait now has an associated `Error` type. [#2183]
|
||||||
- All error trait bounds in server service builders have changed from `Into<Error>` to `Into<Response<AnyBody>>`. [#2253]
|
* All error trait bounds in server service builders have changed from `Into<Error>` to `Into<Response<AnyBody>>`. [#2253]
|
||||||
- All error trait bounds in message body and stream impls changed from `Into<Error>` to `Into<Box<dyn std::error::Error>>`. [#2253]
|
* All error trait bounds in message body and stream impls changed from `Into<Error>` to `Into<Box<dyn std::error::Error>>`. [#2253]
|
||||||
- Places in `Response` where `ResponseBody<B>` was received or returned now simply use `B`. [#2201]
|
* Places in `Response` where `ResponseBody<B>` was received or returned now simply use `B`. [#2201]
|
||||||
- `header` mod is now public. [#2171]
|
* `header` mod is now public. [#2171]
|
||||||
- `uri` mod is now public. [#2171]
|
* `uri` mod is now public. [#2171]
|
||||||
- Update `language-tags` to `0.3`.
|
* Update `language-tags` to `0.3`.
|
||||||
- Reduce the level from `error` to `debug` for the log line that is emitted when a `500 Internal Server Error` is built using `HttpResponse::from_error`. [#2201]
|
* Reduce the level from `error` to `debug` for the log line that is emitted when a `500 Internal Server Error` is built using `HttpResponse::from_error`. [#2201]
|
||||||
- `ResponseBuilder::message_body` now returns a `Result`. [#2201]
|
* `ResponseBuilder::message_body` now returns a `Result`. [#2201]
|
||||||
- Remove `Unpin` bound on `ResponseBuilder::streaming`. [#2253]
|
* Remove `Unpin` bound on `ResponseBuilder::streaming`. [#2253]
|
||||||
- `HttpServer::{listen_rustls(), bind_rustls()}` now honor the ALPN protocols in the configuation parameter. [#2226]
|
* `HttpServer::{listen_rustls(), bind_rustls()}` now honor the ALPN protocols in the configuation parameter. [#2226]
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
- Stop re-exporting `http` crate's `HeaderMap` types in addition to ours. [#2171]
|
* Stop re-exporting `http` crate's `HeaderMap` types in addition to ours. [#2171]
|
||||||
- Down-casting for `MessageBody` types. [#2183]
|
* Down-casting for `MessageBody` types. [#2183]
|
||||||
- `error::Result` alias. [#2201]
|
* `error::Result` alias. [#2201]
|
||||||
- Error field from `Response` and `Response::error`. [#2205]
|
* Error field from `Response` and `Response::error`. [#2205]
|
||||||
- `impl Future` for `Response`. [#2201]
|
* `impl Future` for `Response`. [#2201]
|
||||||
- `Response::take_body` and old `Response::into_body` method that casted body type. [#2201]
|
* `Response::take_body` and old `Response::into_body` method that casted body type. [#2201]
|
||||||
- `InternalError` and all the error types it constructed. [#2215]
|
* `InternalError` and all the error types it constructed. [#2215]
|
||||||
- Conversion (`impl Into`) of `Response<Body>` and `ResponseBuilder` to `Error`. [#2215]
|
* Conversion (`impl Into`) of `Response<Body>` and `ResponseBuilder` to `Error`. [#2215]
|
||||||
|
|
||||||
[#2171]: https://github.com/actix/actix-web/pull/2171
|
[#2171]: https://github.com/actix/actix-web/pull/2171
|
||||||
[#2183]: https://github.com/actix/actix-web/pull/2183
|
[#2183]: https://github.com/actix/actix-web/pull/2183
|
||||||
@@ -223,27 +195,27 @@
|
|||||||
|
|
||||||
## 3.0.0-beta.6 - 2021-04-17
|
## 3.0.0-beta.6 - 2021-04-17
|
||||||
### Added
|
### Added
|
||||||
- `impl<T: MessageBody> MessageBody for Pin<Box<T>>`. [#2152]
|
* `impl<T: MessageBody> MessageBody for Pin<Box<T>>`. [#2152]
|
||||||
- `Response::{ok, bad_request, not_found, internal_server_error}`. [#2159]
|
* `Response::{ok, bad_request, not_found, internal_server_error}`. [#2159]
|
||||||
- Helper `body::to_bytes` for async collecting message body into Bytes. [#2158]
|
* Helper `body::to_bytes` for async collecting message body into Bytes. [#2158]
|
||||||
|
|
||||||
### Changes
|
### Changes
|
||||||
- The type parameter of `Response` no longer has a default. [#2152]
|
* The type parameter of `Response` no longer has a default. [#2152]
|
||||||
- The `Message` variant of `body::Body` is now `Pin<Box<dyn MessageBody>>`. [#2152]
|
* The `Message` variant of `body::Body` is now `Pin<Box<dyn MessageBody>>`. [#2152]
|
||||||
- `BodyStream` and `SizedStream` are no longer restricted to Unpin types. [#2152]
|
* `BodyStream` and `SizedStream` are no longer restricted to Unpin types. [#2152]
|
||||||
- Error enum types are marked `#[non_exhaustive]`. [#2161]
|
* Error enum types are marked `#[non_exhaustive]`. [#2161]
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
- `cookies` feature flag. [#2065]
|
* `cookies` feature flag. [#2065]
|
||||||
- Top-level `cookies` mod (re-export). [#2065]
|
* Top-level `cookies` mod (re-export). [#2065]
|
||||||
- `HttpMessage` trait loses the `cookies` and `cookie` methods. [#2065]
|
* `HttpMessage` trait loses the `cookies` and `cookie` methods. [#2065]
|
||||||
- `impl ResponseError for CookieParseError`. [#2065]
|
* `impl ResponseError for CookieParseError`. [#2065]
|
||||||
- Deprecated methods on `ResponseBuilder`: `if_true`, `if_some`. [#2148]
|
* Deprecated methods on `ResponseBuilder`: `if_true`, `if_some`. [#2148]
|
||||||
- `ResponseBuilder::json`. [#2148]
|
* `ResponseBuilder::json`. [#2148]
|
||||||
- `ResponseBuilder::{set_header, header}`. [#2148]
|
* `ResponseBuilder::{set_header, header}`. [#2148]
|
||||||
- `impl From<serde_json::Value> for Body`. [#2148]
|
* `impl From<serde_json::Value> for Body`. [#2148]
|
||||||
- `Response::build_from`. [#2159]
|
* `Response::build_from`. [#2159]
|
||||||
- Most of the status code builders on `Response`. [#2159]
|
* Most of the status code builders on `Response`. [#2159]
|
||||||
|
|
||||||
[#2065]: https://github.com/actix/actix-web/pull/2065
|
[#2065]: https://github.com/actix/actix-web/pull/2065
|
||||||
[#2148]: https://github.com/actix/actix-web/pull/2148
|
[#2148]: https://github.com/actix/actix-web/pull/2148
|
||||||
@@ -255,16 +227,16 @@
|
|||||||
|
|
||||||
## 3.0.0-beta.5 - 2021-04-02
|
## 3.0.0-beta.5 - 2021-04-02
|
||||||
### Added
|
### Added
|
||||||
- `client::Connector::handshake_timeout` method for customizing TLS connection handshake timeout. [#2081]
|
* `client::Connector::handshake_timeout` method for customizing TLS connection handshake timeout. [#2081]
|
||||||
- `client::ConnectorService` as `client::Connector::finish` method's return type [#2081]
|
* `client::ConnectorService` as `client::Connector::finish` method's return type [#2081]
|
||||||
- `client::ConnectionIo` trait alias [#2081]
|
* `client::ConnectionIo` trait alias [#2081]
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- `client::Connector` type now only have one generic type for `actix_service::Service`. [#2063]
|
* `client::Connector` type now only have one generic type for `actix_service::Service`. [#2063]
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
- Common typed HTTP headers were moved to actix-web. [2094]
|
* Common typed HTTP headers were moved to actix-web. [2094]
|
||||||
- `ResponseError` impl for `actix_utils::timeout::TimeoutError`. [#2127]
|
* `ResponseError` impl for `actix_utils::timeout::TimeoutError`. [#2127]
|
||||||
|
|
||||||
[#2063]: https://github.com/actix/actix-web/pull/2063
|
[#2063]: https://github.com/actix/actix-web/pull/2063
|
||||||
[#2081]: https://github.com/actix/actix-web/pull/2081
|
[#2081]: https://github.com/actix/actix-web/pull/2081
|
||||||
@@ -274,13 +246,13 @@
|
|||||||
|
|
||||||
## 3.0.0-beta.4 - 2021-03-08
|
## 3.0.0-beta.4 - 2021-03-08
|
||||||
### Changed
|
### Changed
|
||||||
- Feature `cookies` is now optional and disabled by default. [#1981]
|
* Feature `cookies` is now optional and disabled by default. [#1981]
|
||||||
- `ws::hash_key` now returns array. [#2035]
|
* `ws::hash_key` now returns array. [#2035]
|
||||||
- `ResponseBuilder::json` now takes `impl Serialize`. [#2052]
|
* `ResponseBuilder::json` now takes `impl Serialize`. [#2052]
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
- Re-export of `futures_channel::oneshot::Canceled` is removed from `error` mod. [#1994]
|
* Re-export of `futures_channel::oneshot::Canceled` is removed from `error` mod. [#1994]
|
||||||
- `ResponseError` impl for `futures_channel::oneshot::Canceled` is removed. [#1994]
|
* `ResponseError` impl for `futures_channel::oneshot::Canceled` is removed. [#1994]
|
||||||
|
|
||||||
[#1981]: https://github.com/actix/actix-web/pull/1981
|
[#1981]: https://github.com/actix/actix-web/pull/1981
|
||||||
[#1994]: https://github.com/actix/actix-web/pull/1994
|
[#1994]: https://github.com/actix/actix-web/pull/1994
|
||||||
@@ -289,48 +261,48 @@
|
|||||||
|
|
||||||
|
|
||||||
## 3.0.0-beta.3 - 2021-02-10
|
## 3.0.0-beta.3 - 2021-02-10
|
||||||
- No notable changes.
|
* No notable changes.
|
||||||
|
|
||||||
|
|
||||||
## 3.0.0-beta.2 - 2021-02-10
|
## 3.0.0-beta.2 - 2021-02-10
|
||||||
### Added
|
### Added
|
||||||
- `TryIntoHeaderPair` trait that allows using typed and untyped headers in the same methods. [#1869]
|
* `TryIntoHeaderPair` trait that allows using typed and untyped headers in the same methods. [#1869]
|
||||||
- `ResponseBuilder::insert_header` method which allows using typed headers. [#1869]
|
* `ResponseBuilder::insert_header` method which allows using typed headers. [#1869]
|
||||||
- `ResponseBuilder::append_header` method which allows using typed headers. [#1869]
|
* `ResponseBuilder::append_header` method which allows using typed headers. [#1869]
|
||||||
- `TestRequest::insert_header` method which allows using typed headers. [#1869]
|
* `TestRequest::insert_header` method which allows using typed headers. [#1869]
|
||||||
- `ContentEncoding` implements all necessary header traits. [#1912]
|
* `ContentEncoding` implements all necessary header traits. [#1912]
|
||||||
- `HeaderMap::len_keys` has the behavior of the old `len` method. [#1964]
|
* `HeaderMap::len_keys` has the behavior of the old `len` method. [#1964]
|
||||||
- `HeaderMap::drain` as an efficient draining iterator. [#1964]
|
* `HeaderMap::drain` as an efficient draining iterator. [#1964]
|
||||||
- Implement `IntoIterator` for owned `HeaderMap`. [#1964]
|
* Implement `IntoIterator` for owned `HeaderMap`. [#1964]
|
||||||
- `trust-dns` optional feature to enable `trust-dns-resolver` as client dns resolver. [#1969]
|
* `trust-dns` optional feature to enable `trust-dns-resolver` as client dns resolver. [#1969]
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- `ResponseBuilder::content_type` now takes an `impl TryIntoHeaderValue` to support using typed
|
* `ResponseBuilder::content_type` now takes an `impl TryIntoHeaderValue` to support using typed
|
||||||
`mime` types. [#1894]
|
`mime` types. [#1894]
|
||||||
- Renamed `TryIntoHeaderValue::{try_into => try_into_value}` to avoid ambiguity with std
|
* Renamed `TryIntoHeaderValue::{try_into => try_into_value}` to avoid ambiguity with std
|
||||||
`TryInto` trait. [#1894]
|
`TryInto` trait. [#1894]
|
||||||
- `Extensions::insert` returns Option of replaced item. [#1904]
|
* `Extensions::insert` returns Option of replaced item. [#1904]
|
||||||
- Remove `HttpResponseBuilder::json2()`. [#1903]
|
* Remove `HttpResponseBuilder::json2()`. [#1903]
|
||||||
- Enable `HttpResponseBuilder::json()` to receive data by value and reference. [#1903]
|
* Enable `HttpResponseBuilder::json()` to receive data by value and reference. [#1903]
|
||||||
- `client::error::ConnectError` Resolver variant contains `Box<dyn std::error::Error>` type. [#1905]
|
* `client::error::ConnectError` Resolver variant contains `Box<dyn std::error::Error>` type. [#1905]
|
||||||
- `client::ConnectorConfig` default timeout changed to 5 seconds. [#1905]
|
* `client::ConnectorConfig` default timeout changed to 5 seconds. [#1905]
|
||||||
- Simplify `BlockingError` type to a unit struct. It's now only triggered when blocking thread pool
|
* Simplify `BlockingError` type to a unit struct. It's now only triggered when blocking thread pool
|
||||||
is dead. [#1957]
|
is dead. [#1957]
|
||||||
- `HeaderMap::len` now returns number of values instead of number of keys. [#1964]
|
* `HeaderMap::len` now returns number of values instead of number of keys. [#1964]
|
||||||
- `HeaderMap::insert` now returns iterator of removed values. [#1964]
|
* `HeaderMap::insert` now returns iterator of removed values. [#1964]
|
||||||
- `HeaderMap::remove` now returns iterator of removed values. [#1964]
|
* `HeaderMap::remove` now returns iterator of removed values. [#1964]
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
- `ResponseBuilder::set`; use `ResponseBuilder::insert_header`. [#1869]
|
* `ResponseBuilder::set`; use `ResponseBuilder::insert_header`. [#1869]
|
||||||
- `ResponseBuilder::set_header`; use `ResponseBuilder::insert_header`. [#1869]
|
* `ResponseBuilder::set_header`; use `ResponseBuilder::insert_header`. [#1869]
|
||||||
- `ResponseBuilder::header`; use `ResponseBuilder::append_header`. [#1869]
|
* `ResponseBuilder::header`; use `ResponseBuilder::append_header`. [#1869]
|
||||||
- `TestRequest::with_hdr`; use `TestRequest::default().insert_header()`. [#1869]
|
* `TestRequest::with_hdr`; use `TestRequest::default().insert_header()`. [#1869]
|
||||||
- `TestRequest::with_header`; use `TestRequest::default().insert_header()`. [#1869]
|
* `TestRequest::with_header`; use `TestRequest::default().insert_header()`. [#1869]
|
||||||
- `actors` optional feature. [#1969]
|
* `actors` optional feature. [#1969]
|
||||||
- `ResponseError` impl for `actix::MailboxError`. [#1969]
|
* `ResponseError` impl for `actix::MailboxError`. [#1969]
|
||||||
|
|
||||||
### Documentation
|
### Documentation
|
||||||
- Vastly improve docs and add examples for `HeaderMap`. [#1964]
|
* Vastly improve docs and add examples for `HeaderMap`. [#1964]
|
||||||
|
|
||||||
[#1869]: https://github.com/actix/actix-web/pull/1869
|
[#1869]: https://github.com/actix/actix-web/pull/1869
|
||||||
[#1894]: https://github.com/actix/actix-web/pull/1894
|
[#1894]: https://github.com/actix/actix-web/pull/1894
|
||||||
@@ -345,24 +317,24 @@
|
|||||||
|
|
||||||
## 3.0.0-beta.1 - 2021-01-07
|
## 3.0.0-beta.1 - 2021-01-07
|
||||||
### Added
|
### Added
|
||||||
- Add `Http3` to `Protocol` enum for future compatibility and also mark `#[non_exhaustive]`.
|
* Add `Http3` to `Protocol` enum for future compatibility and also mark `#[non_exhaustive]`.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- Update `actix-*` dependencies to tokio `1.0` based versions. [#1813]
|
* Update `actix-*` dependencies to tokio `1.0` based versions. [#1813]
|
||||||
- Bumped `rand` to `0.8`.
|
* Bumped `rand` to `0.8`.
|
||||||
- Update `bytes` to `1.0`. [#1813]
|
* Update `bytes` to `1.0`. [#1813]
|
||||||
- Update `h2` to `0.3`. [#1813]
|
* Update `h2` to `0.3`. [#1813]
|
||||||
- The `ws::Message::Text` enum variant now contains a `bytestring::ByteString`. [#1864]
|
* The `ws::Message::Text` enum variant now contains a `bytestring::ByteString`. [#1864]
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
- Deprecated `on_connect` methods have been removed. Prefer the new
|
* Deprecated `on_connect` methods have been removed. Prefer the new
|
||||||
`on_connect_ext` technique. [#1857]
|
`on_connect_ext` technique. [#1857]
|
||||||
- Remove `ResponseError` impl for `actix::actors::resolver::ResolverError`
|
* Remove `ResponseError` impl for `actix::actors::resolver::ResolverError`
|
||||||
due to deprecate of resolver actor. [#1813]
|
due to deprecate of resolver actor. [#1813]
|
||||||
- Remove `ConnectError::SslHandshakeError` and re-export of `HandshakeError`.
|
* Remove `ConnectError::SslHandshakeError` and re-export of `HandshakeError`.
|
||||||
due to the removal of this type from `tokio-openssl` crate. openssl handshake
|
due to the removal of this type from `tokio-openssl` crate. openssl handshake
|
||||||
error would return as `ConnectError::SslError`. [#1813]
|
error would return as `ConnectError::SslError`. [#1813]
|
||||||
- Remove `actix-threadpool` dependency. Use `actix_rt::task::spawn_blocking`.
|
* Remove `actix-threadpool` dependency. Use `actix_rt::task::spawn_blocking`.
|
||||||
Due to this change `actix_threadpool::BlockingError` type is moved into
|
Due to this change `actix_threadpool::BlockingError` type is moved into
|
||||||
`actix_http::error` module. [#1878]
|
`actix_http::error` module. [#1878]
|
||||||
|
|
||||||
@@ -374,20 +346,20 @@
|
|||||||
|
|
||||||
## 2.2.1 - 2021-08-09
|
## 2.2.1 - 2021-08-09
|
||||||
### Fixed
|
### Fixed
|
||||||
- Potential HTTP request smuggling vulnerabilities. [RUSTSEC-2021-0081](https://github.com/rustsec/advisory-db/pull/977)
|
* Potential HTTP request smuggling vulnerabilities. [RUSTSEC-2021-0081](https://github.com/rustsec/advisory-db/pull/977)
|
||||||
|
|
||||||
|
|
||||||
## 2.2.0 - 2020-11-25
|
## 2.2.0 - 2020-11-25
|
||||||
### Added
|
### Added
|
||||||
- HttpResponse builders for 1xx status codes. [#1768]
|
* HttpResponse builders for 1xx status codes. [#1768]
|
||||||
- `Accept::mime_precedence` and `Accept::mime_preference`. [#1793]
|
* `Accept::mime_precedence` and `Accept::mime_preference`. [#1793]
|
||||||
- `TryFrom<u16>` and `TryFrom<f32>` for `http::header::Quality`. [#1797]
|
* `TryFrom<u16>` and `TryFrom<f32>` for `http::header::Quality`. [#1797]
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- Started dropping `transfer-encoding: chunked` and `Content-Length` for 1XX and 204 responses. [#1767]
|
* Started dropping `transfer-encoding: chunked` and `Content-Length` for 1XX and 204 responses. [#1767]
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- Upgrade `serde_urlencoded` to `0.7`. [#1773]
|
* Upgrade `serde_urlencoded` to `0.7`. [#1773]
|
||||||
|
|
||||||
[#1773]: https://github.com/actix/actix-web/pull/1773
|
[#1773]: https://github.com/actix/actix-web/pull/1773
|
||||||
[#1767]: https://github.com/actix/actix-web/pull/1767
|
[#1767]: https://github.com/actix/actix-web/pull/1767
|
||||||
@@ -398,12 +370,12 @@
|
|||||||
|
|
||||||
## 2.1.0 - 2020-10-30
|
## 2.1.0 - 2020-10-30
|
||||||
### Added
|
### Added
|
||||||
- Added more flexible `on_connect_ext` methods for on-connect handling. [#1754]
|
* Added more flexible `on_connect_ext` methods for on-connect handling. [#1754]
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- Upgrade `base64` to `0.13`. [#1744]
|
* Upgrade `base64` to `0.13`. [#1744]
|
||||||
- Upgrade `pin-project` to `1.0`. [#1733]
|
* Upgrade `pin-project` to `1.0`. [#1733]
|
||||||
- Deprecate `ResponseBuilder::{if_some, if_true}`. [#1760]
|
* Deprecate `ResponseBuilder::{if_some, if_true}`. [#1760]
|
||||||
|
|
||||||
[#1760]: https://github.com/actix/actix-web/pull/1760
|
[#1760]: https://github.com/actix/actix-web/pull/1760
|
||||||
[#1754]: https://github.com/actix/actix-web/pull/1754
|
[#1754]: https://github.com/actix/actix-web/pull/1754
|
||||||
@@ -412,28 +384,28 @@
|
|||||||
|
|
||||||
|
|
||||||
## 2.0.0 - 2020-09-11
|
## 2.0.0 - 2020-09-11
|
||||||
- No significant changes from `2.0.0-beta.4`.
|
* No significant changes from `2.0.0-beta.4`.
|
||||||
|
|
||||||
|
|
||||||
## 2.0.0-beta.4 - 2020-09-09
|
## 2.0.0-beta.4 - 2020-09-09
|
||||||
### Changed
|
### Changed
|
||||||
- Update actix-codec and actix-utils dependencies.
|
* Update actix-codec and actix-utils dependencies.
|
||||||
- Update actix-connect and actix-tls dependencies.
|
* Update actix-connect and actix-tls dependencies.
|
||||||
|
|
||||||
|
|
||||||
## 2.0.0-beta.3 - 2020-08-14
|
## 2.0.0-beta.3 - 2020-08-14
|
||||||
### Fixed
|
### Fixed
|
||||||
- Memory leak of `client::pool::ConnectorPoolSupport`. [#1626]
|
* Memory leak of `client::pool::ConnectorPoolSupport`. [#1626]
|
||||||
|
|
||||||
[#1626]: https://github.com/actix/actix-web/pull/1626
|
[#1626]: https://github.com/actix/actix-web/pull/1626
|
||||||
|
|
||||||
|
|
||||||
## 2.0.0-beta.2 - 2020-07-21
|
## 2.0.0-beta.2 - 2020-07-21
|
||||||
### Fixed
|
### Fixed
|
||||||
- Potential UB in h1 decoder using uninitialized memory. [#1614]
|
* Potential UB in h1 decoder using uninitialized memory. [#1614]
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- Fix illegal chunked encoding. [#1615]
|
* Fix illegal chunked encoding. [#1615]
|
||||||
|
|
||||||
[#1614]: https://github.com/actix/actix-web/pull/1614
|
[#1614]: https://github.com/actix/actix-web/pull/1614
|
||||||
[#1615]: https://github.com/actix/actix-web/pull/1615
|
[#1615]: https://github.com/actix/actix-web/pull/1615
|
||||||
@@ -441,10 +413,10 @@
|
|||||||
|
|
||||||
## 2.0.0-beta.1 - 2020-07-11
|
## 2.0.0-beta.1 - 2020-07-11
|
||||||
### Changed
|
### Changed
|
||||||
- Migrate cookie handling to `cookie` crate. [#1558]
|
* Migrate cookie handling to `cookie` crate. [#1558]
|
||||||
- Update `sha-1` to 0.9. [#1586]
|
* Update `sha-1` to 0.9. [#1586]
|
||||||
- Fix leak in client pool. [#1580]
|
* Fix leak in client pool. [#1580]
|
||||||
- MSRV is now 1.41.1.
|
* MSRV is now 1.41.1.
|
||||||
|
|
||||||
[#1558]: https://github.com/actix/actix-web/pull/1558
|
[#1558]: https://github.com/actix/actix-web/pull/1558
|
||||||
[#1586]: https://github.com/actix/actix-web/pull/1586
|
[#1586]: https://github.com/actix/actix-web/pull/1586
|
||||||
@@ -453,15 +425,15 @@
|
|||||||
|
|
||||||
## 2.0.0-alpha.4 - 2020-05-21
|
## 2.0.0-alpha.4 - 2020-05-21
|
||||||
### Changed
|
### Changed
|
||||||
- Bump minimum supported Rust version to 1.40
|
* Bump minimum supported Rust version to 1.40
|
||||||
- content_length function is removed, and you can set Content-Length by calling
|
* content_length function is removed, and you can set Content-Length by calling
|
||||||
no_chunking function [#1439]
|
no_chunking function [#1439]
|
||||||
- `BodySize::Sized64` variant has been removed. `BodySize::Sized` now receives a
|
* `BodySize::Sized64` variant has been removed. `BodySize::Sized` now receives a
|
||||||
`u64` instead of a `usize`.
|
`u64` instead of a `usize`.
|
||||||
- Update `base64` dependency to 0.12
|
* Update `base64` dependency to 0.12
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- Support parsing of `SameSite=None` [#1503]
|
* Support parsing of `SameSite=None` [#1503]
|
||||||
|
|
||||||
[#1439]: https://github.com/actix/actix-web/pull/1439
|
[#1439]: https://github.com/actix/actix-web/pull/1439
|
||||||
[#1503]: https://github.com/actix/actix-web/pull/1503
|
[#1503]: https://github.com/actix/actix-web/pull/1503
|
||||||
@@ -469,13 +441,13 @@
|
|||||||
|
|
||||||
## 2.0.0-alpha.3 - 2020-05-08
|
## 2.0.0-alpha.3 - 2020-05-08
|
||||||
### Fixed
|
### Fixed
|
||||||
- Correct spelling of ConnectError::Unresolved [#1487]
|
* Correct spelling of ConnectError::Unresolved [#1487]
|
||||||
- Fix a mistake in the encoding of websocket continuation messages wherein
|
* Fix a mistake in the encoding of websocket continuation messages wherein
|
||||||
Item::FirstText and Item::FirstBinary are each encoded as the other.
|
Item::FirstText and Item::FirstBinary are each encoded as the other.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- Implement `std::error::Error` for our custom errors [#1422]
|
* Implement `std::error::Error` for our custom errors [#1422]
|
||||||
- Remove `failure` support for `ResponseError` since that crate
|
* Remove `failure` support for `ResponseError` since that crate
|
||||||
will be deprecated in the near future.
|
will be deprecated in the near future.
|
||||||
|
|
||||||
[#1422]: https://github.com/actix/actix-web/pull/1422
|
[#1422]: https://github.com/actix/actix-web/pull/1422
|
||||||
@@ -484,12 +456,12 @@
|
|||||||
|
|
||||||
## 2.0.0-alpha.2 - 2020-03-07
|
## 2.0.0-alpha.2 - 2020-03-07
|
||||||
### Changed
|
### Changed
|
||||||
- Update `actix-connect` and `actix-tls` dependency to 2.0.0-alpha.1. [#1395]
|
* Update `actix-connect` and `actix-tls` dependency to 2.0.0-alpha.1. [#1395]
|
||||||
- Change default initial window size and connection window size for HTTP2 to 2MB and 1MB
|
* Change default initial window size and connection window size for HTTP2 to 2MB and 1MB
|
||||||
respectively to improve download speed for awc when downloading large objects. [#1394]
|
respectively to improve download speed for awc when downloading large objects. [#1394]
|
||||||
- client::Connector accepts initial_window_size and initial_connection_window_size
|
* client::Connector accepts initial_window_size and initial_connection_window_size
|
||||||
HTTP2 configuration. [#1394]
|
HTTP2 configuration. [#1394]
|
||||||
- client::Connector allowing to set max_http_version to limit HTTP version to be used. [#1394]
|
* client::Connector allowing to set max_http_version to limit HTTP version to be used. [#1394]
|
||||||
|
|
||||||
[#1394]: https://github.com/actix/actix-web/pull/1394
|
[#1394]: https://github.com/actix/actix-web/pull/1394
|
||||||
[#1395]: https://github.com/actix/actix-web/pull/1395
|
[#1395]: https://github.com/actix/actix-web/pull/1395
|
||||||
@@ -497,61 +469,61 @@
|
|||||||
|
|
||||||
## 2.0.0-alpha.1 - 2020-02-27
|
## 2.0.0-alpha.1 - 2020-02-27
|
||||||
### Changed
|
### Changed
|
||||||
- Update the `time` dependency to 0.2.7.
|
* Update the `time` dependency to 0.2.7.
|
||||||
- Moved actors messages support from actix crate, enabled with feature `actors`.
|
* Moved actors messages support from actix crate, enabled with feature `actors`.
|
||||||
- Breaking change: trait MessageBody requires Unpin and accepting `Pin<&mut Self>` instead of
|
* Breaking change: trait MessageBody requires Unpin and accepting `Pin<&mut Self>` instead of
|
||||||
`&mut self` in the poll_next().
|
`&mut self` in the poll_next().
|
||||||
- MessageBody is not implemented for &'static [u8] anymore.
|
* MessageBody is not implemented for &'static [u8] anymore.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- Allow `SameSite=None` cookies to be sent in a response.
|
* Allow `SameSite=None` cookies to be sent in a response.
|
||||||
|
|
||||||
|
|
||||||
## 1.0.1 - 2019-12-20
|
## 1.0.1 - 2019-12-20
|
||||||
### Fixed
|
### Fixed
|
||||||
- Poll upgrade service's readiness from HTTP service handlers
|
* Poll upgrade service's readiness from HTTP service handlers
|
||||||
- Replace brotli with brotli2 #1224
|
* Replace brotli with brotli2 #1224
|
||||||
|
|
||||||
|
|
||||||
## 1.0.0 - 2019-12-13
|
## 1.0.0 - 2019-12-13
|
||||||
### Added
|
### Added
|
||||||
- Add websockets continuation frame support
|
* Add websockets continuation frame support
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- Replace `flate2-xxx` features with `compress`
|
* Replace `flate2-xxx` features with `compress`
|
||||||
|
|
||||||
|
|
||||||
## 1.0.0-alpha.5 - 2019-12-09
|
## 1.0.0-alpha.5 - 2019-12-09
|
||||||
### Fixed
|
### Fixed
|
||||||
- Check `Upgrade` service readiness before calling it
|
* Check `Upgrade` service readiness before calling it
|
||||||
- Fix buffer remaining capacity calculation
|
* Fix buffer remaining capacity calculation
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- Websockets: Ping and Pong should have binary data #1049
|
* Websockets: Ping and Pong should have binary data #1049
|
||||||
|
|
||||||
|
|
||||||
## 1.0.0-alpha.4 - 2019-12-08
|
## 1.0.0-alpha.4 - 2019-12-08
|
||||||
### Added
|
### Added
|
||||||
- Add impl ResponseBuilder for Error
|
* Add impl ResponseBuilder for Error
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- Use rust based brotli compression library
|
* Use rust based brotli compression library
|
||||||
|
|
||||||
## 1.0.0-alpha.3 - 2019-12-07
|
## 1.0.0-alpha.3 - 2019-12-07
|
||||||
### Changed
|
### Changed
|
||||||
- Migrate to tokio 0.2
|
* Migrate to tokio 0.2
|
||||||
- Migrate to `std::future`
|
* Migrate to `std::future`
|
||||||
|
|
||||||
|
|
||||||
## 0.2.11 - 2019-11-06
|
## 0.2.11 - 2019-11-06
|
||||||
### Added
|
### Added
|
||||||
- Add support for serde_json::Value to be passed as argument to ResponseBuilder.body()
|
* Add support for serde_json::Value to be passed as argument to ResponseBuilder.body()
|
||||||
- Add an additional `filename*` param in the `Content-Disposition` header of
|
* Add an additional `filename*` param in the `Content-Disposition` header of
|
||||||
`actix_files::NamedFile` to be more compatible. (#1151)
|
`actix_files::NamedFile` to be more compatible. (#1151)
|
||||||
- Allow to use `std::convert::Infallible` as `actix_http::error::Error`
|
* Allow to use `std::convert::Infallible` as `actix_http::error::Error`
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- To be compatible with non-English error responses, `ResponseError` rendered with `text/plain;
|
* To be compatible with non-English error responses, `ResponseError` rendered with `text/plain;
|
||||||
charset=utf-8` header [#1118]
|
charset=utf-8` header [#1118]
|
||||||
|
|
||||||
[#1878]: https://github.com/actix/actix-web/pull/1878
|
[#1878]: https://github.com/actix/actix-web/pull/1878
|
||||||
@@ -559,169 +531,169 @@
|
|||||||
|
|
||||||
## 0.2.10 - 2019-09-11
|
## 0.2.10 - 2019-09-11
|
||||||
### Added
|
### Added
|
||||||
- Add support for sending HTTP requests with `Rc<RequestHead>` in addition to sending HTTP requests
|
* Add support for sending HTTP requests with `Rc<RequestHead>` in addition to sending HTTP requests
|
||||||
with `RequestHead`
|
with `RequestHead`
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- h2 will use error response #1080
|
* h2 will use error response #1080
|
||||||
- on_connect result isn't added to request extensions for http2 requests #1009
|
* on_connect result isn't added to request extensions for http2 requests #1009
|
||||||
|
|
||||||
|
|
||||||
## 0.2.9 - 2019-08-13
|
## 0.2.9 - 2019-08-13
|
||||||
### Changed
|
### Changed
|
||||||
- Dropped the `byteorder`-dependency in favor of `stdlib`-implementation
|
* Dropped the `byteorder`-dependency in favor of `stdlib`-implementation
|
||||||
- Update percent-encoding to 2.1
|
* Update percent-encoding to 2.1
|
||||||
- Update serde_urlencoded to 0.6.1
|
* Update serde_urlencoded to 0.6.1
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- Fixed a panic in the HTTP2 handshake in client HTTP requests (#1031)
|
* Fixed a panic in the HTTP2 handshake in client HTTP requests (#1031)
|
||||||
|
|
||||||
|
|
||||||
## 0.2.8 - 2019-08-01
|
## 0.2.8 - 2019-08-01
|
||||||
### Added
|
### Added
|
||||||
- Add `rustls` support
|
* Add `rustls` support
|
||||||
- Add `Clone` impl for `HeaderMap`
|
* Add `Clone` impl for `HeaderMap`
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- awc client panic #1016
|
* awc client panic #1016
|
||||||
- Invalid response with compression middleware enabled, but compression-related features
|
* Invalid response with compression middleware enabled, but compression-related features
|
||||||
disabled #997
|
disabled #997
|
||||||
|
|
||||||
|
|
||||||
## 0.2.7 - 2019-07-18
|
## 0.2.7 - 2019-07-18
|
||||||
### Added
|
### Added
|
||||||
- Add support for downcasting response errors #986
|
* Add support for downcasting response errors #986
|
||||||
|
|
||||||
|
|
||||||
## 0.2.6 - 2019-07-17
|
## 0.2.6 - 2019-07-17
|
||||||
### Changed
|
### Changed
|
||||||
- Replace `ClonableService` with local copy
|
* Replace `ClonableService` with local copy
|
||||||
- Upgrade `rand` dependency version to 0.7
|
* Upgrade `rand` dependency version to 0.7
|
||||||
|
|
||||||
|
|
||||||
## 0.2.5 - 2019-06-28
|
## 0.2.5 - 2019-06-28
|
||||||
### Added
|
### Added
|
||||||
- Add `on-connect` callback, `HttpServiceBuilder::on_connect()` #946
|
* Add `on-connect` callback, `HttpServiceBuilder::on_connect()` #946
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- Use `encoding_rs` crate instead of unmaintained `encoding` crate
|
* Use `encoding_rs` crate instead of unmaintained `encoding` crate
|
||||||
- Add `Copy` and `Clone` impls for `ws::Codec`
|
* Add `Copy` and `Clone` impls for `ws::Codec`
|
||||||
|
|
||||||
|
|
||||||
## 0.2.4 - 2019-06-16
|
## 0.2.4 - 2019-06-16
|
||||||
### Fixed
|
### Fixed
|
||||||
- Do not compress NoContent (204) responses #918
|
* Do not compress NoContent (204) responses #918
|
||||||
|
|
||||||
|
|
||||||
## 0.2.3 - 2019-06-02
|
## 0.2.3 - 2019-06-02
|
||||||
### Added
|
### Added
|
||||||
- Debug impl for ResponseBuilder
|
* Debug impl for ResponseBuilder
|
||||||
- From SizedStream and BodyStream for Body
|
* From SizedStream and BodyStream for Body
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- SizedStream uses u64
|
* SizedStream uses u64
|
||||||
|
|
||||||
|
|
||||||
## 0.2.2 - 2019-05-29
|
## 0.2.2 - 2019-05-29
|
||||||
### Fixed
|
### Fixed
|
||||||
- Parse incoming stream before closing stream on disconnect #868
|
* Parse incoming stream before closing stream on disconnect #868
|
||||||
|
|
||||||
|
|
||||||
## 0.2.1 - 2019-05-25
|
## 0.2.1 - 2019-05-25
|
||||||
### Fixed
|
### Fixed
|
||||||
- Handle socket read disconnect
|
* Handle socket read disconnect
|
||||||
|
|
||||||
|
|
||||||
## 0.2.0 - 2019-05-12
|
## 0.2.0 - 2019-05-12
|
||||||
### Changed
|
### Changed
|
||||||
- Update actix-service to 0.4
|
* Update actix-service to 0.4
|
||||||
- Expect and upgrade services accept `ServerConfig` config.
|
* Expect and upgrade services accept `ServerConfig` config.
|
||||||
|
|
||||||
### Deleted
|
### Deleted
|
||||||
- `OneRequest` service
|
* `OneRequest` service
|
||||||
|
|
||||||
|
|
||||||
## 0.1.5 - 2019-05-04
|
## 0.1.5 - 2019-05-04
|
||||||
### Fixed
|
### Fixed
|
||||||
- Clean up response extensions in response pool #817
|
* Clean up response extensions in response pool #817
|
||||||
|
|
||||||
|
|
||||||
## 0.1.4 - 2019-04-24
|
## 0.1.4 - 2019-04-24
|
||||||
### Added
|
### Added
|
||||||
- Allow to render h1 request headers in `Camel-Case`
|
* Allow to render h1 request headers in `Camel-Case`
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- Read until eof for http/1.0 responses #771
|
* Read until eof for http/1.0 responses #771
|
||||||
|
|
||||||
|
|
||||||
## 0.1.3 - 2019-04-23
|
## 0.1.3 - 2019-04-23
|
||||||
### Fixed
|
### Fixed
|
||||||
- Fix http client pool management
|
* Fix http client pool management
|
||||||
- Fix http client wait queue management #794
|
* Fix http client wait queue management #794
|
||||||
|
|
||||||
|
|
||||||
## 0.1.2 - 2019-04-23
|
## 0.1.2 - 2019-04-23
|
||||||
### Fixed
|
### Fixed
|
||||||
- Fix BorrowMutError panic in client connector #793
|
* Fix BorrowMutError panic in client connector #793
|
||||||
|
|
||||||
|
|
||||||
## 0.1.1 - 2019-04-19
|
## 0.1.1 - 2019-04-19
|
||||||
### Changed
|
### Changed
|
||||||
- Cookie::max_age() accepts value in seconds
|
* Cookie::max_age() accepts value in seconds
|
||||||
- Cookie::max_age_time() accepts value in time::Duration
|
* Cookie::max_age_time() accepts value in time::Duration
|
||||||
- Allow to specify server address for client connector
|
* Allow to specify server address for client connector
|
||||||
|
|
||||||
|
|
||||||
## 0.1.0 - 2019-04-16
|
## 0.1.0 - 2019-04-16
|
||||||
### Added
|
### Added
|
||||||
- Expose peer addr via `Request::peer_addr()` and `RequestHead::peer_addr`
|
* Expose peer addr via `Request::peer_addr()` and `RequestHead::peer_addr`
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- `actix_http::encoding` always available
|
* `actix_http::encoding` always available
|
||||||
- use trust-dns-resolver 0.11.0
|
* use trust-dns-resolver 0.11.0
|
||||||
|
|
||||||
|
|
||||||
## 0.1.0-alpha.5 - 2019-04-12
|
## 0.1.0-alpha.5 - 2019-04-12
|
||||||
### Added
|
### Added
|
||||||
- Allow to use custom service for upgrade requests
|
* Allow to use custom service for upgrade requests
|
||||||
- Added `h1::SendResponse` future.
|
* Added `h1::SendResponse` future.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- MessageBody::length() renamed to MessageBody::size() for consistency
|
* MessageBody::length() renamed to MessageBody::size() for consistency
|
||||||
- ws handshake verification functions take RequestHead instead of Request
|
* ws handshake verification functions take RequestHead instead of Request
|
||||||
|
|
||||||
|
|
||||||
## 0.1.0-alpha.4 - 2019-04-08
|
## 0.1.0-alpha.4 - 2019-04-08
|
||||||
### Added
|
### Added
|
||||||
- Allow to use custom `Expect` handler
|
* Allow to use custom `Expect` handler
|
||||||
- Add minimal `std::error::Error` impl for `Error`
|
* Add minimal `std::error::Error` impl for `Error`
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- Export IntoHeaderValue
|
* Export IntoHeaderValue
|
||||||
- Render error and return as response body
|
* Render error and return as response body
|
||||||
- Use thread pool for response body compression
|
* Use thread pool for response body compression
|
||||||
|
|
||||||
### Deleted
|
### Deleted
|
||||||
- Removed PayloadBuffer
|
* Removed PayloadBuffer
|
||||||
|
|
||||||
|
|
||||||
## 0.1.0-alpha.3 - 2019-04-02
|
## 0.1.0-alpha.3 - 2019-04-02
|
||||||
### Added
|
### Added
|
||||||
- Warn when an unsealed private cookie isn't valid UTF-8
|
* Warn when an unsealed private cookie isn't valid UTF-8
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- Rust 1.31.0 compatibility
|
* Rust 1.31.0 compatibility
|
||||||
- Preallocate read buffer for h1 codec
|
* Preallocate read buffer for h1 codec
|
||||||
- Detect socket disconnection during protocol selection
|
* Detect socket disconnection during protocol selection
|
||||||
|
|
||||||
|
|
||||||
## 0.1.0-alpha.2 - 2019-03-29
|
## 0.1.0-alpha.2 - 2019-03-29
|
||||||
### Added
|
### Added
|
||||||
- Added ws::Message::Nop, no-op websockets message
|
* Added ws::Message::Nop, no-op websockets message
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- Do not use thread pool for decompression if chunk size is smaller than 2048.
|
* Do not use thread pool for decompression if chunk size is smaller than 2048.
|
||||||
|
|
||||||
|
|
||||||
## 0.1.0-alpha.1 - 2019-03-28
|
## 0.1.0-alpha.1 - 2019-03-28
|
||||||
- Initial impl
|
* Initial impl
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "actix-http"
|
name = "actix-http"
|
||||||
version = "3.0.0-beta.17"
|
version = "3.0.0-beta.15"
|
||||||
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"]
|
||||||
@@ -45,7 +45,7 @@ __compress = []
|
|||||||
actix-service = "2.0.0"
|
actix-service = "2.0.0"
|
||||||
actix-codec = "0.4.1"
|
actix-codec = "0.4.1"
|
||||||
actix-utils = "3.0.0"
|
actix-utils = "3.0.0"
|
||||||
actix-rt = { version = "2.2", default-features = false }
|
actix-rt = "2.2"
|
||||||
|
|
||||||
ahash = "0.7"
|
ahash = "0.7"
|
||||||
base64 = "0.13"
|
base64 = "0.13"
|
||||||
@@ -55,23 +55,25 @@ bytestring = "1"
|
|||||||
derive_more = "0.99.5"
|
derive_more = "0.99.5"
|
||||||
encoding_rs = "0.8"
|
encoding_rs = "0.8"
|
||||||
futures-core = { version = "0.3.7", default-features = false, features = ["alloc"] }
|
futures-core = { version = "0.3.7", default-features = false, features = ["alloc"] }
|
||||||
|
futures-util = { version = "0.3.7", default-features = false, features = ["alloc", "sink"] }
|
||||||
h2 = "0.3.9"
|
h2 = "0.3.9"
|
||||||
http = "0.2.5"
|
http = "0.2.5"
|
||||||
httparse = "1.5.1"
|
httparse = "1.5.1"
|
||||||
httpdate = "1.0.1"
|
httpdate = "1.0.1"
|
||||||
itoa = "1"
|
itoa = "0.4"
|
||||||
language-tags = "0.3"
|
language-tags = "0.3"
|
||||||
local-channel = "0.1"
|
local-channel = "0.1"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
mime = "0.3"
|
mime = "0.3"
|
||||||
percent-encoding = "2.1"
|
percent-encoding = "2.1"
|
||||||
|
pin-project = "1.0.0"
|
||||||
pin-project-lite = "0.2"
|
pin-project-lite = "0.2"
|
||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
sha-1 = "0.10"
|
sha-1 = "0.9"
|
||||||
smallvec = "1.6.1"
|
smallvec = "1.6.1"
|
||||||
|
|
||||||
# tls
|
# tls
|
||||||
actix-tls = { version = "3.0.0", default-features = false, optional = true }
|
actix-tls = { version = "3.0.0-rc.1", default-features = false, optional = true }
|
||||||
|
|
||||||
# compression
|
# compression
|
||||||
brotli2 = { version="0.3.2", optional = true }
|
brotli2 = { version="0.3.2", optional = true }
|
||||||
@@ -79,15 +81,14 @@ flate2 = { version = "1.0.13", optional = true }
|
|||||||
zstd = { version = "0.9", optional = true }
|
zstd = { version = "0.9", optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
actix-http-test = { version = "3.0.0-beta.10", features = ["openssl"] }
|
actix-http-test = { version = "3.0.0-beta.9", features = ["openssl"] }
|
||||||
actix-server = "2.0.0-rc.2"
|
actix-server = "2.0.0-rc.1"
|
||||||
actix-tls = { version = "3.0.0", features = ["openssl"] }
|
actix-tls = { version = "3.0.0-rc.1", features = ["openssl"] }
|
||||||
actix-web = "4.0.0-beta.16"
|
actix-web = "4.0.0-beta.14"
|
||||||
|
|
||||||
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.9"
|
env_logger = "0.9"
|
||||||
futures-util = { version = "0.3.7", default-features = false, features = ["alloc"] }
|
|
||||||
rcgen = "0.8"
|
rcgen = "0.8"
|
||||||
regex = "1.3"
|
regex = "1.3"
|
||||||
rustls-pemfile = "0.2"
|
rustls-pemfile = "0.2"
|
||||||
@@ -96,7 +97,7 @@ serde_json = "1.0"
|
|||||||
static_assertions = "1"
|
static_assertions = "1"
|
||||||
tls-openssl = { package = "openssl", version = "0.10.9" }
|
tls-openssl = { package = "openssl", version = "0.10.9" }
|
||||||
tls-rustls = { package = "rustls", version = "0.20.0" }
|
tls-rustls = { package = "rustls", version = "0.20.0" }
|
||||||
tokio = { version = "1.8", features = ["net", "rt", "macros"] }
|
tokio = { version = "1.2", features = ["net", "rt", "macros"] }
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "ws"
|
name = "ws"
|
||||||
|
@@ -3,11 +3,11 @@
|
|||||||
> 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.17)
|
[](https://docs.rs/actix-http/3.0.0-beta.15)
|
||||||
[](https://blog.rust-lang.org/2021/05/06/Rust-1.52.0.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.17)
|
[](https://deps.rs/crate/actix-http/3.0.0-beta.15)
|
||||||
[](https://crates.io/crates/actix-http)
|
[](https://crates.io/crates/actix-http)
|
||||||
[](https://discord.gg/NWpN5mmg3x)
|
[](https://discord.gg/NWpN5mmg3x)
|
||||||
|
|
||||||
@@ -54,8 +54,8 @@ async fn main() -> io::Result<()> {
|
|||||||
|
|
||||||
This project is licensed under either of
|
This project is licensed under either of
|
||||||
|
|
||||||
- Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0))
|
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0))
|
||||||
- MIT license ([LICENSE-MIT](LICENSE-MIT) or [http://opensource.org/licenses/MIT](http://opensource.org/licenses/MIT))
|
* MIT license ([LICENSE-MIT](LICENSE-MIT) or [http://opensource.org/licenses/MIT](http://opensource.org/licenses/MIT))
|
||||||
|
|
||||||
at your option.
|
at your option.
|
||||||
|
|
||||||
|
@@ -8,97 +8,76 @@ use std::{
|
|||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
|
|
||||||
use super::{BodySize, MessageBody, MessageBodyMapErr};
|
use super::{BodySize, MessageBody, MessageBodyMapErr};
|
||||||
use crate::body;
|
use crate::Error;
|
||||||
|
|
||||||
/// A boxed message body with boxed errors.
|
/// A boxed message body with boxed errors.
|
||||||
#[derive(Debug)]
|
pub struct BoxBody(Pin<Box<dyn MessageBody<Error = Box<dyn StdError>>>>);
|
||||||
pub struct BoxBody(BoxBodyInner);
|
|
||||||
|
|
||||||
enum BoxBodyInner {
|
|
||||||
None(body::None),
|
|
||||||
Bytes(Bytes),
|
|
||||||
Stream(Pin<Box<dyn MessageBody<Error = Box<dyn StdError>>>>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for BoxBodyInner {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
Self::None(arg0) => f.debug_tuple("None").field(arg0).finish(),
|
|
||||||
Self::Bytes(arg0) => f.debug_tuple("Bytes").field(arg0).finish(),
|
|
||||||
Self::Stream(_) => f.debug_tuple("Stream").field(&"dyn MessageBody").finish(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BoxBody {
|
impl BoxBody {
|
||||||
/// Same as `MessageBody::boxed`.
|
/// Boxes a `MessageBody` and any errors it generates.
|
||||||
///
|
|
||||||
/// If the body type to wrap is unknown or generic it is better to use [`MessageBody::boxed`] to
|
|
||||||
/// avoid double boxing.
|
|
||||||
#[inline]
|
|
||||||
pub fn new<B>(body: B) -> Self
|
pub fn new<B>(body: B) -> Self
|
||||||
where
|
where
|
||||||
B: MessageBody + 'static,
|
B: MessageBody + 'static,
|
||||||
{
|
{
|
||||||
match body.size() {
|
|
||||||
BodySize::None => Self(BoxBodyInner::None(body::None)),
|
|
||||||
_ => match body.try_into_bytes() {
|
|
||||||
Ok(bytes) => Self(BoxBodyInner::Bytes(bytes)),
|
|
||||||
Err(body) => {
|
|
||||||
let body = MessageBodyMapErr::new(body, Into::into);
|
let body = MessageBodyMapErr::new(body, Into::into);
|
||||||
Self(BoxBodyInner::Stream(Box::pin(body)))
|
Self(Box::pin(body))
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a mutable pinned reference to the inner message body type.
|
/// Returns a mutable pinned reference to the inner message body type.
|
||||||
#[inline]
|
pub fn as_pin_mut(&mut self) -> Pin<&mut (dyn MessageBody<Error = Box<dyn StdError>>)> {
|
||||||
pub fn as_pin_mut(&mut self) -> Pin<&mut Self> {
|
self.0.as_mut()
|
||||||
Pin::new(self)
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for BoxBody {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.write_str("BoxBody(dyn MessageBody)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MessageBody for BoxBody {
|
impl MessageBody for BoxBody {
|
||||||
type Error = Box<dyn StdError>;
|
type Error = Error;
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn size(&self) -> BodySize {
|
fn size(&self) -> BodySize {
|
||||||
match &self.0 {
|
self.0.size()
|
||||||
BoxBodyInner::None(none) => none.size(),
|
|
||||||
BoxBodyInner::Bytes(bytes) => bytes.size(),
|
|
||||||
BoxBodyInner::Stream(stream) => stream.size(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn poll_next(
|
fn poll_next(
|
||||||
mut self: Pin<&mut Self>,
|
mut self: Pin<&mut Self>,
|
||||||
cx: &mut Context<'_>,
|
cx: &mut Context<'_>,
|
||||||
) -> Poll<Option<Result<Bytes, Self::Error>>> {
|
) -> Poll<Option<Result<Bytes, Self::Error>>> {
|
||||||
match &mut self.0 {
|
self.0
|
||||||
BoxBodyInner::None(body) => {
|
.as_mut()
|
||||||
Pin::new(body).poll_next(cx).map_err(|err| match err {})
|
.poll_next(cx)
|
||||||
}
|
.map_err(|err| Error::new_body().with_cause(err))
|
||||||
BoxBodyInner::Bytes(body) => {
|
|
||||||
Pin::new(body).poll_next(cx).map_err(|err| match err {})
|
|
||||||
}
|
|
||||||
BoxBodyInner::Stream(body) => Pin::new(body).poll_next(cx),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
fn is_complete_body(&self) -> bool {
|
||||||
fn try_into_bytes(self) -> Result<Bytes, Self> {
|
self.0.is_complete_body()
|
||||||
match self.0 {
|
|
||||||
BoxBodyInner::None(body) => Ok(body.try_into_bytes().unwrap()),
|
|
||||||
BoxBodyInner::Bytes(body) => Ok(body.try_into_bytes().unwrap()),
|
|
||||||
_ => Err(self),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
fn take_complete_body(&mut self) -> Bytes {
|
||||||
fn boxed(self) -> BoxBody {
|
debug_assert!(
|
||||||
self
|
self.is_complete_body(),
|
||||||
|
"boxed type does not allow taking complete body; caller should make sure to \
|
||||||
|
call `is_complete_body` first",
|
||||||
|
);
|
||||||
|
|
||||||
|
// we do not have DerefMut access to call take_complete_body directly but since
|
||||||
|
// is_complete_body is true we should expect the entire bytes chunk in one poll_next
|
||||||
|
|
||||||
|
let waker = futures_util::task::noop_waker();
|
||||||
|
let mut cx = Context::from_waker(&waker);
|
||||||
|
|
||||||
|
match self.as_pin_mut().poll_next(&mut cx) {
|
||||||
|
Poll::Ready(Some(Ok(data))) => data,
|
||||||
|
_ => {
|
||||||
|
panic!(
|
||||||
|
"boxed type indicated it allows taking complete body but failed to \
|
||||||
|
return Bytes when polled",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -74,22 +74,18 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn try_into_bytes(self) -> Result<Bytes, Self> {
|
fn is_complete_body(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
EitherBody::Left { body } => body
|
EitherBody::Left { body } => body.is_complete_body(),
|
||||||
.try_into_bytes()
|
EitherBody::Right { body } => body.is_complete_body(),
|
||||||
.map_err(|body| EitherBody::Left { body }),
|
|
||||||
EitherBody::Right { body } => body
|
|
||||||
.try_into_bytes()
|
|
||||||
.map_err(|body| EitherBody::Right { body }),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn boxed(self) -> BoxBody {
|
fn take_complete_body(&mut self) -> Bytes {
|
||||||
match self {
|
match self {
|
||||||
EitherBody::Left { body } => body.boxed(),
|
EitherBody::Left { body } => body.take_complete_body(),
|
||||||
EitherBody::Right { body } => body.boxed(),
|
EitherBody::Right { body } => body.take_complete_body(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -12,20 +12,16 @@ use bytes::{Bytes, BytesMut};
|
|||||||
use futures_core::ready;
|
use futures_core::ready;
|
||||||
use pin_project_lite::pin_project;
|
use pin_project_lite::pin_project;
|
||||||
|
|
||||||
use super::{BodySize, BoxBody};
|
use super::BodySize;
|
||||||
|
|
||||||
/// An interface types that can converted to bytes and used as response bodies.
|
/// An interface types that can converted to bytes and used as response bodies.
|
||||||
// TODO: examples
|
// TODO: examples
|
||||||
pub trait MessageBody {
|
pub trait MessageBody {
|
||||||
/// The type of error that will be returned if streaming body fails.
|
// TODO: consider this bound to only fmt::Display since the error type is not really used
|
||||||
///
|
// and there is an impl for Into<Box<StdError>> on String
|
||||||
/// Since it is not appropriate to generate a response mid-stream, it only requires `Error` for
|
|
||||||
/// internal use and logging.
|
|
||||||
type Error: Into<Box<dyn StdError>>;
|
type Error: Into<Box<dyn StdError>>;
|
||||||
|
|
||||||
/// Body size hint.
|
/// Body size hint.
|
||||||
///
|
|
||||||
/// If [`BodySize::None`] is returned, optimizations that skip reading the body are allowed.
|
|
||||||
fn size(&self) -> BodySize;
|
fn size(&self) -> BodySize;
|
||||||
|
|
||||||
/// Attempt to pull out the next chunk of body bytes.
|
/// Attempt to pull out the next chunk of body bytes.
|
||||||
@@ -35,32 +31,51 @@ pub trait MessageBody {
|
|||||||
cx: &mut Context<'_>,
|
cx: &mut Context<'_>,
|
||||||
) -> Poll<Option<Result<Bytes, Self::Error>>>;
|
) -> Poll<Option<Result<Bytes, Self::Error>>>;
|
||||||
|
|
||||||
/// Try to convert into the complete chunk of body bytes.
|
/// Returns true if entire body bytes chunk is obtainable in one call to `poll_next`.
|
||||||
///
|
///
|
||||||
/// Implement this method if the entire body can be trivially extracted. This is useful for
|
/// This method's implementation should agree with [`take_complete_body`] and should always be
|
||||||
/// optimizations where `poll_next` calls can be avoided.
|
/// checked before taking the body.
|
||||||
///
|
///
|
||||||
/// Body types with [`BodySize::None`] are allowed to return empty `Bytes`. Although, if calling
|
/// The default implementation returns `false.
|
||||||
/// this method, it is recommended to check `size` first and return early.
|
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// [`take_complete_body`]: MessageBody::take_complete_body
|
||||||
/// The default implementation will error and return the original type back to the caller for
|
fn is_complete_body(&self) -> bool {
|
||||||
/// further use.
|
false
|
||||||
#[inline]
|
|
||||||
fn try_into_bytes(self) -> Result<Bytes, Self>
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
Err(self)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts this body into `BoxBody`.
|
/// Returns the complete chunk of body bytes.
|
||||||
#[inline]
|
///
|
||||||
fn boxed(self) -> BoxBody
|
/// Implementors of this method should note the following:
|
||||||
where
|
/// - It is acceptable to skip the omit checks of [`is_complete_body`]. The responsibility of
|
||||||
Self: Sized + 'static,
|
/// performing this check is delegated to the caller.
|
||||||
{
|
/// - If the result of [`is_complete_body`] is conditional, that condition should be given
|
||||||
BoxBody::new(self)
|
/// equivalent attention here.
|
||||||
|
/// - A second call call to [`take_complete_body`] should return an empty `Bytes` or panic.
|
||||||
|
/// - A call to [`poll_next`] after calling [`take_complete_body`] should return `None` unless
|
||||||
|
/// the chunk is guaranteed to be empty.
|
||||||
|
///
|
||||||
|
/// The default implementation panics unconditionally, indicating a control flow bug in the
|
||||||
|
/// calling code.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
/// With a correct implementation, panics if called without first checking [`is_complete_body`].
|
||||||
|
///
|
||||||
|
/// [`is_complete_body`]: MessageBody::is_complete_body
|
||||||
|
/// [`take_complete_body`]: MessageBody::take_complete_body
|
||||||
|
/// [`poll_next`]: MessageBody::poll_next
|
||||||
|
fn take_complete_body(&mut self) -> Bytes {
|
||||||
|
assert!(
|
||||||
|
self.is_complete_body(),
|
||||||
|
"type ({}) allows taking complete body but did not provide an implementation \
|
||||||
|
of `take_complete_body`",
|
||||||
|
std::any::type_name::<Self>()
|
||||||
|
);
|
||||||
|
|
||||||
|
unimplemented!(
|
||||||
|
"type ({}) does not allow taking complete body; caller should make sure to \
|
||||||
|
check `is_complete_body` first",
|
||||||
|
std::any::type_name::<Self>()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,6 +95,14 @@ mod foreign_impls {
|
|||||||
) -> Poll<Option<Result<Bytes, Self::Error>>> {
|
) -> Poll<Option<Result<Bytes, Self::Error>>> {
|
||||||
match *self {}
|
match *self {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_complete_body(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn take_complete_body(&mut self) -> Bytes {
|
||||||
|
match *self {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MessageBody for () {
|
impl MessageBody for () {
|
||||||
@@ -99,14 +122,19 @@ mod foreign_impls {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn try_into_bytes(self) -> Result<Bytes, Self> {
|
fn is_complete_body(&self) -> bool {
|
||||||
Ok(Bytes::new())
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn take_complete_body(&mut self) -> Bytes {
|
||||||
|
Bytes::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<B> MessageBody for Box<B>
|
impl<B> MessageBody for Box<B>
|
||||||
where
|
where
|
||||||
B: MessageBody + Unpin + ?Sized,
|
B: MessageBody + Unpin,
|
||||||
{
|
{
|
||||||
type Error = B::Error;
|
type Error = B::Error;
|
||||||
|
|
||||||
@@ -122,11 +150,21 @@ mod foreign_impls {
|
|||||||
) -> Poll<Option<Result<Bytes, Self::Error>>> {
|
) -> Poll<Option<Result<Bytes, Self::Error>>> {
|
||||||
Pin::new(self.get_mut().as_mut()).poll_next(cx)
|
Pin::new(self.get_mut().as_mut()).poll_next(cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn is_complete_body(&self) -> bool {
|
||||||
|
self.as_ref().is_complete_body()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn take_complete_body(&mut self) -> Bytes {
|
||||||
|
self.as_mut().take_complete_body()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<B> MessageBody for Pin<Box<B>>
|
impl<B> MessageBody for Pin<Box<B>>
|
||||||
where
|
where
|
||||||
B: MessageBody + ?Sized,
|
B: MessageBody,
|
||||||
{
|
{
|
||||||
type Error = B::Error;
|
type Error = B::Error;
|
||||||
|
|
||||||
@@ -137,10 +175,42 @@ mod foreign_impls {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn poll_next(
|
fn poll_next(
|
||||||
self: Pin<&mut Self>,
|
mut self: Pin<&mut Self>,
|
||||||
cx: &mut Context<'_>,
|
cx: &mut Context<'_>,
|
||||||
) -> Poll<Option<Result<Bytes, Self::Error>>> {
|
) -> Poll<Option<Result<Bytes, Self::Error>>> {
|
||||||
self.get_mut().as_mut().poll_next(cx)
|
self.as_mut().poll_next(cx)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn is_complete_body(&self) -> bool {
|
||||||
|
self.as_ref().is_complete_body()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn take_complete_body(&mut self) -> Bytes {
|
||||||
|
debug_assert!(
|
||||||
|
self.is_complete_body(),
|
||||||
|
"inner type \"{}\" does not allow taking complete body; caller should make sure to \
|
||||||
|
call `is_complete_body` first",
|
||||||
|
std::any::type_name::<B>(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// we do not have DerefMut access to call take_complete_body directly but since
|
||||||
|
// is_complete_body is true we should expect the entire bytes chunk in one poll_next
|
||||||
|
|
||||||
|
let waker = futures_util::task::noop_waker();
|
||||||
|
let mut cx = Context::from_waker(&waker);
|
||||||
|
|
||||||
|
match self.as_mut().poll_next(&mut cx) {
|
||||||
|
Poll::Ready(Some(Ok(data))) => data,
|
||||||
|
_ => {
|
||||||
|
panic!(
|
||||||
|
"inner type \"{}\" indicated it allows taking complete body but failed to \
|
||||||
|
return Bytes when polled",
|
||||||
|
std::any::type_name::<B>()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,21 +222,25 @@ mod foreign_impls {
|
|||||||
BodySize::Sized(self.len() as u64)
|
BodySize::Sized(self.len() as u64)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn poll_next(
|
fn poll_next(
|
||||||
self: Pin<&mut Self>,
|
mut self: Pin<&mut Self>,
|
||||||
_cx: &mut Context<'_>,
|
_cx: &mut Context<'_>,
|
||||||
) -> Poll<Option<Result<Bytes, Self::Error>>> {
|
) -> Poll<Option<Result<Bytes, Self::Error>>> {
|
||||||
if self.is_empty() {
|
if self.is_empty() {
|
||||||
Poll::Ready(None)
|
Poll::Ready(None)
|
||||||
} else {
|
} else {
|
||||||
Poll::Ready(Some(Ok(Bytes::from_static(mem::take(self.get_mut())))))
|
Poll::Ready(Some(Ok(self.take_complete_body())))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn try_into_bytes(self) -> Result<Bytes, Self> {
|
fn is_complete_body(&self) -> bool {
|
||||||
Ok(Bytes::from_static(self))
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn take_complete_body(&mut self) -> Bytes {
|
||||||
|
Bytes::from_static(mem::take(self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,21 +252,25 @@ mod foreign_impls {
|
|||||||
BodySize::Sized(self.len() as u64)
|
BodySize::Sized(self.len() as u64)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn poll_next(
|
fn poll_next(
|
||||||
self: Pin<&mut Self>,
|
mut self: Pin<&mut Self>,
|
||||||
_cx: &mut Context<'_>,
|
_cx: &mut Context<'_>,
|
||||||
) -> Poll<Option<Result<Bytes, Self::Error>>> {
|
) -> Poll<Option<Result<Bytes, Self::Error>>> {
|
||||||
if self.is_empty() {
|
if self.is_empty() {
|
||||||
Poll::Ready(None)
|
Poll::Ready(None)
|
||||||
} else {
|
} else {
|
||||||
Poll::Ready(Some(Ok(mem::take(self.get_mut()))))
|
Poll::Ready(Some(Ok(self.take_complete_body())))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn try_into_bytes(self) -> Result<Bytes, Self> {
|
fn is_complete_body(&self) -> bool {
|
||||||
Ok(self)
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn take_complete_body(&mut self) -> Bytes {
|
||||||
|
mem::take(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,21 +282,25 @@ mod foreign_impls {
|
|||||||
BodySize::Sized(self.len() as u64)
|
BodySize::Sized(self.len() as u64)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn poll_next(
|
fn poll_next(
|
||||||
self: Pin<&mut Self>,
|
mut self: Pin<&mut Self>,
|
||||||
_cx: &mut Context<'_>,
|
_cx: &mut Context<'_>,
|
||||||
) -> Poll<Option<Result<Bytes, Self::Error>>> {
|
) -> Poll<Option<Result<Bytes, Self::Error>>> {
|
||||||
if self.is_empty() {
|
if self.is_empty() {
|
||||||
Poll::Ready(None)
|
Poll::Ready(None)
|
||||||
} else {
|
} else {
|
||||||
Poll::Ready(Some(Ok(mem::take(self.get_mut()).freeze())))
|
Poll::Ready(Some(Ok(self.take_complete_body())))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn try_into_bytes(self) -> Result<Bytes, Self> {
|
fn is_complete_body(&self) -> bool {
|
||||||
Ok(self.freeze())
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn take_complete_body(&mut self) -> Bytes {
|
||||||
|
mem::take(self).freeze()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -230,21 +312,25 @@ mod foreign_impls {
|
|||||||
BodySize::Sized(self.len() as u64)
|
BodySize::Sized(self.len() as u64)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn poll_next(
|
fn poll_next(
|
||||||
self: Pin<&mut Self>,
|
mut self: Pin<&mut Self>,
|
||||||
_cx: &mut Context<'_>,
|
_cx: &mut Context<'_>,
|
||||||
) -> Poll<Option<Result<Bytes, Self::Error>>> {
|
) -> Poll<Option<Result<Bytes, Self::Error>>> {
|
||||||
if self.is_empty() {
|
if self.is_empty() {
|
||||||
Poll::Ready(None)
|
Poll::Ready(None)
|
||||||
} else {
|
} else {
|
||||||
Poll::Ready(Some(Ok(mem::take(self.get_mut()).into())))
|
Poll::Ready(Some(Ok(self.take_complete_body())))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn try_into_bytes(self) -> Result<Bytes, Self> {
|
fn is_complete_body(&self) -> bool {
|
||||||
Ok(Bytes::from(self))
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn take_complete_body(&mut self) -> Bytes {
|
||||||
|
Bytes::from(mem::take(self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,7 +342,6 @@ mod foreign_impls {
|
|||||||
BodySize::Sized(self.len() as u64)
|
BodySize::Sized(self.len() as u64)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn poll_next(
|
fn poll_next(
|
||||||
self: Pin<&mut Self>,
|
self: Pin<&mut Self>,
|
||||||
_cx: &mut Context<'_>,
|
_cx: &mut Context<'_>,
|
||||||
@@ -271,8 +356,13 @@ mod foreign_impls {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn try_into_bytes(self) -> Result<Bytes, Self> {
|
fn is_complete_body(&self) -> bool {
|
||||||
Ok(Bytes::from_static(self.as_bytes()))
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn take_complete_body(&mut self) -> Bytes {
|
||||||
|
Bytes::from_static(mem::take(self).as_bytes())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -284,7 +374,6 @@ mod foreign_impls {
|
|||||||
BodySize::Sized(self.len() as u64)
|
BodySize::Sized(self.len() as u64)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn poll_next(
|
fn poll_next(
|
||||||
self: Pin<&mut Self>,
|
self: Pin<&mut Self>,
|
||||||
_cx: &mut Context<'_>,
|
_cx: &mut Context<'_>,
|
||||||
@@ -298,8 +387,13 @@ mod foreign_impls {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn try_into_bytes(self) -> Result<Bytes, Self> {
|
fn is_complete_body(&self) -> bool {
|
||||||
Ok(Bytes::from(self))
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn take_complete_body(&mut self) -> Bytes {
|
||||||
|
Bytes::from(mem::take(self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -311,7 +405,6 @@ mod foreign_impls {
|
|||||||
BodySize::Sized(self.len() as u64)
|
BodySize::Sized(self.len() as u64)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn poll_next(
|
fn poll_next(
|
||||||
self: Pin<&mut Self>,
|
self: Pin<&mut Self>,
|
||||||
_cx: &mut Context<'_>,
|
_cx: &mut Context<'_>,
|
||||||
@@ -321,8 +414,13 @@ mod foreign_impls {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn try_into_bytes(self) -> Result<Bytes, Self> {
|
fn is_complete_body(&self) -> bool {
|
||||||
Ok(self.into_bytes())
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn take_complete_body(&mut self) -> Bytes {
|
||||||
|
mem::take(self).into_bytes()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -377,12 +475,6 @@ where
|
|||||||
None => Poll::Ready(None),
|
None => Poll::Ready(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn try_into_bytes(self) -> Result<Bytes, Self> {
|
|
||||||
let Self { body, mapper } = self;
|
|
||||||
body.try_into_bytes().map_err(|body| Self { body, mapper })
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -392,7 +484,6 @@ mod tests {
|
|||||||
use bytes::{Bytes, BytesMut};
|
use bytes::{Bytes, BytesMut};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::body::{self, EitherBody};
|
|
||||||
|
|
||||||
macro_rules! assert_poll_next {
|
macro_rules! assert_poll_next {
|
||||||
($pin:expr, $exp:expr) => {
|
($pin:expr, $exp:expr) => {
|
||||||
@@ -494,45 +585,49 @@ mod tests {
|
|||||||
assert_poll_next!(pl, Bytes::from("test"));
|
assert_poll_next!(pl, Bytes::from("test"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[test]
|
||||||
async fn complete_body_combinators() {
|
fn take_string() {
|
||||||
let body = Bytes::from_static(b"test");
|
let mut data = "test".repeat(2);
|
||||||
let body = BoxBody::new(body);
|
let data_bytes = Bytes::from(data.clone());
|
||||||
let body = EitherBody::<_, ()>::left(body);
|
assert!(data.is_complete_body());
|
||||||
let body = EitherBody::<(), _>::right(body);
|
assert_eq!(data.take_complete_body(), data_bytes);
|
||||||
// Do not support try_into_bytes:
|
|
||||||
// let body = Box::new(body);
|
|
||||||
// let body = Box::pin(body);
|
|
||||||
|
|
||||||
assert_eq!(body.try_into_bytes().unwrap(), Bytes::from("test"));
|
let mut big_data = "test".repeat(64 * 1024);
|
||||||
|
let data_bytes = Bytes::from(big_data.clone());
|
||||||
|
assert!(big_data.is_complete_body());
|
||||||
|
assert_eq!(big_data.take_complete_body(), data_bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[test]
|
||||||
async fn complete_body_combinators_poll() {
|
fn take_boxed_equivalence() {
|
||||||
let body = Bytes::from_static(b"test");
|
let mut data = Bytes::from_static(b"test");
|
||||||
let body = BoxBody::new(body);
|
assert!(data.is_complete_body());
|
||||||
let body = EitherBody::<_, ()>::left(body);
|
assert_eq!(data.take_complete_body(), b"test".as_ref());
|
||||||
let body = EitherBody::<(), _>::right(body);
|
|
||||||
let mut body = body;
|
|
||||||
|
|
||||||
assert_eq!(body.size(), BodySize::Sized(4));
|
let mut data = Box::new(Bytes::from_static(b"test"));
|
||||||
assert_poll_next!(Pin::new(&mut body), Bytes::from("test"));
|
assert!(data.is_complete_body());
|
||||||
assert_poll_next_none!(Pin::new(&mut body));
|
assert_eq!(data.take_complete_body(), b"test".as_ref());
|
||||||
|
|
||||||
|
let mut data = Box::pin(Bytes::from_static(b"test"));
|
||||||
|
assert!(data.is_complete_body());
|
||||||
|
assert_eq!(data.take_complete_body(), b"test".as_ref());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[test]
|
||||||
async fn none_body_combinators() {
|
fn take_policy() {
|
||||||
fn none_body() -> BoxBody {
|
let mut data = Bytes::from_static(b"test");
|
||||||
let body = body::None;
|
// first call returns chunk
|
||||||
let body = BoxBody::new(body);
|
assert_eq!(data.take_complete_body(), b"test".as_ref());
|
||||||
let body = EitherBody::<_, ()>::left(body);
|
// second call returns empty
|
||||||
let body = EitherBody::<(), _>::right(body);
|
assert_eq!(data.take_complete_body(), b"".as_ref());
|
||||||
body.boxed()
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_eq!(none_body().size(), BodySize::None);
|
let waker = futures_util::task::noop_waker();
|
||||||
assert_eq!(none_body().try_into_bytes().unwrap(), Bytes::new());
|
let mut cx = Context::from_waker(&waker);
|
||||||
assert_poll_next_none!(Pin::new(&mut none_body()));
|
let mut data = Bytes::from_static(b"test");
|
||||||
|
// take returns whole chunk
|
||||||
|
assert_eq!(data.take_complete_body(), b"test".as_ref());
|
||||||
|
// subsequent poll_next returns None
|
||||||
|
assert_eq!(Pin::new(&mut data).poll_next(&mut cx), Poll::Ready(None));
|
||||||
}
|
}
|
||||||
|
|
||||||
// down-casting used to be done with a method on MessageBody trait
|
// down-casting used to be done with a method on MessageBody trait
|
||||||
|
@@ -42,7 +42,12 @@ impl MessageBody for None {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn try_into_bytes(self) -> Result<Bytes, Self> {
|
fn is_complete_body(&self) -> bool {
|
||||||
Ok(Bytes::new())
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn take_complete_body(&mut self) -> Bytes {
|
||||||
|
Bytes::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -36,7 +36,6 @@ where
|
|||||||
<S::Service as Service<Request>>::Future: 'static,
|
<S::Service as Service<Request>>::Future: 'static,
|
||||||
{
|
{
|
||||||
/// Create instance of `ServiceConfigBuilder`
|
/// Create instance of `ServiceConfigBuilder`
|
||||||
#[allow(clippy::new_without_default)]
|
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
HttpServiceBuilder {
|
HttpServiceBuilder {
|
||||||
keep_alive: KeepAlive::Timeout(5),
|
keep_alive: KeepAlive::Timeout(5),
|
||||||
|
@@ -28,15 +28,12 @@ use crate::{
|
|||||||
|
|
||||||
const MAX_CHUNK_SIZE_DECODE_IN_PLACE: usize = 2049;
|
const MAX_CHUNK_SIZE_DECODE_IN_PLACE: usize = 2049;
|
||||||
|
|
||||||
pin_project_lite::pin_project! {
|
|
||||||
pub struct Decoder<S> {
|
pub struct Decoder<S> {
|
||||||
decoder: Option<ContentDecoder>,
|
decoder: Option<ContentDecoder>,
|
||||||
#[pin]
|
|
||||||
stream: S,
|
stream: S,
|
||||||
eof: bool,
|
eof: bool,
|
||||||
fut: Option<JoinHandle<Result<(Option<Bytes>, ContentDecoder), io::Error>>>,
|
fut: Option<JoinHandle<Result<(Option<Bytes>, ContentDecoder), io::Error>>>,
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<S> Decoder<S>
|
impl<S> Decoder<S>
|
||||||
where
|
where
|
||||||
@@ -92,44 +89,42 @@ where
|
|||||||
|
|
||||||
impl<S> Stream for Decoder<S>
|
impl<S> Stream for Decoder<S>
|
||||||
where
|
where
|
||||||
S: Stream<Item = Result<Bytes, PayloadError>>,
|
S: Stream<Item = Result<Bytes, PayloadError>> + Unpin,
|
||||||
{
|
{
|
||||||
type Item = Result<Bytes, PayloadError>;
|
type Item = Result<Bytes, PayloadError>;
|
||||||
|
|
||||||
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||||
let mut this = self.project();
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
if let Some(ref mut fut) = this.fut {
|
if let Some(ref mut fut) = self.fut {
|
||||||
let (chunk, decoder) =
|
let (chunk, decoder) =
|
||||||
ready!(Pin::new(fut).poll(cx)).map_err(|_| BlockingError)??;
|
ready!(Pin::new(fut).poll(cx)).map_err(|_| BlockingError)??;
|
||||||
|
|
||||||
*this.decoder = Some(decoder);
|
self.decoder = Some(decoder);
|
||||||
this.fut.take();
|
self.fut.take();
|
||||||
|
|
||||||
if let Some(chunk) = chunk {
|
if let Some(chunk) = chunk {
|
||||||
return Poll::Ready(Some(Ok(chunk)));
|
return Poll::Ready(Some(Ok(chunk)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if *this.eof {
|
if self.eof {
|
||||||
return Poll::Ready(None);
|
return Poll::Ready(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
match ready!(this.stream.as_mut().poll_next(cx)) {
|
match ready!(Pin::new(&mut self.stream).poll_next(cx)) {
|
||||||
Some(Err(err)) => return Poll::Ready(Some(Err(err))),
|
Some(Err(err)) => return Poll::Ready(Some(Err(err))),
|
||||||
|
|
||||||
Some(Ok(chunk)) => {
|
Some(Ok(chunk)) => {
|
||||||
if let Some(mut decoder) = this.decoder.take() {
|
if let Some(mut decoder) = self.decoder.take() {
|
||||||
if chunk.len() < MAX_CHUNK_SIZE_DECODE_IN_PLACE {
|
if chunk.len() < MAX_CHUNK_SIZE_DECODE_IN_PLACE {
|
||||||
let chunk = decoder.feed_data(chunk)?;
|
let chunk = decoder.feed_data(chunk)?;
|
||||||
*this.decoder = Some(decoder);
|
self.decoder = Some(decoder);
|
||||||
|
|
||||||
if let Some(chunk) = chunk {
|
if let Some(chunk) = chunk {
|
||||||
return Poll::Ready(Some(Ok(chunk)));
|
return Poll::Ready(Some(Ok(chunk)));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
*this.fut = Some(spawn_blocking(move || {
|
self.fut = Some(spawn_blocking(move || {
|
||||||
let chunk = decoder.feed_data(chunk)?;
|
let chunk = decoder.feed_data(chunk)?;
|
||||||
Ok((chunk, decoder))
|
Ok((chunk, decoder))
|
||||||
}));
|
}));
|
||||||
@@ -142,9 +137,9 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
None => {
|
None => {
|
||||||
*this.eof = true;
|
self.eof = true;
|
||||||
|
|
||||||
return if let Some(mut decoder) = this.decoder.take() {
|
return if let Some(mut decoder) = self.decoder.take() {
|
||||||
match decoder.feed_eof() {
|
match decoder.feed_eof() {
|
||||||
Ok(Some(res)) => Poll::Ready(Some(Ok(res))),
|
Ok(Some(res)) => Poll::Ready(Some(Ok(res))),
|
||||||
Ok(None) => Poll::Ready(None),
|
Ok(None) => Poll::Ready(None),
|
||||||
|
@@ -25,7 +25,7 @@ use zstd::stream::write::Encoder as ZstdEncoder;
|
|||||||
|
|
||||||
use super::Writer;
|
use super::Writer;
|
||||||
use crate::{
|
use crate::{
|
||||||
body::{self, BodySize, MessageBody},
|
body::{BodySize, MessageBody},
|
||||||
error::BlockingError,
|
error::BlockingError,
|
||||||
header::{self, ContentEncoding, HeaderValue, CONTENT_ENCODING},
|
header::{self, ContentEncoding, HeaderValue, CONTENT_ENCODING},
|
||||||
ResponseHead, StatusCode,
|
ResponseHead, StatusCode,
|
||||||
@@ -46,16 +46,14 @@ pin_project! {
|
|||||||
impl<B: MessageBody> Encoder<B> {
|
impl<B: MessageBody> Encoder<B> {
|
||||||
fn none() -> Self {
|
fn none() -> Self {
|
||||||
Encoder {
|
Encoder {
|
||||||
body: EncoderBody::None {
|
body: EncoderBody::None,
|
||||||
body: body::None::new(),
|
|
||||||
},
|
|
||||||
encoder: None,
|
encoder: None,
|
||||||
fut: None,
|
fut: None,
|
||||||
eof: true,
|
eof: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn response(encoding: ContentEncoding, head: &mut ResponseHead, body: B) -> Self {
|
pub fn response(encoding: ContentEncoding, head: &mut ResponseHead, mut body: B) -> Self {
|
||||||
let can_encode = !(head.headers().contains_key(&CONTENT_ENCODING)
|
let can_encode = !(head.headers().contains_key(&CONTENT_ENCODING)
|
||||||
|| head.status == StatusCode::SWITCHING_PROTOCOLS
|
|| head.status == StatusCode::SWITCHING_PROTOCOLS
|
||||||
|| head.status == StatusCode::NO_CONTENT
|
|| head.status == StatusCode::NO_CONTENT
|
||||||
@@ -67,9 +65,11 @@ impl<B: MessageBody> Encoder<B> {
|
|||||||
return Self::none();
|
return Self::none();
|
||||||
}
|
}
|
||||||
|
|
||||||
let body = match body.try_into_bytes() {
|
let body = if body.is_complete_body() {
|
||||||
Ok(body) => EncoderBody::Full { body },
|
let body = body.take_complete_body();
|
||||||
Err(body) => EncoderBody::Stream { body },
|
EncoderBody::Full { body }
|
||||||
|
} else {
|
||||||
|
EncoderBody::Stream { body }
|
||||||
};
|
};
|
||||||
|
|
||||||
if can_encode {
|
if can_encode {
|
||||||
@@ -98,7 +98,7 @@ impl<B: MessageBody> Encoder<B> {
|
|||||||
pin_project! {
|
pin_project! {
|
||||||
#[project = EncoderBodyProj]
|
#[project = EncoderBodyProj]
|
||||||
enum EncoderBody<B> {
|
enum EncoderBody<B> {
|
||||||
None { body: body::None },
|
None,
|
||||||
Full { body: Bytes },
|
Full { body: Bytes },
|
||||||
Stream { #[pin] body: B },
|
Stream { #[pin] body: B },
|
||||||
}
|
}
|
||||||
@@ -110,10 +110,9 @@ where
|
|||||||
{
|
{
|
||||||
type Error = EncoderError;
|
type Error = EncoderError;
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn size(&self) -> BodySize {
|
fn size(&self) -> BodySize {
|
||||||
match self {
|
match self {
|
||||||
EncoderBody::None { body } => body.size(),
|
EncoderBody::None => BodySize::None,
|
||||||
EncoderBody::Full { body } => body.size(),
|
EncoderBody::Full { body } => body.size(),
|
||||||
EncoderBody::Stream { body } => body.size(),
|
EncoderBody::Stream { body } => body.size(),
|
||||||
}
|
}
|
||||||
@@ -124,9 +123,7 @@ where
|
|||||||
cx: &mut Context<'_>,
|
cx: &mut Context<'_>,
|
||||||
) -> Poll<Option<Result<Bytes, Self::Error>>> {
|
) -> Poll<Option<Result<Bytes, Self::Error>>> {
|
||||||
match self.project() {
|
match self.project() {
|
||||||
EncoderBodyProj::None { body } => {
|
EncoderBodyProj::None => Poll::Ready(None),
|
||||||
Pin::new(body).poll_next(cx).map_err(|err| match err {})
|
|
||||||
}
|
|
||||||
EncoderBodyProj::Full { body } => {
|
EncoderBodyProj::Full { body } => {
|
||||||
Pin::new(body).poll_next(cx).map_err(|err| match err {})
|
Pin::new(body).poll_next(cx).map_err(|err| match err {})
|
||||||
}
|
}
|
||||||
@@ -136,15 +133,21 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
fn is_complete_body(&self) -> bool {
|
||||||
fn try_into_bytes(self) -> Result<Bytes, Self>
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
match self {
|
match self {
|
||||||
EncoderBody::None { body } => Ok(body.try_into_bytes().unwrap()),
|
EncoderBody::None => true,
|
||||||
EncoderBody::Full { body } => Ok(body.try_into_bytes().unwrap()),
|
EncoderBody::Full { .. } => true,
|
||||||
_ => Err(self),
|
EncoderBody::Stream { .. } => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn take_complete_body(&mut self) -> Bytes {
|
||||||
|
match self {
|
||||||
|
EncoderBody::None => Bytes::new(),
|
||||||
|
EncoderBody::Full { body } => body.take_complete_body(),
|
||||||
|
EncoderBody::Stream { .. } => {
|
||||||
|
panic!("EncoderBody::Stream variant cannot be taken")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -155,7 +158,6 @@ where
|
|||||||
{
|
{
|
||||||
type Error = EncoderError;
|
type Error = EncoderError;
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn size(&self) -> BodySize {
|
fn size(&self) -> BodySize {
|
||||||
if self.encoder.is_some() {
|
if self.encoder.is_some() {
|
||||||
BodySize::Stream
|
BodySize::Stream
|
||||||
@@ -232,21 +234,19 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
fn is_complete_body(&self) -> bool {
|
||||||
fn try_into_bytes(mut self) -> Result<Bytes, Self>
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
if self.encoder.is_some() {
|
if self.encoder.is_some() {
|
||||||
Err(self)
|
false
|
||||||
} else {
|
} else {
|
||||||
match self.body.try_into_bytes() {
|
self.body.is_complete_body()
|
||||||
Ok(body) => Ok(body),
|
|
||||||
Err(body) => {
|
|
||||||
self.body = body;
|
|
||||||
Err(self)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn take_complete_body(&mut self) -> Bytes {
|
||||||
|
if self.encoder.is_some() {
|
||||||
|
panic!("compressed body stream cannot be taken")
|
||||||
|
} else {
|
||||||
|
self.body.take_complete_body()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -332,28 +332,31 @@ impl From<PayloadError> for Error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A set of errors that can occur during dispatching HTTP requests.
|
/// A set of errors that can occur during dispatching HTTP requests.
|
||||||
#[derive(Debug, Display, From)]
|
#[derive(Debug, Display, Error, From)]
|
||||||
|
#[non_exhaustive]
|
||||||
pub enum DispatchError {
|
pub enum DispatchError {
|
||||||
/// Service error.
|
/// Service error
|
||||||
|
// FIXME: display and error type
|
||||||
#[display(fmt = "Service Error")]
|
#[display(fmt = "Service Error")]
|
||||||
Service(Response<BoxBody>),
|
Service(#[error(not(source))] Response<BoxBody>),
|
||||||
|
|
||||||
/// Body streaming error.
|
/// Body error
|
||||||
#[display(fmt = "Body error: {}", _0)]
|
// FIXME: display and error type
|
||||||
Body(Box<dyn StdError>),
|
#[display(fmt = "Body Error")]
|
||||||
|
Body(#[error(not(source))] Box<dyn StdError>),
|
||||||
|
|
||||||
/// Upgrade service error.
|
/// Upgrade service error
|
||||||
Upgrade,
|
Upgrade,
|
||||||
|
|
||||||
/// An `io::Error` that occurred while trying to read or write to a network stream.
|
/// An `io::Error` that occurred while trying to read or write to a network stream.
|
||||||
#[display(fmt = "IO error: {}", _0)]
|
#[display(fmt = "IO error: {}", _0)]
|
||||||
Io(io::Error),
|
Io(io::Error),
|
||||||
|
|
||||||
/// Request parse error.
|
/// Http request parse error.
|
||||||
#[display(fmt = "Request parse error: {}", _0)]
|
#[display(fmt = "Parse error: {}", _0)]
|
||||||
Parse(ParseError),
|
Parse(ParseError),
|
||||||
|
|
||||||
/// HTTP/2 error.
|
/// Http/2 error
|
||||||
#[display(fmt = "{}", _0)]
|
#[display(fmt = "{}", _0)]
|
||||||
H2(h2::Error),
|
H2(h2::Error),
|
||||||
|
|
||||||
@@ -365,23 +368,21 @@ pub enum DispatchError {
|
|||||||
#[display(fmt = "Connection shutdown timeout")]
|
#[display(fmt = "Connection shutdown timeout")]
|
||||||
DisconnectTimeout,
|
DisconnectTimeout,
|
||||||
|
|
||||||
/// Internal error.
|
/// Payload is not consumed
|
||||||
|
#[display(fmt = "Task is completed but request's payload is not consumed")]
|
||||||
|
PayloadIsNotConsumed,
|
||||||
|
|
||||||
|
/// Malformed request
|
||||||
|
#[display(fmt = "Malformed request")]
|
||||||
|
MalformedRequest,
|
||||||
|
|
||||||
|
/// Internal error
|
||||||
#[display(fmt = "Internal error")]
|
#[display(fmt = "Internal error")]
|
||||||
InternalError,
|
InternalError,
|
||||||
}
|
|
||||||
|
|
||||||
impl StdError for DispatchError {
|
/// Unknown error
|
||||||
fn source(&self) -> Option<&(dyn StdError + 'static)> {
|
#[display(fmt = "Unknown error")]
|
||||||
match self {
|
Unknown,
|
||||||
// TODO: error source extraction?
|
|
||||||
DispatchError::Service(_res) => None,
|
|
||||||
DispatchError::Body(err) => Some(&**err),
|
|
||||||
DispatchError::Io(err) => Some(err),
|
|
||||||
DispatchError::Parse(err) => Some(err),
|
|
||||||
DispatchError::H2(err) => Some(err),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A set of error that can occur during parsing content type.
|
/// A set of error that can occur during parsing content type.
|
||||||
|
@@ -5,15 +5,13 @@ use bitflags::bitflags;
|
|||||||
use bytes::{Bytes, BytesMut};
|
use bytes::{Bytes, BytesMut};
|
||||||
use http::{Method, Version};
|
use http::{Method, Version};
|
||||||
|
|
||||||
use super::{
|
use super::decoder::{PayloadDecoder, PayloadItem, PayloadType};
|
||||||
decoder::{self, PayloadDecoder, PayloadItem, PayloadType},
|
use super::{decoder, encoder, reserve_readbuf};
|
||||||
encoder, reserve_readbuf, Message, MessageType,
|
use super::{Message, MessageType};
|
||||||
};
|
use crate::body::BodySize;
|
||||||
use crate::{
|
use crate::config::ServiceConfig;
|
||||||
body::BodySize,
|
use crate::error::{ParseError, PayloadError};
|
||||||
error::{ParseError, PayloadError},
|
use crate::message::{ConnectionType, RequestHeadType, ResponseHead};
|
||||||
ConnectionType, RequestHeadType, ResponseHead, ServiceConfig,
|
|
||||||
};
|
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
struct Flags: u8 {
|
struct Flags: u8 {
|
||||||
|
@@ -5,13 +5,15 @@ use bitflags::bitflags;
|
|||||||
use bytes::BytesMut;
|
use bytes::BytesMut;
|
||||||
use http::{Method, Version};
|
use http::{Method, Version};
|
||||||
|
|
||||||
use super::{
|
use super::decoder::{PayloadDecoder, PayloadItem, PayloadType};
|
||||||
decoder::{self, PayloadDecoder, PayloadItem, PayloadType},
|
use super::{decoder, encoder};
|
||||||
encoder, Message, MessageType,
|
use super::{Message, MessageType};
|
||||||
};
|
use crate::body::BodySize;
|
||||||
use crate::{
|
use crate::config::ServiceConfig;
|
||||||
body::BodySize, error::ParseError, ConnectionType, Request, Response, ServiceConfig,
|
use crate::error::ParseError;
|
||||||
};
|
use crate::message::ConnectionType;
|
||||||
|
use crate::request::Request;
|
||||||
|
use crate::response::Response;
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
struct Flags: u8 {
|
struct Flags: u8 {
|
||||||
@@ -197,7 +199,7 @@ mod tests {
|
|||||||
use http::Method;
|
use http::Method;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::HttpMessage as _;
|
use crate::HttpMessage;
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_http_request_chunked_payload_and_next_message() {
|
async fn test_http_request_chunked_payload_and_next_message() {
|
||||||
|
@@ -2,14 +2,17 @@ use std::{convert::TryFrom, io, marker::PhantomData, mem::MaybeUninit, task::Pol
|
|||||||
|
|
||||||
use actix_codec::Decoder;
|
use actix_codec::Decoder;
|
||||||
use bytes::{Bytes, BytesMut};
|
use bytes::{Bytes, BytesMut};
|
||||||
use http::{
|
use http::header::{HeaderName, HeaderValue};
|
||||||
header::{self, HeaderName, HeaderValue},
|
use http::{header, Method, StatusCode, Uri, Version};
|
||||||
Method, StatusCode, Uri, Version,
|
|
||||||
};
|
|
||||||
use log::{debug, error, trace};
|
use log::{debug, error, trace};
|
||||||
|
|
||||||
use super::chunked::ChunkedState;
|
use super::chunked::ChunkedState;
|
||||||
use crate::{error::ParseError, header::HeaderMap, ConnectionType, Request, ResponseHead};
|
use crate::{
|
||||||
|
error::ParseError,
|
||||||
|
header::HeaderMap,
|
||||||
|
message::{ConnectionType, ResponseHead},
|
||||||
|
request::Request,
|
||||||
|
};
|
||||||
|
|
||||||
pub(crate) const MAX_BUFFER_SIZE: usize = 131_072;
|
pub(crate) const MAX_BUFFER_SIZE: usize = 131_072;
|
||||||
const MAX_HEADERS: usize = 96;
|
const MAX_HEADERS: usize = 96;
|
||||||
@@ -47,7 +50,7 @@ pub(crate) enum PayloadLength {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) trait MessageType: Sized {
|
pub(crate) trait MessageType: Sized {
|
||||||
fn set_connection_type(&mut self, conn_type: Option<ConnectionType>);
|
fn set_connection_type(&mut self, ctype: Option<ConnectionType>);
|
||||||
|
|
||||||
fn set_expect(&mut self);
|
fn set_expect(&mut self);
|
||||||
|
|
||||||
@@ -190,8 +193,8 @@ pub(crate) trait MessageType: Sized {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl MessageType for Request {
|
impl MessageType for Request {
|
||||||
fn set_connection_type(&mut self, conn_type: Option<ConnectionType>) {
|
fn set_connection_type(&mut self, ctype: Option<ConnectionType>) {
|
||||||
if let Some(ctype) = conn_type {
|
if let Some(ctype) = ctype {
|
||||||
self.head_mut().set_connection_type(ctype);
|
self.head_mut().set_connection_type(ctype);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -275,8 +278,8 @@ impl MessageType for Request {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl MessageType for ResponseHead {
|
impl MessageType for ResponseHead {
|
||||||
fn set_connection_type(&mut self, conn_type: Option<ConnectionType>) {
|
fn set_connection_type(&mut self, ctype: Option<ConnectionType>) {
|
||||||
if let Some(ctype) = conn_type {
|
if let Some(ctype) = ctype {
|
||||||
ResponseHead::set_connection_type(self, ctype);
|
ResponseHead::set_connection_type(self, ctype);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -15,14 +15,14 @@ use bitflags::bitflags;
|
|||||||
use bytes::{Buf, BytesMut};
|
use bytes::{Buf, BytesMut};
|
||||||
use futures_core::ready;
|
use futures_core::ready;
|
||||||
use log::{error, trace};
|
use log::{error, trace};
|
||||||
use pin_project_lite::pin_project;
|
use pin_project::pin_project;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
body::{BodySize, BoxBody, MessageBody},
|
body::{BodySize, BoxBody, MessageBody},
|
||||||
config::ServiceConfig,
|
config::ServiceConfig,
|
||||||
error::{DispatchError, ParseError, PayloadError},
|
error::{DispatchError, ParseError, PayloadError},
|
||||||
service::HttpFlow,
|
service::HttpFlow,
|
||||||
Error, Extensions, OnConnectData, Request, Response, StatusCode,
|
Extensions, OnConnectData, Request, Response, StatusCode,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
@@ -46,13 +46,7 @@ bitflags! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// there's 2 versions of Dispatcher state because of:
|
#[pin_project]
|
||||||
// https://github.com/taiki-e/pin-project-lite/issues/3
|
|
||||||
//
|
|
||||||
// tl;dr: pin-project-lite doesn't play well with other attribute macros
|
|
||||||
|
|
||||||
#[cfg(not(test))]
|
|
||||||
pin_project! {
|
|
||||||
/// Dispatcher for HTTP/1.1 protocol
|
/// Dispatcher for HTTP/1.1 protocol
|
||||||
pub struct Dispatcher<T, S, B, X, U>
|
pub struct Dispatcher<T, S, B, X, U>
|
||||||
where
|
where
|
||||||
@@ -69,35 +63,12 @@ pin_project! {
|
|||||||
{
|
{
|
||||||
#[pin]
|
#[pin]
|
||||||
inner: DispatcherState<T, S, B, X, U>,
|
inner: DispatcherState<T, S, B, X, U>,
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pin_project! {
|
|
||||||
/// Dispatcher for HTTP/1.1 protocol
|
|
||||||
pub struct Dispatcher<T, S, B, X, U>
|
|
||||||
where
|
|
||||||
S: Service<Request>,
|
|
||||||
S::Error: Into<Response<BoxBody>>,
|
|
||||||
|
|
||||||
B: MessageBody,
|
|
||||||
|
|
||||||
X: Service<Request, Response = Request>,
|
|
||||||
X::Error: Into<Response<BoxBody>>,
|
|
||||||
|
|
||||||
U: Service<(Request, Framed<T, Codec>), Response = ()>,
|
|
||||||
U::Error: fmt::Display,
|
|
||||||
{
|
|
||||||
#[pin]
|
|
||||||
inner: DispatcherState<T, S, B, X, U>,
|
|
||||||
|
|
||||||
// used in tests
|
|
||||||
poll_count: u64,
|
poll_count: u64,
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pin_project! {
|
#[pin_project(project = DispatcherStateProj)]
|
||||||
#[project = DispatcherStateProj]
|
|
||||||
enum DispatcherState<T, S, B, X, U>
|
enum DispatcherState<T, S, B, X, U>
|
||||||
where
|
where
|
||||||
S: Service<Request>,
|
S: Service<Request>,
|
||||||
@@ -111,13 +82,11 @@ pin_project! {
|
|||||||
U: Service<(Request, Framed<T, Codec>), Response = ()>,
|
U: Service<(Request, Framed<T, Codec>), Response = ()>,
|
||||||
U::Error: fmt::Display,
|
U::Error: fmt::Display,
|
||||||
{
|
{
|
||||||
Normal { #[pin] inner: InnerDispatcher<T, S, B, X, U> },
|
Normal(#[pin] InnerDispatcher<T, S, B, X, U>),
|
||||||
Upgrade { #[pin] fut: U::Future },
|
Upgrade(#[pin] U::Future),
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pin_project! {
|
#[pin_project(project = InnerDispatcherProj)]
|
||||||
#[project = InnerDispatcherProj]
|
|
||||||
struct InnerDispatcher<T, S, B, X, U>
|
struct InnerDispatcher<T, S, B, X, U>
|
||||||
where
|
where
|
||||||
S: Service<Request>,
|
S: Service<Request>,
|
||||||
@@ -151,7 +120,6 @@ pin_project! {
|
|||||||
write_buf: BytesMut,
|
write_buf: BytesMut,
|
||||||
codec: Codec,
|
codec: Codec,
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
enum DispatcherMessage {
|
enum DispatcherMessage {
|
||||||
Item(Request),
|
Item(Request),
|
||||||
@@ -159,8 +127,7 @@ enum DispatcherMessage {
|
|||||||
Error(Response<()>),
|
Error(Response<()>),
|
||||||
}
|
}
|
||||||
|
|
||||||
pin_project! {
|
#[pin_project(project = StateProj)]
|
||||||
#[project = StateProj]
|
|
||||||
enum State<S, B, X>
|
enum State<S, B, X>
|
||||||
where
|
where
|
||||||
S: Service<Request>,
|
S: Service<Request>,
|
||||||
@@ -169,11 +136,10 @@ pin_project! {
|
|||||||
B: MessageBody,
|
B: MessageBody,
|
||||||
{
|
{
|
||||||
None,
|
None,
|
||||||
ExpectCall { #[pin] fut: X::Future },
|
ExpectCall(#[pin] X::Future),
|
||||||
ServiceCall { #[pin] fut: S::Future },
|
ServiceCall(#[pin] S::Future),
|
||||||
SendPayload { #[pin] body: B },
|
SendPayload(#[pin] B),
|
||||||
SendErrorPayload { #[pin] body: BoxBody },
|
SendErrorPayload(#[pin] BoxBody),
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S, B, X> State<S, B, X>
|
impl<S, B, X> State<S, B, X>
|
||||||
@@ -232,8 +198,7 @@ where
|
|||||||
};
|
};
|
||||||
|
|
||||||
Dispatcher {
|
Dispatcher {
|
||||||
inner: DispatcherState::Normal {
|
inner: DispatcherState::Normal(InnerDispatcher {
|
||||||
inner: InnerDispatcher {
|
|
||||||
flow,
|
flow,
|
||||||
flags,
|
flags,
|
||||||
peer_addr,
|
peer_addr,
|
||||||
@@ -251,8 +216,7 @@ where
|
|||||||
read_buf: BytesMut::with_capacity(HW_BUFFER_SIZE),
|
read_buf: BytesMut::with_capacity(HW_BUFFER_SIZE),
|
||||||
write_buf: BytesMut::with_capacity(HW_BUFFER_SIZE),
|
write_buf: BytesMut::with_capacity(HW_BUFFER_SIZE),
|
||||||
codec: Codec::new(config),
|
codec: Codec::new(config),
|
||||||
},
|
}),
|
||||||
},
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
poll_count: 0,
|
poll_count: 0,
|
||||||
@@ -352,7 +316,7 @@ where
|
|||||||
let size = self.as_mut().send_response_inner(message, &body)?;
|
let size = self.as_mut().send_response_inner(message, &body)?;
|
||||||
let state = match size {
|
let state = match size {
|
||||||
BodySize::None | BodySize::Sized(0) => State::None,
|
BodySize::None | BodySize::Sized(0) => State::None,
|
||||||
_ => State::SendPayload { body },
|
_ => State::SendPayload(body),
|
||||||
};
|
};
|
||||||
self.project().state.set(state);
|
self.project().state.set(state);
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -366,7 +330,7 @@ where
|
|||||||
let size = self.as_mut().send_response_inner(message, &body)?;
|
let size = self.as_mut().send_response_inner(message, &body)?;
|
||||||
let state = match size {
|
let state = match size {
|
||||||
BodySize::None | BodySize::Sized(0) => State::None,
|
BodySize::None | BodySize::Sized(0) => State::None,
|
||||||
_ => State::SendErrorPayload { body },
|
_ => State::SendErrorPayload(body),
|
||||||
};
|
};
|
||||||
self.project().state.set(state);
|
self.project().state.set(state);
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -392,12 +356,12 @@ where
|
|||||||
// Handle `EXPECT: 100-Continue` header
|
// Handle `EXPECT: 100-Continue` header
|
||||||
if req.head().expect() {
|
if req.head().expect() {
|
||||||
// set InnerDispatcher state and continue loop to poll it.
|
// set InnerDispatcher state and continue loop to poll it.
|
||||||
let fut = this.flow.expect.call(req);
|
let task = this.flow.expect.call(req);
|
||||||
this.state.set(State::ExpectCall { fut });
|
this.state.set(State::ExpectCall(task));
|
||||||
} else {
|
} else {
|
||||||
// the same as expect call.
|
// the same as expect call.
|
||||||
let fut = this.flow.service.call(req);
|
let task = this.flow.service.call(req);
|
||||||
this.state.set(State::ServiceCall { fut });
|
this.state.set(State::ServiceCall(task));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -417,7 +381,7 @@ where
|
|||||||
// all messages are dealt with.
|
// all messages are dealt with.
|
||||||
None => return Ok(PollResponse::DoNothing),
|
None => return Ok(PollResponse::DoNothing),
|
||||||
},
|
},
|
||||||
StateProj::ServiceCall { fut } => match fut.poll(cx) {
|
StateProj::ServiceCall(fut) => match fut.poll(cx) {
|
||||||
// service call resolved. send response.
|
// service call resolved. send response.
|
||||||
Poll::Ready(Ok(res)) => {
|
Poll::Ready(Ok(res)) => {
|
||||||
let (res, body) = res.into().replace_body(());
|
let (res, body) = res.into().replace_body(());
|
||||||
@@ -443,11 +407,11 @@ where
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
StateProj::SendPayload { mut body } => {
|
StateProj::SendPayload(mut stream) => {
|
||||||
// keep populate writer buffer until buffer size limit hit,
|
// keep populate writer buffer until buffer size limit hit,
|
||||||
// get blocked or finished.
|
// get blocked or finished.
|
||||||
while this.write_buf.len() < super::payload::MAX_BUFFER_SIZE {
|
while this.write_buf.len() < super::payload::MAX_BUFFER_SIZE {
|
||||||
match body.as_mut().poll_next(cx) {
|
match stream.as_mut().poll_next(cx) {
|
||||||
Poll::Ready(Some(Ok(item))) => {
|
Poll::Ready(Some(Ok(item))) => {
|
||||||
this.codec
|
this.codec
|
||||||
.encode(Message::Chunk(Some(item)), this.write_buf)?;
|
.encode(Message::Chunk(Some(item)), this.write_buf)?;
|
||||||
@@ -473,13 +437,13 @@ where
|
|||||||
return Ok(PollResponse::DrainWriteBuf);
|
return Ok(PollResponse::DrainWriteBuf);
|
||||||
}
|
}
|
||||||
|
|
||||||
StateProj::SendErrorPayload { mut body } => {
|
StateProj::SendErrorPayload(mut stream) => {
|
||||||
// TODO: de-dupe impl with SendPayload
|
// TODO: de-dupe impl with SendPayload
|
||||||
|
|
||||||
// keep populate writer buffer until buffer size limit hit,
|
// keep populate writer buffer until buffer size limit hit,
|
||||||
// get blocked or finished.
|
// get blocked or finished.
|
||||||
while this.write_buf.len() < super::payload::MAX_BUFFER_SIZE {
|
while this.write_buf.len() < super::payload::MAX_BUFFER_SIZE {
|
||||||
match body.as_mut().poll_next(cx) {
|
match stream.as_mut().poll_next(cx) {
|
||||||
Poll::Ready(Some(Ok(item))) => {
|
Poll::Ready(Some(Ok(item))) => {
|
||||||
this.codec
|
this.codec
|
||||||
.encode(Message::Chunk(Some(item)), this.write_buf)?;
|
.encode(Message::Chunk(Some(item)), this.write_buf)?;
|
||||||
@@ -494,9 +458,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
Poll::Ready(Some(Err(err))) => {
|
Poll::Ready(Some(Err(err))) => {
|
||||||
return Err(DispatchError::Body(
|
return Err(DispatchError::Service(err.into()))
|
||||||
Error::new_body().with_cause(err).into(),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Poll::Pending => return Ok(PollResponse::DoNothing),
|
Poll::Pending => return Ok(PollResponse::DoNothing),
|
||||||
@@ -507,14 +469,14 @@ where
|
|||||||
return Ok(PollResponse::DrainWriteBuf);
|
return Ok(PollResponse::DrainWriteBuf);
|
||||||
}
|
}
|
||||||
|
|
||||||
StateProj::ExpectCall { fut } => match fut.poll(cx) {
|
StateProj::ExpectCall(fut) => match fut.poll(cx) {
|
||||||
// expect resolved. write continue to buffer and set InnerDispatcher state
|
// expect resolved. write continue to buffer and set InnerDispatcher state
|
||||||
// to service call.
|
// to service call.
|
||||||
Poll::Ready(Ok(req)) => {
|
Poll::Ready(Ok(req)) => {
|
||||||
this.write_buf
|
this.write_buf
|
||||||
.extend_from_slice(b"HTTP/1.1 100 Continue\r\n\r\n");
|
.extend_from_slice(b"HTTP/1.1 100 Continue\r\n\r\n");
|
||||||
let fut = this.flow.service.call(req);
|
let fut = this.flow.service.call(req);
|
||||||
this.state.set(State::ServiceCall { fut });
|
this.state.set(State::ServiceCall(fut));
|
||||||
}
|
}
|
||||||
|
|
||||||
// send expect error as response
|
// send expect error as response
|
||||||
@@ -540,25 +502,25 @@ where
|
|||||||
let mut this = self.as_mut().project();
|
let mut this = self.as_mut().project();
|
||||||
if req.head().expect() {
|
if req.head().expect() {
|
||||||
// set dispatcher state so the future is pinned.
|
// set dispatcher state so the future is pinned.
|
||||||
let fut = this.flow.expect.call(req);
|
let task = this.flow.expect.call(req);
|
||||||
this.state.set(State::ExpectCall { fut });
|
this.state.set(State::ExpectCall(task));
|
||||||
} else {
|
} else {
|
||||||
// the same as above.
|
// the same as above.
|
||||||
let fut = this.flow.service.call(req);
|
let task = this.flow.service.call(req);
|
||||||
this.state.set(State::ServiceCall { fut });
|
this.state.set(State::ServiceCall(task));
|
||||||
};
|
};
|
||||||
|
|
||||||
// eagerly poll the future for once(or twice if expect is resolved immediately).
|
// eagerly poll the future for once(or twice if expect is resolved immediately).
|
||||||
loop {
|
loop {
|
||||||
match self.as_mut().project().state.project() {
|
match self.as_mut().project().state.project() {
|
||||||
StateProj::ExpectCall { fut } => {
|
StateProj::ExpectCall(fut) => {
|
||||||
match fut.poll(cx) {
|
match fut.poll(cx) {
|
||||||
// expect is resolved. continue loop and poll the service call branch.
|
// expect is resolved. continue loop and poll the service call branch.
|
||||||
Poll::Ready(Ok(req)) => {
|
Poll::Ready(Ok(req)) => {
|
||||||
self.as_mut().send_continue();
|
self.as_mut().send_continue();
|
||||||
let mut this = self.as_mut().project();
|
let mut this = self.as_mut().project();
|
||||||
let fut = this.flow.service.call(req);
|
let task = this.flow.service.call(req);
|
||||||
this.state.set(State::ServiceCall { fut });
|
this.state.set(State::ServiceCall(task));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// future is pending. return Ok(()) to notify that a new state is
|
// future is pending. return Ok(()) to notify that a new state is
|
||||||
@@ -574,7 +536,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
StateProj::ServiceCall { fut } => {
|
StateProj::ServiceCall(fut) => {
|
||||||
// return no matter the service call future's result.
|
// return no matter the service call future's result.
|
||||||
return match fut.poll(cx) {
|
return match fut.poll(cx) {
|
||||||
// future is resolved. send response and return a result. On success
|
// future is resolved. send response and return a result. On success
|
||||||
@@ -646,11 +608,10 @@ where
|
|||||||
Payload is attached to Request and passed to Service::call
|
Payload is attached to Request and passed to Service::call
|
||||||
where the state can be collected and consumed.
|
where the state can be collected and consumed.
|
||||||
*/
|
*/
|
||||||
let (sender, payload) = Payload::create(false);
|
let (ps, pl) = Payload::create(false);
|
||||||
let (req1, _) =
|
let (req1, _) = req.replace_payload(crate::Payload::H1(pl));
|
||||||
req.replace_payload(crate::Payload::H1 { payload });
|
|
||||||
req = req1;
|
req = req1;
|
||||||
*this.payload = Some(sender);
|
*this.payload = Some(ps);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Request has no payload.
|
// Request has no payload.
|
||||||
@@ -940,7 +901,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
match this.inner.project() {
|
match this.inner.project() {
|
||||||
DispatcherStateProj::Normal { mut inner } => {
|
DispatcherStateProj::Normal(mut inner) => {
|
||||||
inner.as_mut().poll_keepalive(cx)?;
|
inner.as_mut().poll_keepalive(cx)?;
|
||||||
|
|
||||||
if inner.flags.contains(Flags::SHUTDOWN) {
|
if inner.flags.contains(Flags::SHUTDOWN) {
|
||||||
@@ -980,7 +941,7 @@ where
|
|||||||
self.as_mut()
|
self.as_mut()
|
||||||
.project()
|
.project()
|
||||||
.inner
|
.inner
|
||||||
.set(DispatcherState::Upgrade { fut: upgrade });
|
.set(DispatcherState::Upgrade(upgrade));
|
||||||
return self.poll(cx);
|
return self.poll(cx);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -1032,8 +993,8 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DispatcherStateProj::Upgrade { fut: upgrade } => upgrade.poll(cx).map_err(|err| {
|
DispatcherStateProj::Upgrade(fut) => fut.poll(cx).map_err(|e| {
|
||||||
error!("Upgrade handler error: {}", err);
|
error!("Upgrade handler error: {}", e);
|
||||||
DispatchError::Upgrade
|
DispatchError::Upgrade
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
@@ -1127,7 +1088,7 @@ mod tests {
|
|||||||
Poll::Ready(res) => assert!(res.is_err()),
|
Poll::Ready(res) => assert!(res.is_err()),
|
||||||
}
|
}
|
||||||
|
|
||||||
if let DispatcherStateProj::Normal { inner } = h1.project().inner.project() {
|
if let DispatcherStateProj::Normal(inner) = h1.project().inner.project() {
|
||||||
assert!(inner.flags.contains(Flags::READ_DISCONNECT));
|
assert!(inner.flags.contains(Flags::READ_DISCONNECT));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
&inner.project().io.take().unwrap().write_buf[..26],
|
&inner.project().io.take().unwrap().write_buf[..26],
|
||||||
@@ -1162,7 +1123,7 @@ mod tests {
|
|||||||
|
|
||||||
actix_rt::pin!(h1);
|
actix_rt::pin!(h1);
|
||||||
|
|
||||||
assert!(matches!(&h1.inner, DispatcherState::Normal { .. }));
|
assert!(matches!(&h1.inner, DispatcherState::Normal(_)));
|
||||||
|
|
||||||
match h1.as_mut().poll(cx) {
|
match h1.as_mut().poll(cx) {
|
||||||
Poll::Pending => panic!("first poll should not be pending"),
|
Poll::Pending => panic!("first poll should not be pending"),
|
||||||
@@ -1172,7 +1133,7 @@ mod tests {
|
|||||||
// polls: initial => shutdown
|
// polls: initial => shutdown
|
||||||
assert_eq!(h1.poll_count, 2);
|
assert_eq!(h1.poll_count, 2);
|
||||||
|
|
||||||
if let DispatcherStateProj::Normal { inner } = h1.project().inner.project() {
|
if let DispatcherStateProj::Normal(inner) = h1.project().inner.project() {
|
||||||
let res = &mut inner.project().io.take().unwrap().write_buf[..];
|
let res = &mut inner.project().io.take().unwrap().write_buf[..];
|
||||||
stabilize_date_header(res);
|
stabilize_date_header(res);
|
||||||
|
|
||||||
@@ -1216,7 +1177,7 @@ mod tests {
|
|||||||
|
|
||||||
actix_rt::pin!(h1);
|
actix_rt::pin!(h1);
|
||||||
|
|
||||||
assert!(matches!(&h1.inner, DispatcherState::Normal { .. }));
|
assert!(matches!(&h1.inner, DispatcherState::Normal(_)));
|
||||||
|
|
||||||
match h1.as_mut().poll(cx) {
|
match h1.as_mut().poll(cx) {
|
||||||
Poll::Pending => panic!("first poll should not be pending"),
|
Poll::Pending => panic!("first poll should not be pending"),
|
||||||
@@ -1226,7 +1187,7 @@ mod tests {
|
|||||||
// polls: initial => shutdown
|
// polls: initial => shutdown
|
||||||
assert_eq!(h1.poll_count, 1);
|
assert_eq!(h1.poll_count, 1);
|
||||||
|
|
||||||
if let DispatcherStateProj::Normal { inner } = h1.project().inner.project() {
|
if let DispatcherStateProj::Normal(inner) = h1.project().inner.project() {
|
||||||
let res = &mut inner.project().io.take().unwrap().write_buf[..];
|
let res = &mut inner.project().io.take().unwrap().write_buf[..];
|
||||||
stabilize_date_header(res);
|
stabilize_date_header(res);
|
||||||
|
|
||||||
@@ -1276,13 +1237,13 @@ mod tests {
|
|||||||
actix_rt::pin!(h1);
|
actix_rt::pin!(h1);
|
||||||
|
|
||||||
assert!(h1.as_mut().poll(cx).is_pending());
|
assert!(h1.as_mut().poll(cx).is_pending());
|
||||||
assert!(matches!(&h1.inner, DispatcherState::Normal { .. }));
|
assert!(matches!(&h1.inner, DispatcherState::Normal(_)));
|
||||||
|
|
||||||
// polls: manual
|
// polls: manual
|
||||||
assert_eq!(h1.poll_count, 1);
|
assert_eq!(h1.poll_count, 1);
|
||||||
eprintln!("poll count: {}", h1.poll_count);
|
eprintln!("poll count: {}", h1.poll_count);
|
||||||
|
|
||||||
if let DispatcherState::Normal { ref inner } = h1.inner {
|
if let DispatcherState::Normal(ref inner) = h1.inner {
|
||||||
let io = inner.io.as_ref().unwrap();
|
let io = inner.io.as_ref().unwrap();
|
||||||
let res = &io.write_buf()[..];
|
let res = &io.write_buf()[..];
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -1297,7 +1258,7 @@ mod tests {
|
|||||||
// polls: manual manual shutdown
|
// polls: manual manual shutdown
|
||||||
assert_eq!(h1.poll_count, 3);
|
assert_eq!(h1.poll_count, 3);
|
||||||
|
|
||||||
if let DispatcherState::Normal { ref inner } = h1.inner {
|
if let DispatcherState::Normal(ref inner) = h1.inner {
|
||||||
let io = inner.io.as_ref().unwrap();
|
let io = inner.io.as_ref().unwrap();
|
||||||
let mut res = (&io.write_buf()[..]).to_owned();
|
let mut res = (&io.write_buf()[..]).to_owned();
|
||||||
stabilize_date_header(&mut res);
|
stabilize_date_header(&mut res);
|
||||||
@@ -1348,12 +1309,12 @@ mod tests {
|
|||||||
actix_rt::pin!(h1);
|
actix_rt::pin!(h1);
|
||||||
|
|
||||||
assert!(h1.as_mut().poll(cx).is_ready());
|
assert!(h1.as_mut().poll(cx).is_ready());
|
||||||
assert!(matches!(&h1.inner, DispatcherState::Normal { .. }));
|
assert!(matches!(&h1.inner, DispatcherState::Normal(_)));
|
||||||
|
|
||||||
// polls: manual shutdown
|
// polls: manual shutdown
|
||||||
assert_eq!(h1.poll_count, 2);
|
assert_eq!(h1.poll_count, 2);
|
||||||
|
|
||||||
if let DispatcherState::Normal { ref inner } = h1.inner {
|
if let DispatcherState::Normal(ref inner) = h1.inner {
|
||||||
let io = inner.io.as_ref().unwrap();
|
let io = inner.io.as_ref().unwrap();
|
||||||
let mut res = (&io.write_buf()[..]).to_owned();
|
let mut res = (&io.write_buf()[..]).to_owned();
|
||||||
stabilize_date_header(&mut res);
|
stabilize_date_header(&mut res);
|
||||||
@@ -1425,7 +1386,7 @@ mod tests {
|
|||||||
actix_rt::pin!(h1);
|
actix_rt::pin!(h1);
|
||||||
|
|
||||||
assert!(h1.as_mut().poll(cx).is_ready());
|
assert!(h1.as_mut().poll(cx).is_ready());
|
||||||
assert!(matches!(&h1.inner, DispatcherState::Upgrade { .. }));
|
assert!(matches!(&h1.inner, DispatcherState::Upgrade(_)));
|
||||||
|
|
||||||
// polls: manual shutdown
|
// polls: manual shutdown
|
||||||
assert_eq!(h1.poll_count, 2);
|
assert_eq!(h1.poll_count, 2);
|
||||||
|
@@ -1,19 +1,19 @@
|
|||||||
use std::{
|
use std::io::Write;
|
||||||
cmp,
|
use std::marker::PhantomData;
|
||||||
io::{self, Write as _},
|
use std::ptr::copy_nonoverlapping;
|
||||||
marker::PhantomData,
|
use std::slice::from_raw_parts_mut;
|
||||||
ptr::copy_nonoverlapping,
|
use std::{cmp, io};
|
||||||
slice::from_raw_parts_mut,
|
|
||||||
};
|
|
||||||
|
|
||||||
use bytes::{BufMut, BytesMut};
|
use bytes::{BufMut, BytesMut};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
body::BodySize,
|
body::BodySize,
|
||||||
header::{
|
config::ServiceConfig,
|
||||||
map::Value, HeaderMap, HeaderName, CONNECTION, CONTENT_LENGTH, DATE, TRANSFER_ENCODING,
|
header::{map::Value, HeaderMap, HeaderName},
|
||||||
},
|
header::{CONNECTION, CONTENT_LENGTH, DATE, TRANSFER_ENCODING},
|
||||||
helpers, ConnectionType, RequestHeadType, Response, ServiceConfig, StatusCode, Version,
|
helpers,
|
||||||
|
message::{ConnectionType, RequestHeadType},
|
||||||
|
Response, StatusCode, Version,
|
||||||
};
|
};
|
||||||
|
|
||||||
const AVERAGE_HEADER_SIZE: usize = 30;
|
const AVERAGE_HEADER_SIZE: usize = 30;
|
||||||
|
@@ -1,7 +1,8 @@
|
|||||||
use actix_service::{Service, ServiceFactory};
|
use actix_service::{Service, ServiceFactory};
|
||||||
use actix_utils::future::{ready, Ready};
|
use actix_utils::future::{ready, Ready};
|
||||||
|
|
||||||
use crate::{Error, Request};
|
use crate::error::Error;
|
||||||
|
use crate::request::Request;
|
||||||
|
|
||||||
pub struct ExpectHandler;
|
pub struct ExpectHandler;
|
||||||
|
|
||||||
|
@@ -59,7 +59,7 @@ pub(crate) fn reserve_readbuf(src: &mut BytesMut) {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::Request;
|
use crate::request::Request;
|
||||||
|
|
||||||
impl Message<Request> {
|
impl Message<Request> {
|
||||||
pub fn message(self) -> Request {
|
pub fn message(self) -> Request {
|
||||||
|
@@ -1,12 +1,9 @@
|
|||||||
//! Payload stream
|
//! Payload stream
|
||||||
|
use std::cell::RefCell;
|
||||||
use std::{
|
use std::collections::VecDeque;
|
||||||
cell::RefCell,
|
use std::pin::Pin;
|
||||||
collections::VecDeque,
|
use std::rc::{Rc, Weak};
|
||||||
pin::Pin,
|
use std::task::{Context, Poll, Waker};
|
||||||
rc::{Rc, Weak},
|
|
||||||
task::{Context, Poll, Waker},
|
|
||||||
};
|
|
||||||
|
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use futures_core::Stream;
|
use futures_core::Stream;
|
||||||
@@ -25,32 +22,39 @@ pub enum PayloadStatus {
|
|||||||
|
|
||||||
/// Buffered stream of bytes chunks
|
/// Buffered stream of bytes chunks
|
||||||
///
|
///
|
||||||
/// Payload stores chunks in a vector. First chunk can be received with `poll_next`. Payload does
|
/// Payload stores chunks in a vector. First chunk can be received with
|
||||||
/// not notify current task when new data is available.
|
/// `.readany()` method. Payload stream is not thread safe. Payload does not
|
||||||
|
/// notify current task when new data is available.
|
||||||
///
|
///
|
||||||
/// Payload can be used as `Response` body stream.
|
/// Payload stream can be used as `Response` body stream.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Payload {
|
pub struct Payload {
|
||||||
inner: Rc<RefCell<Inner>>,
|
inner: Rc<RefCell<Inner>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Payload {
|
impl Payload {
|
||||||
/// Creates a payload stream.
|
/// Create payload stream.
|
||||||
///
|
///
|
||||||
/// This method construct two objects responsible for bytes stream generation:
|
/// This method construct two objects responsible for bytes stream
|
||||||
/// - `PayloadSender` - *Sender* side of the stream
|
/// generation.
|
||||||
/// - `Payload` - *Receiver* side of the stream
|
///
|
||||||
|
/// * `PayloadSender` - *Sender* side of the stream
|
||||||
|
///
|
||||||
|
/// * `Payload` - *Receiver* side of the stream
|
||||||
pub fn create(eof: bool) -> (PayloadSender, Payload) {
|
pub fn create(eof: bool) -> (PayloadSender, Payload) {
|
||||||
let shared = Rc::new(RefCell::new(Inner::new(eof)));
|
let shared = Rc::new(RefCell::new(Inner::new(eof)));
|
||||||
|
|
||||||
(
|
(
|
||||||
PayloadSender::new(Rc::downgrade(&shared)),
|
PayloadSender {
|
||||||
|
inner: Rc::downgrade(&shared),
|
||||||
|
},
|
||||||
Payload { inner: shared },
|
Payload { inner: shared },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates an empty payload.
|
/// Create empty payload
|
||||||
pub(crate) fn empty() -> Payload {
|
#[doc(hidden)]
|
||||||
|
pub fn empty() -> Payload {
|
||||||
Payload {
|
Payload {
|
||||||
inner: Rc::new(RefCell::new(Inner::new(true))),
|
inner: Rc::new(RefCell::new(Inner::new(true))),
|
||||||
}
|
}
|
||||||
@@ -73,6 +77,14 @@ impl Payload {
|
|||||||
pub fn unread_data(&mut self, data: Bytes) {
|
pub fn unread_data(&mut self, data: Bytes) {
|
||||||
self.inner.borrow_mut().unread_data(data);
|
self.inner.borrow_mut().unread_data(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn readany(
|
||||||
|
&mut self,
|
||||||
|
cx: &mut Context<'_>,
|
||||||
|
) -> Poll<Option<Result<Bytes, PayloadError>>> {
|
||||||
|
self.inner.borrow_mut().readany(cx)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Stream for Payload {
|
impl Stream for Payload {
|
||||||
@@ -82,7 +94,7 @@ impl Stream for Payload {
|
|||||||
self: Pin<&mut Self>,
|
self: Pin<&mut Self>,
|
||||||
cx: &mut Context<'_>,
|
cx: &mut Context<'_>,
|
||||||
) -> Poll<Option<Result<Bytes, PayloadError>>> {
|
) -> Poll<Option<Result<Bytes, PayloadError>>> {
|
||||||
Pin::new(&mut *self.inner.borrow_mut()).poll_next(cx)
|
self.inner.borrow_mut().readany(cx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,10 +104,6 @@ pub struct PayloadSender {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl PayloadSender {
|
impl PayloadSender {
|
||||||
fn new(inner: Weak<RefCell<Inner>>) -> Self {
|
|
||||||
Self { inner }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_error(&mut self, err: PayloadError) {
|
pub fn set_error(&mut self, err: PayloadError) {
|
||||||
if let Some(shared) = self.inner.upgrade() {
|
if let Some(shared) = self.inner.upgrade() {
|
||||||
@@ -219,10 +227,7 @@ impl Inner {
|
|||||||
self.len
|
self.len
|
||||||
}
|
}
|
||||||
|
|
||||||
fn poll_next(
|
fn readany(&mut self, cx: &mut Context<'_>) -> Poll<Option<Result<Bytes, PayloadError>>> {
|
||||||
mut self: Pin<&mut Self>,
|
|
||||||
cx: &mut Context<'_>,
|
|
||||||
) -> Poll<Option<Result<Bytes, PayloadError>>> {
|
|
||||||
if let Some(data) = self.items.pop_front() {
|
if let Some(data) = self.items.pop_front() {
|
||||||
self.len -= data.len();
|
self.len -= data.len();
|
||||||
self.need_read = self.len < MAX_BUFFER_SIZE;
|
self.need_read = self.len < MAX_BUFFER_SIZE;
|
||||||
@@ -252,18 +257,8 @@ impl Inner {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::panic::{RefUnwindSafe, UnwindSafe};
|
|
||||||
|
|
||||||
use actix_utils::future::poll_fn;
|
|
||||||
use static_assertions::{assert_impl_all, assert_not_impl_any};
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use actix_utils::future::poll_fn;
|
||||||
assert_impl_all!(Payload: Unpin);
|
|
||||||
assert_not_impl_any!(Payload: Send, Sync, UnwindSafe, RefUnwindSafe);
|
|
||||||
|
|
||||||
assert_impl_all!(Inner: Unpin, Send, Sync);
|
|
||||||
assert_not_impl_any!(Inner: UnwindSafe, RefUnwindSafe);
|
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_unread_data() {
|
async fn test_unread_data() {
|
||||||
@@ -275,10 +270,7 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Bytes::from("data"),
|
Bytes::from("data"),
|
||||||
poll_fn(|cx| Pin::new(&mut payload).poll_next(cx))
|
poll_fn(|cx| payload.readany(cx)).await.unwrap().unwrap()
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
.unwrap()
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -356,9 +356,9 @@ where
|
|||||||
type Future = Dispatcher<T, S, B, X, U>;
|
type Future = Dispatcher<T, S, B, X, U>;
|
||||||
|
|
||||||
fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||||
self._poll_ready(cx).map_err(|err| {
|
self._poll_ready(cx).map_err(|e| {
|
||||||
log::error!("HTTP/1 service readiness error: {:?}", err);
|
log::error!("HTTP/1 service readiness error: {:?}", e);
|
||||||
DispatchError::Service(err)
|
DispatchError::Service(e)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -2,7 +2,9 @@ use actix_codec::Framed;
|
|||||||
use actix_service::{Service, ServiceFactory};
|
use actix_service::{Service, ServiceFactory};
|
||||||
use futures_core::future::LocalBoxFuture;
|
use futures_core::future::LocalBoxFuture;
|
||||||
|
|
||||||
use crate::{h1::Codec, Error, Request};
|
use crate::error::Error;
|
||||||
|
use crate::h1::Codec;
|
||||||
|
use crate::request::Request;
|
||||||
|
|
||||||
pub struct UpgradeHandler;
|
pub struct UpgradeHandler;
|
||||||
|
|
||||||
|
@@ -9,8 +9,9 @@ use pin_project_lite::pin_project;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
body::{BodySize, MessageBody},
|
body::{BodySize, MessageBody},
|
||||||
|
error::Error,
|
||||||
h1::{Codec, Message},
|
h1::{Codec, Message},
|
||||||
Error, Response,
|
response::Response,
|
||||||
};
|
};
|
||||||
|
|
||||||
pin_project! {
|
pin_project! {
|
||||||
@@ -45,7 +46,7 @@ where
|
|||||||
impl<T, B> Future for SendResponse<T, B>
|
impl<T, B> Future for SendResponse<T, B>
|
||||||
where
|
where
|
||||||
T: AsyncRead + AsyncWrite + Unpin,
|
T: AsyncRead + AsyncWrite + Unpin,
|
||||||
B: MessageBody,
|
B: MessageBody + Unpin,
|
||||||
B::Error: Into<Error>,
|
B::Error: Into<Error>,
|
||||||
{
|
{
|
||||||
type Output = Result<Framed<T, Codec>, Error>;
|
type Output = Result<Framed<T, Codec>, Error>;
|
||||||
@@ -81,7 +82,7 @@ where
|
|||||||
// body is done when item is None
|
// body is done when item is None
|
||||||
body_done = item.is_none();
|
body_done = item.is_none();
|
||||||
if body_done {
|
if body_done {
|
||||||
this.body.set(None);
|
let _ = this.body.take();
|
||||||
}
|
}
|
||||||
let framed = this.framed.as_mut().as_pin_mut().unwrap();
|
let framed = this.framed.as_mut().as_pin_mut().unwrap();
|
||||||
framed
|
framed
|
||||||
|
@@ -108,8 +108,8 @@ where
|
|||||||
match Pin::new(&mut this.connection).poll_accept(cx)? {
|
match Pin::new(&mut this.connection).poll_accept(cx)? {
|
||||||
Poll::Ready(Some((req, tx))) => {
|
Poll::Ready(Some((req, tx))) => {
|
||||||
let (parts, body) = req.into_parts();
|
let (parts, body) = req.into_parts();
|
||||||
let payload = crate::h2::Payload::new(body);
|
let pl = crate::h2::Payload::new(body);
|
||||||
let pl = Payload::H2 { payload };
|
let pl = Payload::H2(pl);
|
||||||
let mut req = Request::with_payload(pl);
|
let mut req = Request::with_payload(pl);
|
||||||
|
|
||||||
let head = req.head_mut();
|
let head = req.head_mut();
|
||||||
@@ -288,11 +288,9 @@ fn prepare_response(
|
|||||||
let _ = match size {
|
let _ = match size {
|
||||||
BodySize::None | BodySize::Stream => None,
|
BodySize::None | BodySize::Stream => None,
|
||||||
|
|
||||||
BodySize::Sized(0) => {
|
BodySize::Sized(0) => res
|
||||||
#[allow(clippy::declare_interior_mutable_const)]
|
.headers_mut()
|
||||||
const HV_ZERO: HeaderValue = HeaderValue::from_static("0");
|
.insert(CONTENT_LENGTH, HeaderValue::from_static("0")),
|
||||||
res.headers_mut().insert(CONTENT_LENGTH, HV_ZERO)
|
|
||||||
}
|
|
||||||
|
|
||||||
BodySize::Sized(len) => {
|
BodySize::Sized(len) => {
|
||||||
let mut buf = itoa::Buffer::new();
|
let mut buf = itoa::Buffer::new();
|
||||||
|
@@ -98,14 +98,3 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use std::panic::{RefUnwindSafe, UnwindSafe};
|
|
||||||
|
|
||||||
use static_assertions::assert_impl_all;
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
assert_impl_all!(Payload: Unpin, Send, Sync, UnwindSafe, RefUnwindSafe);
|
|
||||||
}
|
|
||||||
|
@@ -306,11 +306,8 @@ impl HeaderMap {
|
|||||||
/// assert_eq!(set_cookies_iter.next().unwrap(), "two=2");
|
/// assert_eq!(set_cookies_iter.next().unwrap(), "two=2");
|
||||||
/// assert!(set_cookies_iter.next().is_none());
|
/// assert!(set_cookies_iter.next().is_none());
|
||||||
/// ```
|
/// ```
|
||||||
pub fn get_all(&self, key: impl AsHeaderName) -> std::slice::Iter<'_, HeaderValue> {
|
pub fn get_all(&self, key: impl AsHeaderName) -> GetAll<'_> {
|
||||||
match self.get_value(key) {
|
GetAll::new(self.get_value(key))
|
||||||
Some(value) => value.iter(),
|
|
||||||
None => (&[]).iter(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: get_all_mut ?
|
// TODO: get_all_mut ?
|
||||||
@@ -605,6 +602,52 @@ impl<'a> IntoIterator for &'a HeaderMap {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Iterator over borrowed values with the same associated name.
|
||||||
|
///
|
||||||
|
/// See [`HeaderMap::get_all`].
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct GetAll<'a> {
|
||||||
|
idx: usize,
|
||||||
|
value: Option<&'a Value>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> GetAll<'a> {
|
||||||
|
fn new(value: Option<&'a Value>) -> Self {
|
||||||
|
Self { idx: 0, value }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Iterator for GetAll<'a> {
|
||||||
|
type Item = &'a HeaderValue;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
let val = self.value?;
|
||||||
|
|
||||||
|
match val.get(self.idx) {
|
||||||
|
Some(val) => {
|
||||||
|
self.idx += 1;
|
||||||
|
Some(val)
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
// current index is none; remove value to fast-path future next calls
|
||||||
|
self.value = None;
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||||
|
match self.value {
|
||||||
|
Some(val) => (val.len(), Some(val.len())),
|
||||||
|
None => (0, Some(0)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ExactSizeIterator for GetAll<'_> {}
|
||||||
|
|
||||||
|
impl iter::FusedIterator for GetAll<'_> {}
|
||||||
|
|
||||||
/// Iterator over removed, owned values with the same associated name.
|
/// Iterator over removed, owned values with the same associated name.
|
||||||
///
|
///
|
||||||
/// Returned from methods that remove or replace items. See [`HeaderMap::insert`]
|
/// Returned from methods that remove or replace items. See [`HeaderMap::insert`]
|
||||||
@@ -852,7 +895,7 @@ mod tests {
|
|||||||
|
|
||||||
assert_impl_all!(HeaderMap: IntoIterator);
|
assert_impl_all!(HeaderMap: IntoIterator);
|
||||||
assert_impl_all!(Keys<'_>: Iterator, ExactSizeIterator, FusedIterator);
|
assert_impl_all!(Keys<'_>: Iterator, ExactSizeIterator, FusedIterator);
|
||||||
assert_impl_all!(std::slice::Iter<'_, HeaderValue>: Iterator, ExactSizeIterator, FusedIterator);
|
assert_impl_all!(GetAll<'_>: Iterator, ExactSizeIterator, FusedIterator);
|
||||||
assert_impl_all!(Removed: Iterator, ExactSizeIterator, FusedIterator);
|
assert_impl_all!(Removed: Iterator, ExactSizeIterator, FusedIterator);
|
||||||
assert_impl_all!(Iter<'_>: Iterator, ExactSizeIterator, FusedIterator);
|
assert_impl_all!(Iter<'_>: Iterator, ExactSizeIterator, FusedIterator);
|
||||||
assert_impl_all!(IntoIter: Iterator, ExactSizeIterator, FusedIterator);
|
assert_impl_all!(IntoIter: Iterator, ExactSizeIterator, FusedIterator);
|
||||||
|
@@ -45,13 +45,13 @@ pub enum ContentEncoding {
|
|||||||
impl ContentEncoding {
|
impl ContentEncoding {
|
||||||
/// Is the content compressed?
|
/// Is the content compressed?
|
||||||
#[inline]
|
#[inline]
|
||||||
pub const fn is_compression(self) -> bool {
|
pub fn is_compression(self) -> bool {
|
||||||
matches!(self, ContentEncoding::Identity | ContentEncoding::Auto)
|
matches!(self, ContentEncoding::Identity | ContentEncoding::Auto)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert content encoding to string.
|
/// Convert content encoding to string.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub const fn as_str(self) -> &'static str {
|
pub fn as_str(self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
ContentEncoding::Br => "br",
|
ContentEncoding::Br => "br",
|
||||||
ContentEncoding::Gzip => "gzip",
|
ContentEncoding::Gzip => "gzip",
|
||||||
|
@@ -87,7 +87,7 @@ impl fmt::Display for Quality {
|
|||||||
|
|
||||||
// 0 is already handled so it's not possible to have a trailing 0 in this range
|
// 0 is already handled so it's not possible to have a trailing 0 in this range
|
||||||
// we can just write the integer
|
// we can just write the integer
|
||||||
itoa_fmt(f, x)
|
itoa::fmt(f, x)
|
||||||
} else if x < 100 {
|
} else if x < 100 {
|
||||||
// x in is range 10–99
|
// x in is range 10–99
|
||||||
|
|
||||||
@@ -95,21 +95,21 @@ impl fmt::Display for Quality {
|
|||||||
|
|
||||||
if x % 10 == 0 {
|
if x % 10 == 0 {
|
||||||
// trailing 0, divide by 10 and write
|
// trailing 0, divide by 10 and write
|
||||||
itoa_fmt(f, x / 10)
|
itoa::fmt(f, x / 10)
|
||||||
} else {
|
} else {
|
||||||
itoa_fmt(f, x)
|
itoa::fmt(f, x)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// x is in range 100–999
|
// x is in range 100–999
|
||||||
|
|
||||||
if x % 100 == 0 {
|
if x % 100 == 0 {
|
||||||
// two trailing 0s, divide by 100 and write
|
// two trailing 0s, divide by 100 and write
|
||||||
itoa_fmt(f, x / 100)
|
itoa::fmt(f, x / 100)
|
||||||
} else if x % 10 == 0 {
|
} else if x % 10 == 0 {
|
||||||
// one trailing 0, divide by 10 and write
|
// one trailing 0, divide by 10 and write
|
||||||
itoa_fmt(f, x / 10)
|
itoa::fmt(f, x / 10)
|
||||||
} else {
|
} else {
|
||||||
itoa_fmt(f, x)
|
itoa::fmt(f, x)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -117,12 +117,6 @@ impl fmt::Display for Quality {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Write integer to a `fmt::Write`.
|
|
||||||
pub fn itoa_fmt<W: fmt::Write, V: itoa::Integer>(mut wr: W, value: V) -> fmt::Result {
|
|
||||||
let mut buf = itoa::Buffer::new();
|
|
||||||
wr.write_str(buf.format(value))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Display, Error)]
|
#[derive(Debug, Clone, Display, Error)]
|
||||||
#[display(fmt = "quality out of bounds")]
|
#[display(fmt = "quality out of bounds")]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
|
@@ -19,6 +19,7 @@
|
|||||||
#![allow(
|
#![allow(
|
||||||
clippy::type_complexity,
|
clippy::type_complexity,
|
||||||
clippy::too_many_arguments,
|
clippy::too_many_arguments,
|
||||||
|
clippy::new_without_default,
|
||||||
clippy::borrow_interior_mutable_const
|
clippy::borrow_interior_mutable_const
|
||||||
)]
|
)]
|
||||||
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
||||||
@@ -27,26 +28,26 @@
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate log;
|
extern crate log;
|
||||||
|
|
||||||
pub use ::http::{uri, uri::Uri};
|
|
||||||
pub use ::http::{Method, StatusCode, Version};
|
|
||||||
|
|
||||||
pub mod body;
|
pub mod body;
|
||||||
mod builder;
|
mod builder;
|
||||||
mod config;
|
mod config;
|
||||||
|
|
||||||
#[cfg(feature = "__compress")]
|
#[cfg(feature = "__compress")]
|
||||||
pub mod encoding;
|
pub mod encoding;
|
||||||
pub mod error;
|
|
||||||
mod extensions;
|
mod extensions;
|
||||||
pub mod h1;
|
|
||||||
pub mod h2;
|
|
||||||
pub mod header;
|
pub mod header;
|
||||||
mod helpers;
|
mod helpers;
|
||||||
mod http_message;
|
mod http_message;
|
||||||
mod message;
|
mod message;
|
||||||
mod payload;
|
mod payload;
|
||||||
mod requests;
|
mod request;
|
||||||
mod responses;
|
mod response;
|
||||||
|
mod response_builder;
|
||||||
mod service;
|
mod service;
|
||||||
|
|
||||||
|
pub mod error;
|
||||||
|
pub mod h1;
|
||||||
|
pub mod h2;
|
||||||
pub mod test;
|
pub mod test;
|
||||||
pub mod ws;
|
pub mod ws;
|
||||||
|
|
||||||
@@ -57,13 +58,16 @@ pub use self::extensions::Extensions;
|
|||||||
pub use self::header::ContentEncoding;
|
pub use self::header::ContentEncoding;
|
||||||
pub use self::http_message::HttpMessage;
|
pub use self::http_message::HttpMessage;
|
||||||
pub use self::message::ConnectionType;
|
pub use self::message::ConnectionType;
|
||||||
pub use self::message::Message;
|
pub use self::message::{Message, RequestHead, RequestHeadType, ResponseHead};
|
||||||
#[allow(deprecated)]
|
pub use self::payload::{Payload, PayloadStream};
|
||||||
pub use self::payload::{BoxedPayloadStream, Payload, PayloadStream};
|
pub use self::request::Request;
|
||||||
pub use self::requests::{Request, RequestHead, RequestHeadType};
|
pub use self::response::Response;
|
||||||
pub use self::responses::{Response, ResponseBuilder, ResponseHead};
|
pub use self::response_builder::ResponseBuilder;
|
||||||
pub use self::service::HttpService;
|
pub use self::service::HttpService;
|
||||||
|
|
||||||
|
pub use ::http::{uri, uri::Uri};
|
||||||
|
pub use ::http::{Method, StatusCode, Version};
|
||||||
|
|
||||||
/// A major HTTP protocol version.
|
/// A major HTTP protocol version.
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
|
@@ -1,7 +1,16 @@
|
|||||||
use std::{cell::RefCell, rc::Rc};
|
use std::{
|
||||||
|
cell::{Ref, RefCell, RefMut},
|
||||||
|
net,
|
||||||
|
rc::Rc,
|
||||||
|
};
|
||||||
|
|
||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
header::{self, HeaderMap},
|
||||||
|
Extensions, Method, StatusCode, Uri, Version,
|
||||||
|
};
|
||||||
|
|
||||||
/// Represents various types of connection
|
/// Represents various types of connection
|
||||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||||
pub enum ConnectionType {
|
pub enum ConnectionType {
|
||||||
@@ -35,6 +44,294 @@ pub trait Head: Default + 'static {
|
|||||||
F: FnOnce(&MessagePool<Self>) -> R;
|
F: FnOnce(&MessagePool<Self>) -> R;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct RequestHead {
|
||||||
|
pub method: Method,
|
||||||
|
pub uri: Uri,
|
||||||
|
pub version: Version,
|
||||||
|
pub headers: HeaderMap,
|
||||||
|
pub peer_addr: Option<net::SocketAddr>,
|
||||||
|
flags: Flags,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for RequestHead {
|
||||||
|
fn default() -> RequestHead {
|
||||||
|
RequestHead {
|
||||||
|
method: Method::default(),
|
||||||
|
uri: Uri::default(),
|
||||||
|
version: Version::HTTP_11,
|
||||||
|
headers: HeaderMap::with_capacity(16),
|
||||||
|
peer_addr: None,
|
||||||
|
flags: Flags::empty(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Head for RequestHead {
|
||||||
|
fn clear(&mut self) {
|
||||||
|
self.flags = Flags::empty();
|
||||||
|
self.headers.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn with_pool<F, R>(f: F) -> R
|
||||||
|
where
|
||||||
|
F: FnOnce(&MessagePool<Self>) -> R,
|
||||||
|
{
|
||||||
|
REQUEST_POOL.with(|p| f(p))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RequestHead {
|
||||||
|
/// Read the message headers.
|
||||||
|
pub fn headers(&self) -> &HeaderMap {
|
||||||
|
&self.headers
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mutable reference to the message headers.
|
||||||
|
pub fn headers_mut(&mut self) -> &mut HeaderMap {
|
||||||
|
&mut self.headers
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Is to uppercase headers with Camel-Case.
|
||||||
|
/// Default is `false`
|
||||||
|
#[inline]
|
||||||
|
pub fn camel_case_headers(&self) -> bool {
|
||||||
|
self.flags.contains(Flags::CAMEL_CASE)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set `true` to send headers which are formatted as Camel-Case.
|
||||||
|
#[inline]
|
||||||
|
pub fn set_camel_case_headers(&mut self, val: bool) {
|
||||||
|
if val {
|
||||||
|
self.flags.insert(Flags::CAMEL_CASE);
|
||||||
|
} else {
|
||||||
|
self.flags.remove(Flags::CAMEL_CASE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
/// Set connection type of the message
|
||||||
|
pub fn set_connection_type(&mut self, ctype: ConnectionType) {
|
||||||
|
match ctype {
|
||||||
|
ConnectionType::Close => self.flags.insert(Flags::CLOSE),
|
||||||
|
ConnectionType::KeepAlive => self.flags.insert(Flags::KEEP_ALIVE),
|
||||||
|
ConnectionType::Upgrade => self.flags.insert(Flags::UPGRADE),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
/// Connection type
|
||||||
|
pub fn connection_type(&self) -> ConnectionType {
|
||||||
|
if self.flags.contains(Flags::CLOSE) {
|
||||||
|
ConnectionType::Close
|
||||||
|
} else if self.flags.contains(Flags::KEEP_ALIVE) {
|
||||||
|
ConnectionType::KeepAlive
|
||||||
|
} else if self.flags.contains(Flags::UPGRADE) {
|
||||||
|
ConnectionType::Upgrade
|
||||||
|
} else if self.version < Version::HTTP_11 {
|
||||||
|
ConnectionType::Close
|
||||||
|
} else {
|
||||||
|
ConnectionType::KeepAlive
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Connection upgrade status
|
||||||
|
pub fn upgrade(&self) -> bool {
|
||||||
|
self.headers()
|
||||||
|
.get(header::CONNECTION)
|
||||||
|
.map(|hdr| {
|
||||||
|
if let Ok(s) = hdr.to_str() {
|
||||||
|
s.to_ascii_lowercase().contains("upgrade")
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.unwrap_or(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
/// Get response body chunking state
|
||||||
|
pub fn chunked(&self) -> bool {
|
||||||
|
!self.flags.contains(Flags::NO_CHUNKING)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn no_chunking(&mut self, val: bool) {
|
||||||
|
if val {
|
||||||
|
self.flags.insert(Flags::NO_CHUNKING);
|
||||||
|
} else {
|
||||||
|
self.flags.remove(Flags::NO_CHUNKING);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
/// Request contains `EXPECT` header
|
||||||
|
pub fn expect(&self) -> bool {
|
||||||
|
self.flags.contains(Flags::EXPECT)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn set_expect(&mut self) {
|
||||||
|
self.flags.insert(Flags::EXPECT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[allow(clippy::large_enum_variant)]
|
||||||
|
pub enum RequestHeadType {
|
||||||
|
Owned(RequestHead),
|
||||||
|
Rc(Rc<RequestHead>, Option<HeaderMap>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RequestHeadType {
|
||||||
|
pub fn extra_headers(&self) -> Option<&HeaderMap> {
|
||||||
|
match self {
|
||||||
|
RequestHeadType::Owned(_) => None,
|
||||||
|
RequestHeadType::Rc(_, headers) => headers.as_ref(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRef<RequestHead> for RequestHeadType {
|
||||||
|
fn as_ref(&self) -> &RequestHead {
|
||||||
|
match self {
|
||||||
|
RequestHeadType::Owned(head) => head,
|
||||||
|
RequestHeadType::Rc(head, _) => head.as_ref(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<RequestHead> for RequestHeadType {
|
||||||
|
fn from(head: RequestHead) -> Self {
|
||||||
|
RequestHeadType::Owned(head)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ResponseHead {
|
||||||
|
pub version: Version,
|
||||||
|
pub status: StatusCode,
|
||||||
|
pub headers: HeaderMap,
|
||||||
|
pub reason: Option<&'static str>,
|
||||||
|
pub(crate) extensions: RefCell<Extensions>,
|
||||||
|
flags: Flags,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ResponseHead {
|
||||||
|
/// Create new instance of `ResponseHead` type
|
||||||
|
#[inline]
|
||||||
|
pub fn new(status: StatusCode) -> ResponseHead {
|
||||||
|
ResponseHead {
|
||||||
|
status,
|
||||||
|
version: Version::default(),
|
||||||
|
headers: HeaderMap::with_capacity(12),
|
||||||
|
reason: None,
|
||||||
|
flags: Flags::empty(),
|
||||||
|
extensions: RefCell::new(Extensions::new()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Message extensions
|
||||||
|
#[inline]
|
||||||
|
pub fn extensions(&self) -> Ref<'_, Extensions> {
|
||||||
|
self.extensions.borrow()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mutable reference to a the message's extensions
|
||||||
|
#[inline]
|
||||||
|
pub fn extensions_mut(&self) -> RefMut<'_, Extensions> {
|
||||||
|
self.extensions.borrow_mut()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
/// Read the message headers.
|
||||||
|
pub fn headers(&self) -> &HeaderMap {
|
||||||
|
&self.headers
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
/// Mutable reference to the message headers.
|
||||||
|
pub fn headers_mut(&mut self) -> &mut HeaderMap {
|
||||||
|
&mut self.headers
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
/// Set connection type of the message
|
||||||
|
pub fn set_connection_type(&mut self, ctype: ConnectionType) {
|
||||||
|
match ctype {
|
||||||
|
ConnectionType::Close => self.flags.insert(Flags::CLOSE),
|
||||||
|
ConnectionType::KeepAlive => self.flags.insert(Flags::KEEP_ALIVE),
|
||||||
|
ConnectionType::Upgrade => self.flags.insert(Flags::UPGRADE),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn connection_type(&self) -> ConnectionType {
|
||||||
|
if self.flags.contains(Flags::CLOSE) {
|
||||||
|
ConnectionType::Close
|
||||||
|
} else if self.flags.contains(Flags::KEEP_ALIVE) {
|
||||||
|
ConnectionType::KeepAlive
|
||||||
|
} else if self.flags.contains(Flags::UPGRADE) {
|
||||||
|
ConnectionType::Upgrade
|
||||||
|
} else if self.version < Version::HTTP_11 {
|
||||||
|
ConnectionType::Close
|
||||||
|
} else {
|
||||||
|
ConnectionType::KeepAlive
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if keep-alive is enabled
|
||||||
|
#[inline]
|
||||||
|
pub fn keep_alive(&self) -> bool {
|
||||||
|
self.connection_type() == ConnectionType::KeepAlive
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check upgrade status of this message
|
||||||
|
#[inline]
|
||||||
|
pub fn upgrade(&self) -> bool {
|
||||||
|
self.connection_type() == ConnectionType::Upgrade
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get custom reason for the response
|
||||||
|
#[inline]
|
||||||
|
pub fn reason(&self) -> &str {
|
||||||
|
self.reason.unwrap_or_else(|| {
|
||||||
|
self.status
|
||||||
|
.canonical_reason()
|
||||||
|
.unwrap_or("<unknown status code>")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn conn_type(&self) -> Option<ConnectionType> {
|
||||||
|
if self.flags.contains(Flags::CLOSE) {
|
||||||
|
Some(ConnectionType::Close)
|
||||||
|
} else if self.flags.contains(Flags::KEEP_ALIVE) {
|
||||||
|
Some(ConnectionType::KeepAlive)
|
||||||
|
} else if self.flags.contains(Flags::UPGRADE) {
|
||||||
|
Some(ConnectionType::Upgrade)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
/// Get response body chunking state
|
||||||
|
pub fn chunked(&self) -> bool {
|
||||||
|
!self.flags.contains(Flags::NO_CHUNKING)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
/// Set no chunking for payload
|
||||||
|
pub fn no_chunking(&mut self, val: bool) {
|
||||||
|
if val {
|
||||||
|
self.flags.insert(Flags::NO_CHUNKING);
|
||||||
|
} else {
|
||||||
|
self.flags.remove(Flags::NO_CHUNKING);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Message<T: Head> {
|
pub struct Message<T: Head> {
|
||||||
/// Rc here should not be cloned by anyone.
|
/// Rc here should not be cloned by anyone.
|
||||||
/// It's used to reuse allocation of T and no shared ownership is allowed.
|
/// It's used to reuse allocation of T and no shared ownership is allowed.
|
||||||
@@ -43,7 +340,6 @@ pub struct Message<T: Head> {
|
|||||||
|
|
||||||
impl<T: Head> Message<T> {
|
impl<T: Head> Message<T> {
|
||||||
/// Get new message from the pool of objects
|
/// Get new message from the pool of objects
|
||||||
#[allow(clippy::new_without_default)]
|
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
T::with_pool(MessagePool::get_message)
|
T::with_pool(MessagePool::get_message)
|
||||||
}
|
}
|
||||||
@@ -69,12 +365,53 @@ impl<T: Head> Drop for Message<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) struct BoxedResponseHead {
|
||||||
|
head: Option<Box<ResponseHead>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BoxedResponseHead {
|
||||||
|
/// Get new message from the pool of objects
|
||||||
|
pub fn new(status: StatusCode) -> Self {
|
||||||
|
RESPONSE_POOL.with(|p| p.get_message(status))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::Deref for BoxedResponseHead {
|
||||||
|
type Target = ResponseHead;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
self.head.as_ref().unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::DerefMut for BoxedResponseHead {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
self.head.as_mut().unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for BoxedResponseHead {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if let Some(head) = self.head.take() {
|
||||||
|
RESPONSE_POOL.with(move |p| p.release(head))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
/// Request's objects pool
|
/// Request's objects pool
|
||||||
pub struct MessagePool<T: Head>(RefCell<Vec<Rc<T>>>);
|
pub struct MessagePool<T: Head>(RefCell<Vec<Rc<T>>>);
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[allow(clippy::vec_box)]
|
||||||
|
/// Request's objects pool
|
||||||
|
pub struct BoxedResponsePool(RefCell<Vec<Box<ResponseHead>>>);
|
||||||
|
|
||||||
|
thread_local!(static REQUEST_POOL: MessagePool<RequestHead> = MessagePool::<RequestHead>::create());
|
||||||
|
thread_local!(static RESPONSE_POOL: BoxedResponsePool = BoxedResponsePool::create());
|
||||||
|
|
||||||
impl<T: Head> MessagePool<T> {
|
impl<T: Head> MessagePool<T> {
|
||||||
pub(crate) fn create() -> MessagePool<T> {
|
fn create() -> MessagePool<T> {
|
||||||
MessagePool(RefCell::new(Vec::with_capacity(128)))
|
MessagePool(RefCell::new(Vec::with_capacity(128)))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,11 +433,43 @@ impl<T: Head> MessagePool<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
/// Release message instance
|
/// Release request instance
|
||||||
fn release(&self, msg: Rc<T>) {
|
fn release(&self, msg: Rc<T>) {
|
||||||
let pool = &mut self.0.borrow_mut();
|
let v = &mut self.0.borrow_mut();
|
||||||
if pool.len() < 128 {
|
if v.len() < 128 {
|
||||||
pool.push(msg);
|
v.push(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BoxedResponsePool {
|
||||||
|
fn create() -> BoxedResponsePool {
|
||||||
|
BoxedResponsePool(RefCell::new(Vec::with_capacity(128)))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get message from the pool
|
||||||
|
#[inline]
|
||||||
|
fn get_message(&self, status: StatusCode) -> BoxedResponseHead {
|
||||||
|
if let Some(mut head) = self.0.borrow_mut().pop() {
|
||||||
|
head.reason = None;
|
||||||
|
head.status = status;
|
||||||
|
head.headers.clear();
|
||||||
|
head.flags = Flags::empty();
|
||||||
|
BoxedResponseHead { head: Some(head) }
|
||||||
|
} else {
|
||||||
|
BoxedResponseHead {
|
||||||
|
head: Some(Box::new(ResponseHead::new(status))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
/// Release request instance
|
||||||
|
fn release(&self, mut msg: Box<ResponseHead>) {
|
||||||
|
let v = &mut self.0.borrow_mut();
|
||||||
|
if v.len() < 128 {
|
||||||
|
msg.extensions.get_mut().clear();
|
||||||
|
v.push(msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,89 +1,67 @@
|
|||||||
use std::{
|
use std::pin::Pin;
|
||||||
mem,
|
use std::task::{Context, Poll};
|
||||||
pin::Pin,
|
|
||||||
task::{Context, Poll},
|
|
||||||
};
|
|
||||||
|
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use futures_core::Stream;
|
use futures_core::Stream;
|
||||||
|
use h2::RecvStream;
|
||||||
|
|
||||||
use crate::error::PayloadError;
|
use crate::error::PayloadError;
|
||||||
|
|
||||||
/// A boxed payload stream.
|
/// A boxed payload.
|
||||||
pub type BoxedPayloadStream = Pin<Box<dyn Stream<Item = Result<Bytes, PayloadError>>>>;
|
pub type PayloadStream = Pin<Box<dyn Stream<Item = Result<Bytes, PayloadError>>>>;
|
||||||
|
|
||||||
#[deprecated(since = "4.0.0", note = "Renamed to `BoxedPayloadStream`.")]
|
|
||||||
pub type PayloadStream = BoxedPayloadStream;
|
|
||||||
|
|
||||||
pin_project_lite::pin_project! {
|
|
||||||
/// A streaming payload.
|
/// A streaming payload.
|
||||||
#[project = PayloadProj]
|
pub enum Payload<S = PayloadStream> {
|
||||||
pub enum Payload<S = BoxedPayloadStream> {
|
|
||||||
None,
|
None,
|
||||||
H1 { payload: crate::h1::Payload },
|
H1(crate::h1::Payload),
|
||||||
H2 { payload: crate::h2::Payload },
|
H2(crate::h2::Payload),
|
||||||
Stream { #[pin] payload: S },
|
Stream(S),
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S> From<crate::h1::Payload> for Payload<S> {
|
impl<S> From<crate::h1::Payload> for Payload<S> {
|
||||||
fn from(payload: crate::h1::Payload) -> Self {
|
fn from(v: crate::h1::Payload) -> Self {
|
||||||
Payload::H1 { payload }
|
Payload::H1(v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S> From<crate::h2::Payload> for Payload<S> {
|
impl<S> From<crate::h2::Payload> for Payload<S> {
|
||||||
fn from(payload: crate::h2::Payload) -> Self {
|
fn from(v: crate::h2::Payload) -> Self {
|
||||||
Payload::H2 { payload }
|
Payload::H2(v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S> From<h2::RecvStream> for Payload<S> {
|
impl<S> From<RecvStream> for Payload<S> {
|
||||||
fn from(stream: h2::RecvStream) -> Self {
|
fn from(v: RecvStream) -> Self {
|
||||||
Payload::H2 {
|
Payload::H2(crate::h2::Payload::new(v))
|
||||||
payload: crate::h2::Payload::new(stream),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<BoxedPayloadStream> for Payload {
|
impl From<PayloadStream> for Payload {
|
||||||
fn from(payload: BoxedPayloadStream) -> Self {
|
fn from(pl: PayloadStream) -> Self {
|
||||||
Payload::Stream { payload }
|
Payload::Stream(pl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S> Payload<S> {
|
impl<S> Payload<S> {
|
||||||
/// Takes current payload and replaces it with `None` value
|
/// Takes current payload and replaces it with `None` value
|
||||||
pub fn take(&mut self) -> Payload<S> {
|
pub fn take(&mut self) -> Payload<S> {
|
||||||
mem::replace(self, Payload::None)
|
std::mem::replace(self, Payload::None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S> Stream for Payload<S>
|
impl<S> Stream for Payload<S>
|
||||||
where
|
where
|
||||||
S: Stream<Item = Result<Bytes, PayloadError>>,
|
S: Stream<Item = Result<Bytes, PayloadError>> + Unpin,
|
||||||
{
|
{
|
||||||
type Item = Result<Bytes, PayloadError>;
|
type Item = Result<Bytes, PayloadError>;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||||
match self.project() {
|
match self.get_mut() {
|
||||||
PayloadProj::None => Poll::Ready(None),
|
Payload::None => Poll::Ready(None),
|
||||||
PayloadProj::H1 { payload } => Pin::new(payload).poll_next(cx),
|
Payload::H1(ref mut pl) => pl.readany(cx),
|
||||||
PayloadProj::H2 { payload } => Pin::new(payload).poll_next(cx),
|
Payload::H2(ref mut pl) => Pin::new(pl).poll_next(cx),
|
||||||
PayloadProj::Stream { payload } => payload.poll_next(cx),
|
Payload::Stream(ref mut pl) => Pin::new(pl).poll_next(cx),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use std::panic::{RefUnwindSafe, UnwindSafe};
|
|
||||||
|
|
||||||
use static_assertions::{assert_impl_all, assert_not_impl_any};
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
assert_impl_all!(Payload: Unpin);
|
|
||||||
assert_not_impl_any!(Payload: Send, Sync, UnwindSafe, RefUnwindSafe);
|
|
||||||
}
|
|
||||||
|
@@ -10,12 +10,15 @@ use std::{
|
|||||||
use http::{header, Method, Uri, Version};
|
use http::{header, Method, Uri, Version};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
header::HeaderMap, BoxedPayloadStream, Extensions, HttpMessage, Message, Payload,
|
extensions::Extensions,
|
||||||
RequestHead,
|
header::HeaderMap,
|
||||||
|
message::{Message, RequestHead},
|
||||||
|
payload::{Payload, PayloadStream},
|
||||||
|
HttpMessage,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// An HTTP request.
|
/// An HTTP request.
|
||||||
pub struct Request<P = BoxedPayloadStream> {
|
pub struct Request<P = PayloadStream> {
|
||||||
pub(crate) payload: Payload<P>,
|
pub(crate) payload: Payload<P>,
|
||||||
pub(crate) head: Message<RequestHead>,
|
pub(crate) head: Message<RequestHead>,
|
||||||
pub(crate) conn_data: Option<Rc<Extensions>>,
|
pub(crate) conn_data: Option<Rc<Extensions>>,
|
||||||
@@ -47,7 +50,7 @@ impl<P> HttpMessage for Request<P> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Message<RequestHead>> for Request<BoxedPayloadStream> {
|
impl From<Message<RequestHead>> for Request<PayloadStream> {
|
||||||
fn from(head: Message<RequestHead>) -> Self {
|
fn from(head: Message<RequestHead>) -> Self {
|
||||||
Request {
|
Request {
|
||||||
head,
|
head,
|
||||||
@@ -58,10 +61,9 @@ impl From<Message<RequestHead>> for Request<BoxedPayloadStream> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Request<BoxedPayloadStream> {
|
impl Request<PayloadStream> {
|
||||||
/// Create new Request instance
|
/// Create new Request instance
|
||||||
#[allow(clippy::new_without_default)]
|
pub fn new() -> Request<PayloadStream> {
|
||||||
pub fn new() -> Request<BoxedPayloadStream> {
|
|
||||||
Request {
|
Request {
|
||||||
head: Message::new(),
|
head: Message::new(),
|
||||||
payload: Payload::None,
|
payload: Payload::None,
|
||||||
@@ -204,7 +206,7 @@ impl<P> Request<P> {
|
|||||||
|
|
||||||
/// Returns the request data container, leaving an empty one in it's place.
|
/// Returns the request data container, leaving an empty one in it's place.
|
||||||
pub fn take_req_data(&mut self) -> Extensions {
|
pub fn take_req_data(&mut self) -> Extensions {
|
||||||
mem::take(self.req_data.get_mut())
|
mem::take(&mut self.req_data.get_mut())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@@ -1,174 +0,0 @@
|
|||||||
use std::{net, rc::Rc};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
header::{self, HeaderMap},
|
|
||||||
message::{Flags, Head, MessagePool},
|
|
||||||
ConnectionType, Method, Uri, Version,
|
|
||||||
};
|
|
||||||
|
|
||||||
thread_local! {
|
|
||||||
static REQUEST_POOL: MessagePool<RequestHead> = MessagePool::<RequestHead>::create()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct RequestHead {
|
|
||||||
pub method: Method,
|
|
||||||
pub uri: Uri,
|
|
||||||
pub version: Version,
|
|
||||||
pub headers: HeaderMap,
|
|
||||||
pub peer_addr: Option<net::SocketAddr>,
|
|
||||||
flags: Flags,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for RequestHead {
|
|
||||||
fn default() -> RequestHead {
|
|
||||||
RequestHead {
|
|
||||||
method: Method::default(),
|
|
||||||
uri: Uri::default(),
|
|
||||||
version: Version::HTTP_11,
|
|
||||||
headers: HeaderMap::with_capacity(16),
|
|
||||||
peer_addr: None,
|
|
||||||
flags: Flags::empty(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Head for RequestHead {
|
|
||||||
fn clear(&mut self) {
|
|
||||||
self.flags = Flags::empty();
|
|
||||||
self.headers.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn with_pool<F, R>(f: F) -> R
|
|
||||||
where
|
|
||||||
F: FnOnce(&MessagePool<Self>) -> R,
|
|
||||||
{
|
|
||||||
REQUEST_POOL.with(|p| f(p))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RequestHead {
|
|
||||||
/// Read the message headers.
|
|
||||||
pub fn headers(&self) -> &HeaderMap {
|
|
||||||
&self.headers
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Mutable reference to the message headers.
|
|
||||||
pub fn headers_mut(&mut self) -> &mut HeaderMap {
|
|
||||||
&mut self.headers
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Is to uppercase headers with Camel-Case.
|
|
||||||
/// Default is `false`
|
|
||||||
#[inline]
|
|
||||||
pub fn camel_case_headers(&self) -> bool {
|
|
||||||
self.flags.contains(Flags::CAMEL_CASE)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set `true` to send headers which are formatted as Camel-Case.
|
|
||||||
#[inline]
|
|
||||||
pub fn set_camel_case_headers(&mut self, val: bool) {
|
|
||||||
if val {
|
|
||||||
self.flags.insert(Flags::CAMEL_CASE);
|
|
||||||
} else {
|
|
||||||
self.flags.remove(Flags::CAMEL_CASE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
/// Set connection type of the message
|
|
||||||
pub fn set_connection_type(&mut self, ctype: ConnectionType) {
|
|
||||||
match ctype {
|
|
||||||
ConnectionType::Close => self.flags.insert(Flags::CLOSE),
|
|
||||||
ConnectionType::KeepAlive => self.flags.insert(Flags::KEEP_ALIVE),
|
|
||||||
ConnectionType::Upgrade => self.flags.insert(Flags::UPGRADE),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
/// Connection type
|
|
||||||
pub fn connection_type(&self) -> ConnectionType {
|
|
||||||
if self.flags.contains(Flags::CLOSE) {
|
|
||||||
ConnectionType::Close
|
|
||||||
} else if self.flags.contains(Flags::KEEP_ALIVE) {
|
|
||||||
ConnectionType::KeepAlive
|
|
||||||
} else if self.flags.contains(Flags::UPGRADE) {
|
|
||||||
ConnectionType::Upgrade
|
|
||||||
} else if self.version < Version::HTTP_11 {
|
|
||||||
ConnectionType::Close
|
|
||||||
} else {
|
|
||||||
ConnectionType::KeepAlive
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Connection upgrade status
|
|
||||||
pub fn upgrade(&self) -> bool {
|
|
||||||
self.headers()
|
|
||||||
.get(header::CONNECTION)
|
|
||||||
.map(|hdr| {
|
|
||||||
if let Ok(s) = hdr.to_str() {
|
|
||||||
s.to_ascii_lowercase().contains("upgrade")
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.unwrap_or(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
/// Get response body chunking state
|
|
||||||
pub fn chunked(&self) -> bool {
|
|
||||||
!self.flags.contains(Flags::NO_CHUNKING)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn no_chunking(&mut self, val: bool) {
|
|
||||||
if val {
|
|
||||||
self.flags.insert(Flags::NO_CHUNKING);
|
|
||||||
} else {
|
|
||||||
self.flags.remove(Flags::NO_CHUNKING);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
/// Request contains `EXPECT` header
|
|
||||||
pub fn expect(&self) -> bool {
|
|
||||||
self.flags.contains(Flags::EXPECT)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub(crate) fn set_expect(&mut self) {
|
|
||||||
self.flags.insert(Flags::EXPECT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
#[allow(clippy::large_enum_variant)]
|
|
||||||
pub enum RequestHeadType {
|
|
||||||
Owned(RequestHead),
|
|
||||||
Rc(Rc<RequestHead>, Option<HeaderMap>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RequestHeadType {
|
|
||||||
pub fn extra_headers(&self) -> Option<&HeaderMap> {
|
|
||||||
match self {
|
|
||||||
RequestHeadType::Owned(_) => None,
|
|
||||||
RequestHeadType::Rc(_, headers) => headers.as_ref(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsRef<RequestHead> for RequestHeadType {
|
|
||||||
fn as_ref(&self) -> &RequestHead {
|
|
||||||
match self {
|
|
||||||
RequestHeadType::Owned(head) => head,
|
|
||||||
RequestHeadType::Rc(head, _) => head.as_ref(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<RequestHead> for RequestHeadType {
|
|
||||||
fn from(head: RequestHead) -> Self {
|
|
||||||
RequestHeadType::Owned(head)
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,7 +0,0 @@
|
|||||||
//! HTTP requests.
|
|
||||||
|
|
||||||
mod head;
|
|
||||||
mod request;
|
|
||||||
|
|
||||||
pub use self::head::{RequestHead, RequestHeadType};
|
|
||||||
pub use self::request::Request;
|
|
@@ -10,9 +10,10 @@ use bytestring::ByteString;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
body::{BoxBody, MessageBody},
|
body::{BoxBody, MessageBody},
|
||||||
|
extensions::Extensions,
|
||||||
header::{self, HeaderMap, TryIntoHeaderValue},
|
header::{self, HeaderMap, TryIntoHeaderValue},
|
||||||
responses::BoxedResponseHead,
|
message::{BoxedResponseHead, ResponseHead},
|
||||||
Error, Extensions, ResponseBuilder, ResponseHead, StatusCode,
|
Error, ResponseBuilder, StatusCode,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// An HTTP response.
|
/// An HTTP response.
|
||||||
@@ -169,7 +170,7 @@ impl<B> Response<B> {
|
|||||||
/// Returns split head and body.
|
/// Returns split head and body.
|
||||||
///
|
///
|
||||||
/// # Implementation Notes
|
/// # Implementation Notes
|
||||||
/// Due to internal performance optimizations, the first element of the returned tuple is a
|
/// Due to internal performance optimisations, the first element of the returned tuple is a
|
||||||
/// `Response` as well but only contains the head of the response this was called on.
|
/// `Response` as well but only contains the head of the response this was called on.
|
||||||
pub fn into_parts(self) -> (Response<()>, B) {
|
pub fn into_parts(self) -> (Response<()>, B) {
|
||||||
self.replace_body(())
|
self.replace_body(())
|
||||||
@@ -193,7 +194,7 @@ impl<B> Response<B> {
|
|||||||
where
|
where
|
||||||
B: MessageBody + 'static,
|
B: MessageBody + 'static,
|
||||||
{
|
{
|
||||||
self.map_body(|_, body| body.boxed())
|
self.map_body(|_, body| BoxBody::new(body))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns body, consuming this response.
|
/// Returns body, consuming this response.
|
@@ -9,8 +9,8 @@ use crate::{
|
|||||||
body::{EitherBody, MessageBody},
|
body::{EitherBody, MessageBody},
|
||||||
error::{Error, HttpError},
|
error::{Error, HttpError},
|
||||||
header::{self, TryIntoHeaderPair, TryIntoHeaderValue},
|
header::{self, TryIntoHeaderPair, TryIntoHeaderValue},
|
||||||
responses::{BoxedResponseHead, ResponseHead},
|
message::{BoxedResponseHead, ConnectionType, ResponseHead},
|
||||||
ConnectionType, Extensions, Response, StatusCode,
|
Extensions, Response, StatusCode,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// An HTTP response builder.
|
/// An HTTP response builder.
|
@@ -1,208 +0,0 @@
|
|||||||
//! Response head type and caching pool.
|
|
||||||
|
|
||||||
use std::{
|
|
||||||
cell::{Ref, RefCell, RefMut},
|
|
||||||
ops,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
header::HeaderMap, message::Flags, ConnectionType, Extensions, StatusCode, Version,
|
|
||||||
};
|
|
||||||
|
|
||||||
thread_local! {
|
|
||||||
static RESPONSE_POOL: BoxedResponsePool = BoxedResponsePool::create();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct ResponseHead {
|
|
||||||
pub version: Version,
|
|
||||||
pub status: StatusCode,
|
|
||||||
pub headers: HeaderMap,
|
|
||||||
pub reason: Option<&'static str>,
|
|
||||||
pub(crate) extensions: RefCell<Extensions>,
|
|
||||||
flags: Flags,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ResponseHead {
|
|
||||||
/// Create new instance of `ResponseHead` type
|
|
||||||
#[inline]
|
|
||||||
pub fn new(status: StatusCode) -> ResponseHead {
|
|
||||||
ResponseHead {
|
|
||||||
status,
|
|
||||||
version: Version::HTTP_11,
|
|
||||||
headers: HeaderMap::with_capacity(12),
|
|
||||||
reason: None,
|
|
||||||
flags: Flags::empty(),
|
|
||||||
extensions: RefCell::new(Extensions::new()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
/// Read the message headers.
|
|
||||||
pub fn headers(&self) -> &HeaderMap {
|
|
||||||
&self.headers
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
/// Mutable reference to the message headers.
|
|
||||||
pub fn headers_mut(&mut self) -> &mut HeaderMap {
|
|
||||||
&mut self.headers
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Message extensions
|
|
||||||
#[inline]
|
|
||||||
pub fn extensions(&self) -> Ref<'_, Extensions> {
|
|
||||||
self.extensions.borrow()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Mutable reference to a the message's extensions
|
|
||||||
#[inline]
|
|
||||||
pub fn extensions_mut(&self) -> RefMut<'_, Extensions> {
|
|
||||||
self.extensions.borrow_mut()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
/// Set connection type of the message
|
|
||||||
pub fn set_connection_type(&mut self, ctype: ConnectionType) {
|
|
||||||
match ctype {
|
|
||||||
ConnectionType::Close => self.flags.insert(Flags::CLOSE),
|
|
||||||
ConnectionType::KeepAlive => self.flags.insert(Flags::KEEP_ALIVE),
|
|
||||||
ConnectionType::Upgrade => self.flags.insert(Flags::UPGRADE),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn connection_type(&self) -> ConnectionType {
|
|
||||||
if self.flags.contains(Flags::CLOSE) {
|
|
||||||
ConnectionType::Close
|
|
||||||
} else if self.flags.contains(Flags::KEEP_ALIVE) {
|
|
||||||
ConnectionType::KeepAlive
|
|
||||||
} else if self.flags.contains(Flags::UPGRADE) {
|
|
||||||
ConnectionType::Upgrade
|
|
||||||
} else if self.version < Version::HTTP_11 {
|
|
||||||
ConnectionType::Close
|
|
||||||
} else {
|
|
||||||
ConnectionType::KeepAlive
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Check if keep-alive is enabled
|
|
||||||
#[inline]
|
|
||||||
pub fn keep_alive(&self) -> bool {
|
|
||||||
self.connection_type() == ConnectionType::KeepAlive
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Check upgrade status of this message
|
|
||||||
#[inline]
|
|
||||||
pub fn upgrade(&self) -> bool {
|
|
||||||
self.connection_type() == ConnectionType::Upgrade
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get custom reason for the response
|
|
||||||
#[inline]
|
|
||||||
pub fn reason(&self) -> &str {
|
|
||||||
self.reason.unwrap_or_else(|| {
|
|
||||||
self.status
|
|
||||||
.canonical_reason()
|
|
||||||
.unwrap_or("<unknown status code>")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub(crate) fn conn_type(&self) -> Option<ConnectionType> {
|
|
||||||
if self.flags.contains(Flags::CLOSE) {
|
|
||||||
Some(ConnectionType::Close)
|
|
||||||
} else if self.flags.contains(Flags::KEEP_ALIVE) {
|
|
||||||
Some(ConnectionType::KeepAlive)
|
|
||||||
} else if self.flags.contains(Flags::UPGRADE) {
|
|
||||||
Some(ConnectionType::Upgrade)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
/// Get response body chunking state
|
|
||||||
pub fn chunked(&self) -> bool {
|
|
||||||
!self.flags.contains(Flags::NO_CHUNKING)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
/// Set no chunking for payload
|
|
||||||
pub fn no_chunking(&mut self, val: bool) {
|
|
||||||
if val {
|
|
||||||
self.flags.insert(Flags::NO_CHUNKING);
|
|
||||||
} else {
|
|
||||||
self.flags.remove(Flags::NO_CHUNKING);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) struct BoxedResponseHead {
|
|
||||||
head: Option<Box<ResponseHead>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BoxedResponseHead {
|
|
||||||
/// Get new message from the pool of objects
|
|
||||||
pub fn new(status: StatusCode) -> Self {
|
|
||||||
RESPONSE_POOL.with(|p| p.get_message(status))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ops::Deref for BoxedResponseHead {
|
|
||||||
type Target = ResponseHead;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
self.head.as_ref().unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ops::DerefMut for BoxedResponseHead {
|
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
||||||
self.head.as_mut().unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for BoxedResponseHead {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
if let Some(head) = self.head.take() {
|
|
||||||
RESPONSE_POOL.with(move |p| p.release(head))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Request's objects pool
|
|
||||||
#[doc(hidden)]
|
|
||||||
pub struct BoxedResponsePool(#[allow(clippy::vec_box)] RefCell<Vec<Box<ResponseHead>>>);
|
|
||||||
|
|
||||||
impl BoxedResponsePool {
|
|
||||||
fn create() -> BoxedResponsePool {
|
|
||||||
BoxedResponsePool(RefCell::new(Vec::with_capacity(128)))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get message from the pool
|
|
||||||
#[inline]
|
|
||||||
fn get_message(&self, status: StatusCode) -> BoxedResponseHead {
|
|
||||||
if let Some(mut head) = self.0.borrow_mut().pop() {
|
|
||||||
head.reason = None;
|
|
||||||
head.status = status;
|
|
||||||
head.headers.clear();
|
|
||||||
head.flags = Flags::empty();
|
|
||||||
BoxedResponseHead { head: Some(head) }
|
|
||||||
} else {
|
|
||||||
BoxedResponseHead {
|
|
||||||
head: Some(Box::new(ResponseHead::new(status))),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Release request instance
|
|
||||||
#[inline]
|
|
||||||
fn release(&self, mut msg: Box<ResponseHead>) {
|
|
||||||
let pool = &mut self.0.borrow_mut();
|
|
||||||
if pool.len() < 128 {
|
|
||||||
msg.extensions.get_mut().clear();
|
|
||||||
pool.push(msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,11 +0,0 @@
|
|||||||
//! HTTP response.
|
|
||||||
|
|
||||||
mod builder;
|
|
||||||
mod head;
|
|
||||||
#[allow(clippy::module_inception)]
|
|
||||||
mod response;
|
|
||||||
|
|
||||||
pub use self::builder::ResponseBuilder;
|
|
||||||
pub(crate) use self::head::BoxedResponseHead;
|
|
||||||
pub use self::head::ResponseHead;
|
|
||||||
pub use self::response::Response;
|
|
@@ -493,9 +493,9 @@ where
|
|||||||
type Future = HttpServiceHandlerResponse<T, S, B, X, U>;
|
type Future = HttpServiceHandlerResponse<T, S, B, X, U>;
|
||||||
|
|
||||||
fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||||
self._poll_ready(cx).map_err(|err| {
|
self._poll_ready(cx).map_err(|e| {
|
||||||
log::error!("HTTP service readiness error: {:?}", err);
|
log::error!("HTTP service readiness error: {:?}", e);
|
||||||
DispatchError::Service(err)
|
DispatchError::Service(e)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -120,7 +120,7 @@ impl TestRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Set request payload.
|
/// Set request payload.
|
||||||
pub fn set_payload(&mut self, data: impl Into<Bytes>) -> &mut Self {
|
pub fn set_payload<B: Into<Bytes>>(&mut self, data: B) -> &mut Self {
|
||||||
let mut payload = crate::h1::Payload::empty();
|
let mut payload = crate::h1::Payload::empty();
|
||||||
payload.unread_data(data.into());
|
payload.unread_data(data.into());
|
||||||
parts(&mut self.0).payload = Some(payload.into());
|
parts(&mut self.0).payload = Some(payload.into());
|
||||||
@@ -264,7 +264,7 @@ impl TestSeqBuffer {
|
|||||||
|
|
||||||
/// Create new empty `TestBuffer` instance.
|
/// Create new empty `TestBuffer` instance.
|
||||||
pub fn empty() -> Self {
|
pub fn empty() -> Self {
|
||||||
Self::new(BytesMut::new())
|
Self::new("")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_buf(&self) -> Ref<'_, BytesMut> {
|
pub fn read_buf(&self) -> Ref<'_, BytesMut> {
|
||||||
|
@@ -9,7 +9,7 @@ use derive_more::{Display, Error, From};
|
|||||||
use http::{header, Method, StatusCode};
|
use http::{header, Method, StatusCode};
|
||||||
|
|
||||||
use crate::body::BoxBody;
|
use crate::body::BoxBody;
|
||||||
use crate::{header::HeaderValue, RequestHead, Response, ResponseBuilder};
|
use crate::{header::HeaderValue, message::RequestHead, response::Response, ResponseBuilder};
|
||||||
|
|
||||||
mod codec;
|
mod codec;
|
||||||
mod dispatcher;
|
mod dispatcher;
|
||||||
@@ -99,9 +99,8 @@ impl From<HandshakeError> for Response<BoxBody> {
|
|||||||
match err {
|
match err {
|
||||||
HandshakeError::GetMethodRequired => {
|
HandshakeError::GetMethodRequired => {
|
||||||
let mut res = Response::new(StatusCode::METHOD_NOT_ALLOWED);
|
let mut res = Response::new(StatusCode::METHOD_NOT_ALLOWED);
|
||||||
#[allow(clippy::declare_interior_mutable_const)]
|
res.headers_mut()
|
||||||
const HV_GET: HeaderValue = HeaderValue::from_static("GET");
|
.insert(header::ALLOW, HeaderValue::from_static("GET"));
|
||||||
res.headers_mut().insert(header::ALLOW, HV_GET);
|
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -7,7 +7,6 @@ use std::{
|
|||||||
io::{self, BufReader, Write},
|
io::{self, BufReader, Write},
|
||||||
net::{SocketAddr, TcpStream as StdTcpStream},
|
net::{SocketAddr, TcpStream as StdTcpStream},
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
task::Poll,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use actix_http::{
|
use actix_http::{
|
||||||
@@ -17,37 +16,25 @@ use actix_http::{
|
|||||||
Error, HttpService, Method, Request, Response, StatusCode, Version,
|
Error, HttpService, Method, Request, Response, StatusCode, Version,
|
||||||
};
|
};
|
||||||
use actix_http_test::test_server;
|
use actix_http_test::test_server;
|
||||||
use actix_rt::pin;
|
|
||||||
use actix_service::{fn_factory_with_config, fn_service};
|
use actix_service::{fn_factory_with_config, fn_service};
|
||||||
use actix_tls::connect::rustls::webpki_roots_cert_store;
|
use actix_tls::connect::rustls::webpki_roots_cert_store;
|
||||||
use actix_utils::future::{err, ok, poll_fn};
|
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::{ready, Stream};
|
use futures_core::Stream;
|
||||||
use futures_util::stream::once;
|
use futures_util::stream::{once, StreamExt as _};
|
||||||
use rustls::{Certificate, PrivateKey, ServerConfig as RustlsServerConfig, ServerName};
|
use rustls::{Certificate, PrivateKey, ServerConfig as RustlsServerConfig, ServerName};
|
||||||
use rustls_pemfile::{certs, pkcs8_private_keys};
|
use rustls_pemfile::{certs, pkcs8_private_keys};
|
||||||
|
|
||||||
async fn load_body<S>(stream: S) -> Result<BytesMut, PayloadError>
|
async fn load_body<S>(mut stream: S) -> Result<BytesMut, PayloadError>
|
||||||
where
|
where
|
||||||
S: Stream<Item = Result<Bytes, PayloadError>>,
|
S: Stream<Item = Result<Bytes, PayloadError>> + Unpin,
|
||||||
{
|
{
|
||||||
let mut buf = BytesMut::new();
|
let mut body = BytesMut::new();
|
||||||
|
while let Some(item) = stream.next().await {
|
||||||
pin!(stream);
|
body.extend_from_slice(&item?)
|
||||||
|
|
||||||
poll_fn(|cx| loop {
|
|
||||||
let body = stream.as_mut();
|
|
||||||
|
|
||||||
match ready!(body.poll_next(cx)) {
|
|
||||||
Some(Ok(bytes)) => buf.extend_from_slice(&*bytes),
|
|
||||||
None => return Poll::Ready(Ok(())),
|
|
||||||
Some(Err(err)) => return Poll::Ready(Err(err)),
|
|
||||||
}
|
}
|
||||||
})
|
Ok(body)
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(buf)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tls_config() -> RustlsServerConfig {
|
fn tls_config() -> RustlsServerConfig {
|
||||||
|
@@ -3,124 +3,120 @@
|
|||||||
## Unreleased - 2021-xx-xx
|
## Unreleased - 2021-xx-xx
|
||||||
|
|
||||||
|
|
||||||
## 0.4.0-beta.11 - 2021-12-27
|
|
||||||
* No significant changes since `0.4.0-beta.10`.
|
|
||||||
|
|
||||||
|
|
||||||
## 0.4.0-beta.10 - 2021-12-11
|
## 0.4.0-beta.10 - 2021-12-11
|
||||||
- No significant changes since `0.4.0-beta.9`.
|
* No significant changes since `0.4.0-beta.9`.
|
||||||
|
|
||||||
|
|
||||||
## 0.4.0-beta.9 - 2021-12-01
|
## 0.4.0-beta.9 - 2021-12-01
|
||||||
- Polling `Field` after dropping `Multipart` now fails immediately instead of hanging forever. [#2463]
|
* Polling `Field` after dropping `Multipart` now fails immediately instead of hanging forever. [#2463]
|
||||||
|
|
||||||
[#2463]: https://github.com/actix/actix-web/pull/2463
|
[#2463]: https://github.com/actix/actix-web/pull/2463
|
||||||
|
|
||||||
|
|
||||||
## 0.4.0-beta.8 - 2021-11-22
|
## 0.4.0-beta.8 - 2021-11-22
|
||||||
- Ensure a correct Content-Disposition header is included in every part of a multipart message. [#2451]
|
* Ensure a correct Content-Disposition header is included in every part of a multipart message. [#2451]
|
||||||
- Added `MultipartError::NoContentDisposition` variant. [#2451]
|
* Added `MultipartError::NoContentDisposition` variant. [#2451]
|
||||||
- Since Content-Disposition is now ensured, `Field::content_disposition` is now infallible. [#2451]
|
* Since Content-Disposition is now ensured, `Field::content_disposition` is now infallible. [#2451]
|
||||||
- Added `Field::name` method for getting the field name. [#2451]
|
* Added `Field::name` method for getting the field name. [#2451]
|
||||||
- `MultipartError` now marks variants with inner errors as the source. [#2451]
|
* `MultipartError` now marks variants with inner errors as the source. [#2451]
|
||||||
- `MultipartError` is now marked as non-exhaustive. [#2451]
|
* `MultipartError` is now marked as non-exhaustive. [#2451]
|
||||||
|
|
||||||
[#2451]: https://github.com/actix/actix-web/pull/2451
|
[#2451]: https://github.com/actix/actix-web/pull/2451
|
||||||
|
|
||||||
|
|
||||||
## 0.4.0-beta.7 - 2021-10-20
|
## 0.4.0-beta.7 - 2021-10-20
|
||||||
- Minimum supported Rust version (MSRV) is now 1.52.
|
* 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.
|
||||||
|
|
||||||
|
|
||||||
## 0.4.0-beta.5 - 2021-06-17
|
## 0.4.0-beta.5 - 2021-06-17
|
||||||
- No notable changes.
|
* No notable changes.
|
||||||
|
|
||||||
|
|
||||||
## 0.4.0-beta.4 - 2021-04-02
|
## 0.4.0-beta.4 - 2021-04-02
|
||||||
- No notable changes.
|
* No notable changes.
|
||||||
|
|
||||||
|
|
||||||
## 0.4.0-beta.3 - 2021-03-09
|
## 0.4.0-beta.3 - 2021-03-09
|
||||||
- No notable changes.
|
* No notable changes.
|
||||||
|
|
||||||
|
|
||||||
## 0.4.0-beta.2 - 2021-02-10
|
## 0.4.0-beta.2 - 2021-02-10
|
||||||
- No notable changes.
|
* No notable changes.
|
||||||
|
|
||||||
|
|
||||||
## 0.4.0-beta.1 - 2021-01-07
|
## 0.4.0-beta.1 - 2021-01-07
|
||||||
- Fix multipart consuming payload before header checks. [#1513]
|
* Fix multipart consuming payload before header checks. [#1513]
|
||||||
- Update `bytes` to `1.0`. [#1813]
|
* Update `bytes` to `1.0`. [#1813]
|
||||||
|
|
||||||
[#1813]: https://github.com/actix/actix-web/pull/1813
|
[#1813]: https://github.com/actix/actix-web/pull/1813
|
||||||
[#1513]: https://github.com/actix/actix-web/pull/1513
|
[#1513]: https://github.com/actix/actix-web/pull/1513
|
||||||
|
|
||||||
|
|
||||||
## 0.3.0 - 2020-09-11
|
## 0.3.0 - 2020-09-11
|
||||||
- No significant changes from `0.3.0-beta.2`.
|
* No significant changes from `0.3.0-beta.2`.
|
||||||
|
|
||||||
|
|
||||||
## 0.3.0-beta.2 - 2020-09-10
|
## 0.3.0-beta.2 - 2020-09-10
|
||||||
- Update `actix-*` dependencies to latest versions.
|
* Update `actix-*` dependencies to latest versions.
|
||||||
|
|
||||||
|
|
||||||
## 0.3.0-beta.1 - 2020-07-15
|
## 0.3.0-beta.1 - 2020-07-15
|
||||||
- Update `actix-web` to 3.0.0-beta.1
|
* Update `actix-web` to 3.0.0-beta.1
|
||||||
|
|
||||||
|
|
||||||
## 0.3.0-alpha.1 - 2020-05-25
|
## 0.3.0-alpha.1 - 2020-05-25
|
||||||
- Update `actix-web` to 3.0.0-alpha.3
|
* Update `actix-web` to 3.0.0-alpha.3
|
||||||
- Bump minimum supported Rust version to 1.40
|
* Bump minimum supported Rust version to 1.40
|
||||||
- Minimize `futures` dependencies
|
* Minimize `futures` dependencies
|
||||||
- Remove the unused `time` dependency
|
* Remove the unused `time` dependency
|
||||||
- Fix missing `std::error::Error` implement for `MultipartError`.
|
* Fix missing `std::error::Error` implement for `MultipartError`.
|
||||||
|
|
||||||
## [0.2.0] - 2019-12-20
|
## [0.2.0] - 2019-12-20
|
||||||
|
|
||||||
- Release
|
* Release
|
||||||
|
|
||||||
## [0.2.0-alpha.4] - 2019-12-xx
|
## [0.2.0-alpha.4] - 2019-12-xx
|
||||||
|
|
||||||
- Multipart handling now handles Pending during read of boundary #1205
|
* Multipart handling now handles Pending during read of boundary #1205
|
||||||
|
|
||||||
## [0.2.0-alpha.2] - 2019-12-03
|
## [0.2.0-alpha.2] - 2019-12-03
|
||||||
|
|
||||||
- Migrate to `std::future`
|
* Migrate to `std::future`
|
||||||
|
|
||||||
## [0.1.4] - 2019-09-12
|
## [0.1.4] - 2019-09-12
|
||||||
|
|
||||||
- Multipart handling now parses requests which do not end in CRLF #1038
|
* Multipart handling now parses requests which do not end in CRLF #1038
|
||||||
|
|
||||||
## [0.1.3] - 2019-08-18
|
## [0.1.3] - 2019-08-18
|
||||||
|
|
||||||
- Fix ring dependency from actix-web default features for #741.
|
* Fix ring dependency from actix-web default features for #741.
|
||||||
|
|
||||||
## [0.1.2] - 2019-06-02
|
## [0.1.2] - 2019-06-02
|
||||||
|
|
||||||
- Fix boundary parsing #876
|
* Fix boundary parsing #876
|
||||||
|
|
||||||
## [0.1.1] - 2019-05-25
|
## [0.1.1] - 2019-05-25
|
||||||
|
|
||||||
- Fix disconnect handling #834
|
* Fix disconnect handling #834
|
||||||
|
|
||||||
## [0.1.0] - 2019-05-18
|
## [0.1.0] - 2019-05-18
|
||||||
|
|
||||||
- Release
|
* Release
|
||||||
|
|
||||||
## [0.1.0-beta.4] - 2019-05-12
|
## [0.1.0-beta.4] - 2019-05-12
|
||||||
|
|
||||||
- Handle cancellation of uploads #736
|
* Handle cancellation of uploads #736
|
||||||
|
|
||||||
- Upgrade to actix-web 1.0.0-beta.4
|
* Upgrade to actix-web 1.0.0-beta.4
|
||||||
|
|
||||||
## [0.1.0-beta.1] - 2019-04-21
|
## [0.1.0-beta.1] - 2019-04-21
|
||||||
|
|
||||||
- Do not support nested multipart
|
* Do not support nested multipart
|
||||||
|
|
||||||
- Split multipart support to separate crate
|
* Split multipart support to separate crate
|
||||||
|
|
||||||
- Optimize multipart handling #634, #769
|
* Optimize multipart handling #634, #769
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "actix-multipart"
|
name = "actix-multipart"
|
||||||
version = "0.4.0-beta.11"
|
version = "0.4.0-beta.10"
|
||||||
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"]
|
||||||
@@ -15,7 +15,7 @@ path = "src/lib.rs"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix-utils = "3.0.0"
|
actix-utils = "3.0.0"
|
||||||
actix-web = { version = "4.0.0-beta.16", default-features = false }
|
actix-web = { version = "4.0.0-beta.14", default-features = false }
|
||||||
|
|
||||||
bytes = "1"
|
bytes = "1"
|
||||||
derive_more = "0.99.5"
|
derive_more = "0.99.5"
|
||||||
@@ -28,7 +28,7 @@ twoway = "0.2"
|
|||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
actix-rt = "2.2"
|
actix-rt = "2.2"
|
||||||
actix-http = "3.0.0-beta.17"
|
actix-http = "3.0.0-beta.15"
|
||||||
futures-util = { version = "0.3.7", default-features = false, features = ["alloc"] }
|
futures-util = { version = "0.3.7", default-features = false, features = ["alloc"] }
|
||||||
tokio = { version = "1.8", features = ["sync"] }
|
tokio = { version = "1", features = ["sync"] }
|
||||||
tokio-stream = "0.1"
|
tokio-stream = "0.1"
|
||||||
|
@@ -3,11 +3,11 @@
|
|||||||
> 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.11)
|
[](https://docs.rs/actix-multipart/0.4.0-beta.10)
|
||||||
[](https://blog.rust-lang.org/2021/05/06/Rust-1.52.0.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.11)
|
[](https://deps.rs/crate/actix-multipart/0.4.0-beta.10)
|
||||||
[](https://crates.io/crates/actix-multipart)
|
[](https://crates.io/crates/actix-multipart)
|
||||||
[](https://discord.gg/NWpN5mmg3x)
|
[](https://discord.gg/NWpN5mmg3x)
|
||||||
|
|
||||||
|
@@ -1233,7 +1233,7 @@ mod tests {
|
|||||||
|
|
||||||
// and should not consume the payload
|
// and should not consume the payload
|
||||||
match payload {
|
match payload {
|
||||||
actix_web::dev::Payload::H1 { .. } => {} //expected
|
actix_web::dev::Payload::H1(_) => {} //expected
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,23 +1,20 @@
|
|||||||
# Changes
|
# Changes
|
||||||
|
|
||||||
## Unreleased - 2021-xx-xx
|
## Unreleased - 2021-xx-xx
|
||||||
|
* Minimum supported Rust version (MSRV) is now 1.52.
|
||||||
|
|
||||||
## 0.5.0-beta.3 - 2021-12-17
|
|
||||||
- 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
|
||||||
- Introduce `ResourceDef::join`. [#380]
|
* Introduce `ResourceDef::join`. [#380]
|
||||||
- Disallow prefix routes with tail segments. [#379]
|
* Disallow prefix routes with tail segments. [#379]
|
||||||
- Enforce path separators on dynamic prefixes. [#378]
|
* Enforce path separators on dynamic prefixes. [#378]
|
||||||
- Improve malformed path error message. [#384]
|
* Improve malformed path error message. [#384]
|
||||||
- Prefix segments now always end with with a segment delimiter or end-of-input. [#2355]
|
* Prefix segments now always end with with a segment delimiter or end-of-input. [#2355]
|
||||||
- Prefix segments with trailing slashes define a trailing empty segment. [#2355]
|
* Prefix segments with trailing slashes define a trailing empty segment. [#2355]
|
||||||
- Support multi-pattern prefixes and joins. [#2356]
|
* Support multi-pattern prefixes and joins. [#2356]
|
||||||
- `ResourceDef::pattern` now returns the first pattern in multi-pattern resources. [#2356]
|
* `ResourceDef::pattern` now returns the first pattern in multi-pattern resources. [#2356]
|
||||||
- Support `build_resource_path` on multi-pattern resources. [#2356]
|
* Support `build_resource_path` on multi-pattern resources. [#2356]
|
||||||
- Minimum supported Rust version (MSRV) is now 1.51.
|
* Minimum supported Rust version (MSRV) is now 1.51.
|
||||||
|
|
||||||
[#378]: https://github.com/actix/actix-net/pull/378
|
[#378]: https://github.com/actix/actix-net/pull/378
|
||||||
[#379]: https://github.com/actix/actix-net/pull/379
|
[#379]: https://github.com/actix/actix-net/pull/379
|
||||||
@@ -28,23 +25,23 @@
|
|||||||
|
|
||||||
|
|
||||||
## 0.5.0-beta.1 - 2021-07-20
|
## 0.5.0-beta.1 - 2021-07-20
|
||||||
- Fix a bug in multi-patterns where static patterns are interpreted as regex. [#366]
|
* Fix a bug in multi-patterns where static patterns are interpreted as regex. [#366]
|
||||||
- Introduce `ResourceDef::pattern_iter` to get an iterator over all patterns in a multi-pattern resource. [#373]
|
* Introduce `ResourceDef::pattern_iter` to get an iterator over all patterns in a multi-pattern resource. [#373]
|
||||||
- Fix segment interpolation leaving `Path` in unintended state after matching. [#368]
|
* Fix segment interpolation leaving `Path` in unintended state after matching. [#368]
|
||||||
- Fix `ResourceDef` `PartialEq` implementation. [#373]
|
* Fix `ResourceDef` `PartialEq` implementation. [#373]
|
||||||
- Re-work `IntoPatterns` trait, adding a `Patterns` enum. [#372]
|
* Re-work `IntoPatterns` trait, adding a `Patterns` enum. [#372]
|
||||||
- Implement `IntoPatterns` for `bytestring::ByteString`. [#372]
|
* Implement `IntoPatterns` for `bytestring::ByteString`. [#372]
|
||||||
- Rename `Path::{len => segment_count}` to be more descriptive of it's purpose. [#370]
|
* Rename `Path::{len => segment_count}` to be more descriptive of it's purpose. [#370]
|
||||||
- Rename `ResourceDef::{resource_path => resource_path_from_iter}`. [#371]
|
* Rename `ResourceDef::{resource_path => resource_path_from_iter}`. [#371]
|
||||||
- `ResourceDef::resource_path_from_iter` now takes an `IntoIterator`. [#373]
|
* `ResourceDef::resource_path_from_iter` now takes an `IntoIterator`. [#373]
|
||||||
- Rename `ResourceDef::{resource_path_named => resource_path_from_map}`. [#371]
|
* Rename `ResourceDef::{resource_path_named => resource_path_from_map}`. [#371]
|
||||||
- Rename `ResourceDef::{is_prefix_match => find_match}`. [#373]
|
* Rename `ResourceDef::{is_prefix_match => find_match}`. [#373]
|
||||||
- Rename `ResourceDef::{match_path => capture_match_info}`. [#373]
|
* Rename `ResourceDef::{match_path => capture_match_info}`. [#373]
|
||||||
- Rename `ResourceDef::{match_path_checked => capture_match_info_fn}`. [#373]
|
* Rename `ResourceDef::{match_path_checked => capture_match_info_fn}`. [#373]
|
||||||
- Remove `ResourceDef::name_mut` and introduce `ResourceDef::set_name`. [#373]
|
* Remove `ResourceDef::name_mut` and introduce `ResourceDef::set_name`. [#373]
|
||||||
- Rename `Router::{*_checked => *_fn}`. [#373]
|
* Rename `Router::{*_checked => *_fn}`. [#373]
|
||||||
- Return type of `ResourceDef::name` is now `Option<&str>`. [#373]
|
* Return type of `ResourceDef::name` is now `Option<&str>`. [#373]
|
||||||
- Return type of `ResourceDef::pattern` is now `Option<&str>`. [#373]
|
* Return type of `ResourceDef::pattern` is now `Option<&str>`. [#373]
|
||||||
|
|
||||||
[#368]: https://github.com/actix/actix-net/pull/368
|
[#368]: https://github.com/actix/actix-net/pull/368
|
||||||
[#366]: https://github.com/actix/actix-net/pull/366
|
[#366]: https://github.com/actix/actix-net/pull/366
|
||||||
@@ -56,10 +53,10 @@
|
|||||||
|
|
||||||
|
|
||||||
## 0.4.0 - 2021-06-06
|
## 0.4.0 - 2021-06-06
|
||||||
- When matching path parameters, `%25` is now kept in the percent-encoded form; no longer decoded to `%`. [#357]
|
* When matching path parameters, `%25` is now kept in the percent-encoded form; no longer decoded to `%`. [#357]
|
||||||
- Path tail patterns now match new lines (`\n`) in request URL. [#360]
|
* Path tail patterns now match new lines (`\n`) in request URL. [#360]
|
||||||
- Fixed a safety bug where `Path` could return a malformed string after percent decoding. [#359]
|
* Fixed a safety bug where `Path` could return a malformed string after percent decoding. [#359]
|
||||||
- Methods `Path::{add, add_static}` now take `impl Into<Cow<'static, str>>`. [#345]
|
* Methods `Path::{add, add_static}` now take `impl Into<Cow<'static, str>>`. [#345]
|
||||||
|
|
||||||
[#345]: https://github.com/actix/actix-net/pull/345
|
[#345]: https://github.com/actix/actix-net/pull/345
|
||||||
[#357]: https://github.com/actix/actix-net/pull/357
|
[#357]: https://github.com/actix/actix-net/pull/357
|
||||||
@@ -68,68 +65,68 @@
|
|||||||
|
|
||||||
|
|
||||||
## 0.3.0 - 2019-12-31
|
## 0.3.0 - 2019-12-31
|
||||||
- Version was yanked previously. See https://crates.io/crates/actix-router/0.3.0
|
* Version was yanked previously. See https://crates.io/crates/actix-router/0.3.0
|
||||||
|
|
||||||
|
|
||||||
## 0.2.7 - 2021-02-06
|
## 0.2.7 - 2021-02-06
|
||||||
- Add `Router::recognize_checked` [#247]
|
* Add `Router::recognize_checked` [#247]
|
||||||
|
|
||||||
[#247]: https://github.com/actix/actix-net/pull/247
|
[#247]: https://github.com/actix/actix-net/pull/247
|
||||||
|
|
||||||
|
|
||||||
## 0.2.6 - 2021-01-09
|
## 0.2.6 - 2021-01-09
|
||||||
- Use `bytestring` version range compatible with Bytes v1.0. [#246]
|
* Use `bytestring` version range compatible with Bytes v1.0. [#246]
|
||||||
|
|
||||||
[#246]: https://github.com/actix/actix-net/pull/246
|
[#246]: https://github.com/actix/actix-net/pull/246
|
||||||
|
|
||||||
|
|
||||||
## 0.2.5 - 2020-09-20
|
## 0.2.5 - 2020-09-20
|
||||||
- Fix `from_hex()` method
|
* Fix `from_hex()` method
|
||||||
|
|
||||||
|
|
||||||
## 0.2.4 - 2019-12-31
|
## 0.2.4 - 2019-12-31
|
||||||
- Add `ResourceDef::resource_path_named()` path generation method
|
* Add `ResourceDef::resource_path_named()` path generation method
|
||||||
|
|
||||||
|
|
||||||
## 0.2.3 - 2019-12-25
|
## 0.2.3 - 2019-12-25
|
||||||
- Add impl `IntoPattern` for `&String`
|
* Add impl `IntoPattern` for `&String`
|
||||||
|
|
||||||
|
|
||||||
## 0.2.2 - 2019-12-25
|
## 0.2.2 - 2019-12-25
|
||||||
- Use `IntoPattern` for `RouterBuilder::path()`
|
* Use `IntoPattern` for `RouterBuilder::path()`
|
||||||
|
|
||||||
|
|
||||||
## 0.2.1 - 2019-12-25
|
## 0.2.1 - 2019-12-25
|
||||||
- Add `IntoPattern` trait
|
* Add `IntoPattern` trait
|
||||||
- Add multi-pattern resources
|
* Add multi-pattern resources
|
||||||
|
|
||||||
|
|
||||||
## 0.2.0 - 2019-12-07
|
## 0.2.0 - 2019-12-07
|
||||||
- Update http to 0.2
|
* Update http to 0.2
|
||||||
- Update regex to 1.3
|
* Update regex to 1.3
|
||||||
- Use bytestring instead of string
|
* Use bytestring instead of string
|
||||||
|
|
||||||
|
|
||||||
## 0.1.5 - 2019-05-15
|
## 0.1.5 - 2019-05-15
|
||||||
- Remove debug prints
|
* Remove debug prints
|
||||||
|
|
||||||
|
|
||||||
## 0.1.4 - 2019-05-15
|
## 0.1.4 - 2019-05-15
|
||||||
- Fix checked resource match
|
* Fix checked resource match
|
||||||
|
|
||||||
|
|
||||||
## 0.1.3 - 2019-04-22
|
## 0.1.3 - 2019-04-22
|
||||||
- Added support for `remainder match` (i.e "/path/{tail}*")
|
* Added support for `remainder match` (i.e "/path/{tail}*")
|
||||||
|
|
||||||
|
|
||||||
## 0.1.2 - 2019-04-07
|
## 0.1.2 - 2019-04-07
|
||||||
- Export `Quoter` type
|
* Export `Quoter` type
|
||||||
- Allow to reset `Path` instance
|
* Allow to reset `Path` instance
|
||||||
|
|
||||||
|
|
||||||
## 0.1.1 - 2019-04-03
|
## 0.1.1 - 2019-04-03
|
||||||
- Get dynamic segment by name instead of iterator.
|
* Get dynamic segment by name instead of iterator.
|
||||||
|
|
||||||
|
|
||||||
## 0.1.0 - 2019-03-09
|
## 0.1.0 - 2019-03-09
|
||||||
- Initial release
|
* Initial release
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "actix-router"
|
name = "actix-router"
|
||||||
version = "0.5.0-beta.3"
|
version = "0.5.0-beta.2"
|
||||||
authors = [
|
authors = [
|
||||||
"Nikolay Kim <fafhrd91@gmail.com>",
|
"Nikolay Kim <fafhrd91@gmail.com>",
|
||||||
"Ali MJ Al-Nasrawy <alimjalnasrawy@gmail.com>",
|
"Ali MJ Al-Nasrawy <alimjalnasrawy@gmail.com>",
|
||||||
@@ -21,7 +21,7 @@ default = ["http"]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bytestring = ">=0.1.5, <2"
|
bytestring = ">=0.1.5, <2"
|
||||||
firestorm = "0.5"
|
firestorm = "0.4"
|
||||||
http = { version = "0.2.3", optional = true }
|
http = { version = "0.2.3", optional = true }
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
regex = "1.5"
|
regex = "1.5"
|
||||||
@@ -29,7 +29,7 @@ serde = "1"
|
|||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
criterion = { version = "0.3", features = ["html_reports"] }
|
criterion = { version = "0.3", features = ["html_reports"] }
|
||||||
firestorm = { version = "0.5", features = ["enable_system_time"] }
|
firestorm = { version = "0.4", features = ["enable_system_time"] }
|
||||||
http = "0.2.5"
|
http = "0.2.5"
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
|
|
||||||
|
@@ -29,25 +29,26 @@ const REGEX_FLAGS: &str = "(?s-m)";
|
|||||||
///
|
///
|
||||||
///
|
///
|
||||||
/// # Pattern Format and Matching Behavior
|
/// # Pattern Format and Matching Behavior
|
||||||
|
///
|
||||||
/// Resource pattern is defined as a string of zero or more _segments_ where each segment is
|
/// Resource pattern is defined as a string of zero or more _segments_ where each segment is
|
||||||
/// preceded by a slash `/`.
|
/// preceded by a slash `/`.
|
||||||
///
|
///
|
||||||
/// This means that pattern string __must__ either be empty or begin with a slash (`/`). This also
|
/// This means that pattern string __must__ either be empty or begin with a slash (`/`).
|
||||||
/// implies that a trailing slash in pattern defines an empty segment. For example, the pattern
|
/// This also implies that a trailing slash in pattern defines an empty segment.
|
||||||
/// `"/user/"` has two segments: `["user", ""]`
|
/// For example, the pattern `"/user/"` has two segments: `["user", ""]`
|
||||||
///
|
///
|
||||||
/// A key point to understand is that `ResourceDef` matches segments, not strings. Segments are
|
/// A key point to underhand is that `ResourceDef` matches segments, not strings.
|
||||||
/// matched individually. For example, the pattern `/user/` is not considered a prefix for the path
|
/// It matches segments individually.
|
||||||
/// `/user/123/456`, because the second segment doesn't match: `["user", ""]`
|
/// For example, the pattern `/user/` is not considered a prefix for the path `/user/123/456`,
|
||||||
/// vs `["user", "123", "456"]`.
|
/// because the second segment doesn't match: `["user", ""]` vs `["user", "123", "456"]`.
|
||||||
///
|
///
|
||||||
/// This definition is consistent with the definition of absolute URL path in
|
/// This definition is consistent with the definition of absolute URL path in
|
||||||
/// [RFC 3986 §3.3](https://datatracker.ietf.org/doc/html/rfc3986#section-3.3)
|
/// [RFC 3986 (section 3.3)](https://datatracker.ietf.org/doc/html/rfc3986#section-3.3)
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
/// # Static Resources
|
/// # Static Resources
|
||||||
/// A static resource is the most basic type of definition. Pass a pattern to [new][Self::new].
|
/// A static resource is the most basic type of definition. Pass a pattern to
|
||||||
/// Conforming paths must match the pattern exactly.
|
/// [new][Self::new]. Conforming paths must match the pattern exactly.
|
||||||
///
|
///
|
||||||
/// ## Examples
|
/// ## Examples
|
||||||
/// ```
|
/// ```
|
||||||
@@ -62,6 +63,7 @@ const REGEX_FLAGS: &str = "(?s-m)";
|
|||||||
/// assert!(!resource.is_match("/search"));
|
/// assert!(!resource.is_match("/search"));
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
|
///
|
||||||
/// # Dynamic Segments
|
/// # Dynamic Segments
|
||||||
/// Also known as "path parameters". Resources can define sections of a pattern that be extracted
|
/// Also known as "path parameters". Resources can define sections of a pattern that be extracted
|
||||||
/// from a conforming path, if it conforms to (one of) the resource pattern(s).
|
/// from a conforming path, if it conforms to (one of) the resource pattern(s).
|
||||||
@@ -100,15 +102,15 @@ const REGEX_FLAGS: &str = "(?s-m)";
|
|||||||
/// assert_eq!(path.get("id").unwrap(), "123");
|
/// assert_eq!(path.get("id").unwrap(), "123");
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
|
///
|
||||||
/// # Prefix Resources
|
/// # Prefix Resources
|
||||||
/// A prefix resource is defined as pattern that can match just the start of a path, up to a
|
/// A prefix resource is defined as pattern that can match just the start of a path, up to a
|
||||||
/// segment boundary.
|
/// segment boundary.
|
||||||
///
|
///
|
||||||
/// Prefix patterns with a trailing slash may have an unexpected, though correct, behavior.
|
/// Prefix patterns with a trailing slash may have an unexpected, though correct, behavior.
|
||||||
/// They define and therefore require an empty segment in order to match. It is easier to understand
|
/// They define and therefore require an empty segment in order to match. Examples are given below.
|
||||||
/// this behavior after reading the [matching behavior section]. Examples are given below.
|
|
||||||
///
|
///
|
||||||
/// The empty pattern (`""`), as a prefix, matches any path.
|
/// Empty pattern matches any path as a prefix.
|
||||||
///
|
///
|
||||||
/// Prefix resources can contain dynamic segments.
|
/// Prefix resources can contain dynamic segments.
|
||||||
///
|
///
|
||||||
@@ -128,6 +130,7 @@ const REGEX_FLAGS: &str = "(?s-m)";
|
|||||||
/// assert!(!resource.is_match("/user/123"));
|
/// assert!(!resource.is_match("/user/123"));
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
|
///
|
||||||
/// # Custom Regex Segments
|
/// # Custom Regex Segments
|
||||||
/// Dynamic segments can be customised to only match a specific regular expression. It can be
|
/// Dynamic segments can be customised to only match a specific regular expression. It can be
|
||||||
/// helpful to do this if resource definitions would otherwise conflict and cause one to
|
/// helpful to do this if resource definitions would otherwise conflict and cause one to
|
||||||
@@ -155,6 +158,7 @@ const REGEX_FLAGS: &str = "(?s-m)";
|
|||||||
/// assert!(!resource.is_match("/user/abc"));
|
/// assert!(!resource.is_match("/user/abc"));
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
|
///
|
||||||
/// # Tail Segments
|
/// # Tail Segments
|
||||||
/// As a shortcut to defining a custom regex for matching _all_ remaining characters (not just those
|
/// As a shortcut to defining a custom regex for matching _all_ remaining characters (not just those
|
||||||
/// up until a `/` character), there is a special pattern to match (and capture) the remaining
|
/// up until a `/` character), there is a special pattern to match (and capture) the remaining
|
||||||
@@ -175,6 +179,7 @@ const REGEX_FLAGS: &str = "(?s-m)";
|
|||||||
/// assert_eq!(path.get("tail").unwrap(), "main/LICENSE");
|
/// assert_eq!(path.get("tail").unwrap(), "main/LICENSE");
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
|
///
|
||||||
/// # Multi-Pattern Resources
|
/// # Multi-Pattern Resources
|
||||||
/// For resources that can map to multiple distinct paths, it may be suitable to use
|
/// For resources that can map to multiple distinct paths, it may be suitable to use
|
||||||
/// multi-pattern resources by passing an array/vec to [`new`][Self::new]. They will be combined
|
/// multi-pattern resources by passing an array/vec to [`new`][Self::new]. They will be combined
|
||||||
@@ -193,6 +198,7 @@ const REGEX_FLAGS: &str = "(?s-m)";
|
|||||||
/// assert!(resource.is_match("/index"));
|
/// assert!(resource.is_match("/index"));
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
|
///
|
||||||
/// # Trailing Slashes
|
/// # Trailing Slashes
|
||||||
/// It should be noted that this library takes no steps to normalize intra-path or trailing slashes.
|
/// It should be noted that this library takes no steps to normalize intra-path or trailing slashes.
|
||||||
/// As such, all resource definitions implicitly expect a pre-processing step to normalize paths if
|
/// As such, all resource definitions implicitly expect a pre-processing step to normalize paths if
|
||||||
@@ -206,8 +212,6 @@ const REGEX_FLAGS: &str = "(?s-m)";
|
|||||||
/// assert!(!ResourceDef::new("/root/").is_match("/root"));
|
/// assert!(!ResourceDef::new("/root/").is_match("/root"));
|
||||||
/// assert!(!ResourceDef::prefix("/root/").is_match("/root"));
|
/// assert!(!ResourceDef::prefix("/root/").is_match("/root"));
|
||||||
/// ```
|
/// ```
|
||||||
///
|
|
||||||
/// [matching behavior section]: #pattern-format-and-matching-behavior
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct ResourceDef {
|
pub struct ResourceDef {
|
||||||
id: u16,
|
id: u16,
|
||||||
@@ -275,7 +279,7 @@ impl ResourceDef {
|
|||||||
/// ```
|
/// ```
|
||||||
pub fn new<T: IntoPatterns>(paths: T) -> Self {
|
pub fn new<T: IntoPatterns>(paths: T) -> Self {
|
||||||
profile_method!(new);
|
profile_method!(new);
|
||||||
Self::construct(paths, false)
|
Self::new2(paths, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Constructs a new resource definition using a pattern that performs prefix matching.
|
/// Constructs a new resource definition using a pattern that performs prefix matching.
|
||||||
@@ -288,7 +292,7 @@ impl ResourceDef {
|
|||||||
/// resource definition with a tail segment; use [`new`][Self::new] in this case.
|
/// resource definition with a tail segment; use [`new`][Self::new] in this case.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
/// Panics if path pattern is malformed.
|
/// Panics if path regex pattern is malformed.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
/// ```
|
/// ```
|
||||||
@@ -303,14 +307,14 @@ impl ResourceDef {
|
|||||||
/// ```
|
/// ```
|
||||||
pub fn prefix<T: IntoPatterns>(paths: T) -> Self {
|
pub fn prefix<T: IntoPatterns>(paths: T) -> Self {
|
||||||
profile_method!(prefix);
|
profile_method!(prefix);
|
||||||
ResourceDef::construct(paths, true)
|
ResourceDef::new2(paths, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Constructs a new resource definition using a string pattern that performs prefix matching,
|
/// Constructs a new resource definition using a string pattern that performs prefix matching,
|
||||||
/// ensuring a leading `/` if pattern is not empty.
|
/// inserting a `/` to beginning of the pattern if absent and pattern is not empty.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
/// Panics if path pattern is malformed.
|
/// Panics if path regex pattern is malformed.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
/// ```
|
/// ```
|
||||||
@@ -511,8 +515,8 @@ impl ResourceDef {
|
|||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
match patterns.len() {
|
match patterns.len() {
|
||||||
1 => ResourceDef::construct(&patterns[0], other.is_prefix()),
|
1 => ResourceDef::new2(&patterns[0], other.is_prefix()),
|
||||||
_ => ResourceDef::construct(patterns, other.is_prefix()),
|
_ => ResourceDef::new2(patterns, other.is_prefix()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -877,8 +881,8 @@ impl ResourceDef {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn construct<T: IntoPatterns>(paths: T, is_prefix: bool) -> Self {
|
fn new2<T: IntoPatterns>(paths: T, is_prefix: bool) -> Self {
|
||||||
profile_method!(construct);
|
profile_method!(new2);
|
||||||
|
|
||||||
let patterns = paths.patterns();
|
let patterns = paths.patterns();
|
||||||
let (pat_type, segments) = match &patterns {
|
let (pat_type, segments) = match &patterns {
|
||||||
@@ -1810,7 +1814,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic]
|
#[should_panic]
|
||||||
fn prefix_plus_tail_match_disallowed() {
|
fn prefix_plus_tail_match_is_allowed() {
|
||||||
ResourceDef::prefix("/user/{id}*");
|
ResourceDef::prefix("/user/{id}*");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -2,28 +2,22 @@ use crate::ResourcePath;
|
|||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
const GEN_DELIMS: &[u8] = b":/?#[]@";
|
const GEN_DELIMS: &[u8] = b":/?#[]@";
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
const SUB_DELIMS_WITHOUT_QS: &[u8] = b"!$'()*,";
|
const SUB_DELIMS_WITHOUT_QS: &[u8] = b"!$'()*,";
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
const SUB_DELIMS: &[u8] = b"!$'()*,+?=;";
|
const SUB_DELIMS: &[u8] = b"!$'()*,+?=;";
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
const RESERVED: &[u8] = b":/?#[]@!$'()*,+?=;";
|
const RESERVED: &[u8] = b":/?#[]@!$'()*,+?=;";
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
const UNRESERVED: &[u8] = b"abcdefghijklmnopqrstuvwxyz
|
const UNRESERVED: &[u8] = b"abcdefghijklmnopqrstuvwxyz
|
||||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ
|
ABCDEFGHIJKLMNOPQRSTUVWXYZ
|
||||||
1234567890
|
1234567890
|
||||||
-._~";
|
-._~";
|
||||||
|
|
||||||
const ALLOWED: &[u8] = b"abcdefghijklmnopqrstuvwxyz
|
const ALLOWED: &[u8] = b"abcdefghijklmnopqrstuvwxyz
|
||||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ
|
ABCDEFGHIJKLMNOPQRSTUVWXYZ
|
||||||
1234567890
|
1234567890
|
||||||
-._~
|
-._~
|
||||||
!$'()*,";
|
!$'()*,";
|
||||||
|
|
||||||
const QS: &[u8] = b"+&=;b";
|
const QS: &[u8] = b"+&=;b";
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@@ -40,20 +34,19 @@ thread_local! {
|
|||||||
static DEFAULT_QUOTER: Quoter = Quoter::new(b"@:", b"%/+");
|
static DEFAULT_QUOTER: Quoter = Quoter::new(b"@:", b"%/+");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Default, Clone, Debug)]
|
||||||
pub struct Url {
|
pub struct Url {
|
||||||
uri: http::Uri,
|
uri: http::Uri,
|
||||||
path: Option<String>,
|
path: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Url {
|
impl Url {
|
||||||
#[inline]
|
|
||||||
pub fn new(uri: http::Uri) -> Url {
|
pub fn new(uri: http::Uri) -> Url {
|
||||||
let path = DEFAULT_QUOTER.with(|q| q.requote(uri.path().as_bytes()));
|
let path = DEFAULT_QUOTER.with(|q| q.requote(uri.path().as_bytes()));
|
||||||
|
|
||||||
Url { uri, path }
|
Url { uri, path }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn with_quoter(uri: http::Uri, quoter: &Quoter) -> Url {
|
pub fn with_quoter(uri: http::Uri, quoter: &Quoter) -> Url {
|
||||||
Url {
|
Url {
|
||||||
path: quoter.requote(uri.path().as_bytes()),
|
path: quoter.requote(uri.path().as_bytes()),
|
||||||
@@ -61,16 +54,15 @@ impl Url {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn uri(&self) -> &http::Uri {
|
pub fn uri(&self) -> &http::Uri {
|
||||||
&self.uri
|
&self.uri
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn path(&self) -> &str {
|
pub fn path(&self) -> &str {
|
||||||
match self.path {
|
if let Some(ref s) = self.path {
|
||||||
Some(ref path) => path,
|
s
|
||||||
_ => self.uri.path(),
|
} else {
|
||||||
|
self.uri.path()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,7 +86,6 @@ impl ResourcePath for Url {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A quoter
|
|
||||||
pub struct Quoter {
|
pub struct Quoter {
|
||||||
safe_table: [u8; 16],
|
safe_table: [u8; 16],
|
||||||
protected_table: [u8; 16],
|
protected_table: [u8; 16],
|
||||||
@@ -102,7 +93,7 @@ pub struct Quoter {
|
|||||||
|
|
||||||
impl Quoter {
|
impl Quoter {
|
||||||
pub fn new(safe: &[u8], protected: &[u8]) -> Quoter {
|
pub fn new(safe: &[u8], protected: &[u8]) -> Quoter {
|
||||||
let mut quoter = Quoter {
|
let mut q = Quoter {
|
||||||
safe_table: [0; 16],
|
safe_table: [0; 16],
|
||||||
protected_table: [0; 16],
|
protected_table: [0; 16],
|
||||||
};
|
};
|
||||||
@@ -110,24 +101,24 @@ impl Quoter {
|
|||||||
// prepare safe table
|
// prepare safe table
|
||||||
for i in 0..128 {
|
for i in 0..128 {
|
||||||
if ALLOWED.contains(&i) {
|
if ALLOWED.contains(&i) {
|
||||||
set_bit(&mut quoter.safe_table, i);
|
set_bit(&mut q.safe_table, i);
|
||||||
}
|
}
|
||||||
if QS.contains(&i) {
|
if QS.contains(&i) {
|
||||||
set_bit(&mut quoter.safe_table, i);
|
set_bit(&mut q.safe_table, i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for ch in safe {
|
for ch in safe {
|
||||||
set_bit(&mut quoter.safe_table, *ch)
|
set_bit(&mut q.safe_table, *ch)
|
||||||
}
|
}
|
||||||
|
|
||||||
// prepare protected table
|
// prepare protected table
|
||||||
for ch in protected {
|
for ch in protected {
|
||||||
set_bit(&mut quoter.safe_table, *ch);
|
set_bit(&mut q.safe_table, *ch);
|
||||||
set_bit(&mut quoter.protected_table, *ch);
|
set_bit(&mut q.protected_table, *ch);
|
||||||
}
|
}
|
||||||
|
|
||||||
quoter
|
q
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn requote(&self, val: &[u8]) -> Option<String> {
|
pub fn requote(&self, val: &[u8]) -> Option<String> {
|
||||||
@@ -224,7 +215,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_url() {
|
fn test_parse_url() {
|
||||||
let re = "/user/{id}/test";
|
let re = "/user/{id}/test";
|
||||||
|
|
||||||
let path = match_url(re, "/user/2345/test");
|
let path = match_url(re, "/user/2345/test");
|
||||||
@@ -240,24 +231,24 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn protected_chars() {
|
fn test_protected_chars() {
|
||||||
let encoded = percent_encode(PROTECTED);
|
let encoded = percent_encode(PROTECTED);
|
||||||
let path = match_url("/user/{id}/test", format!("/user/{}/test", encoded));
|
let path = match_url("/user/{id}/test", format!("/user/{}/test", encoded));
|
||||||
assert_eq!(path.get("id").unwrap(), &encoded);
|
assert_eq!(path.get("id").unwrap(), &encoded);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn non_protected_ascii() {
|
fn test_non_protecteed_ascii() {
|
||||||
let non_protected_ascii = ('\u{0}'..='\u{7F}')
|
let nonprotected_ascii = ('\u{0}'..='\u{7F}')
|
||||||
.filter(|&c| c.is_ascii() && !PROTECTED.contains(&(c as u8)))
|
.filter(|&c| c.is_ascii() && !PROTECTED.contains(&(c as u8)))
|
||||||
.collect::<String>();
|
.collect::<String>();
|
||||||
let encoded = percent_encode(non_protected_ascii.as_bytes());
|
let encoded = percent_encode(nonprotected_ascii.as_bytes());
|
||||||
let path = match_url("/user/{id}/test", format!("/user/{}/test", encoded));
|
let path = match_url("/user/{id}/test", format!("/user/{}/test", encoded));
|
||||||
assert_eq!(path.get("id").unwrap(), &non_protected_ascii);
|
assert_eq!(path.get("id").unwrap(), &nonprotected_ascii);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn valid_utf8_multibyte() {
|
fn test_valid_utf8_multibyte() {
|
||||||
let test = ('\u{FF00}'..='\u{FFFF}').collect::<String>();
|
let test = ('\u{FF00}'..='\u{FFFF}').collect::<String>();
|
||||||
let encoded = percent_encode(test.as_bytes());
|
let encoded = percent_encode(test.as_bytes());
|
||||||
let path = match_url("/a/{id}/b", format!("/a/{}/b", &encoded));
|
let path = match_url("/a/{id}/b", format!("/a/{}/b", &encoded));
|
||||||
@@ -265,7 +256,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn invalid_utf8() {
|
fn test_invalid_utf8() {
|
||||||
let invalid_utf8 = percent_encode((0x80..=0xff).collect::<Vec<_>>().as_slice());
|
let invalid_utf8 = percent_encode((0x80..=0xff).collect::<Vec<_>>().as_slice());
|
||||||
let uri = Uri::try_from(format!("/{}", invalid_utf8)).unwrap();
|
let uri = Uri::try_from(format!("/{}", invalid_utf8)).unwrap();
|
||||||
let path = Path::new(Url::new(uri));
|
let path = Path::new(Url::new(uri));
|
||||||
@@ -275,7 +266,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn hex_encoding() {
|
fn test_from_hex() {
|
||||||
let hex = b"0123456789abcdefABCDEF";
|
let hex = b"0123456789abcdefABCDEF";
|
||||||
|
|
||||||
for i in 0..256 {
|
for i in 0..256 {
|
||||||
|
@@ -3,51 +3,40 @@
|
|||||||
## Unreleased - 2021-xx-xx
|
## Unreleased - 2021-xx-xx
|
||||||
|
|
||||||
|
|
||||||
## 0.1.0-beta.10 - 2021-12-27
|
|
||||||
* No significant changes since `0.1.0-beta.9`.
|
|
||||||
|
|
||||||
|
|
||||||
## 0.1.0-beta.9 - 2021-12-17
|
|
||||||
- Re-export `actix_http::body::to_bytes`. [#2518]
|
|
||||||
- Update `actix_web::test` re-exports. [#2518]
|
|
||||||
|
|
||||||
[#2518]: https://github.com/actix/actix-web/pull/2518
|
|
||||||
|
|
||||||
|
|
||||||
## 0.1.0-beta.8 - 2021-12-11
|
## 0.1.0-beta.8 - 2021-12-11
|
||||||
- No significant changes since `0.1.0-beta.7`.
|
* No significant changes since `0.1.0-beta.7`.
|
||||||
|
|
||||||
|
|
||||||
## 0.1.0-beta.7 - 2021-11-22
|
## 0.1.0-beta.7 - 2021-11-22
|
||||||
- Fix compatibility with experimental `io-uring` feature of `actix-rt`. [#2408]
|
* Fix compatibility with experimental `io-uring` feature of `actix-rt`. [#2408]
|
||||||
|
|
||||||
[#2408]: https://github.com/actix/actix-web/pull/2408
|
[#2408]: https://github.com/actix/actix-web/pull/2408
|
||||||
|
|
||||||
|
|
||||||
## 0.1.0-beta.6 - 2021-11-15
|
## 0.1.0-beta.6 - 2021-11-15
|
||||||
- No significant changes from `0.1.0-beta.5`.
|
* No significant changes from `0.1.0-beta.5`.
|
||||||
|
|
||||||
|
|
||||||
## 0.1.0-beta.5 - 2021-10-20
|
## 0.1.0-beta.5 - 2021-10-20
|
||||||
- Updated rustls to v0.20. [#2414]
|
* Updated rustls to v0.20. [#2414]
|
||||||
- Minimum supported Rust version (MSRV) is now 1.52.
|
* Minimum supported Rust version (MSRV) is now 1.52.
|
||||||
|
|
||||||
[#2414]: https://github.com/actix/actix-web/pull/2414
|
[#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.
|
||||||
|
|
||||||
|
|
||||||
## 0.1.0-beta.3 - 2021-06-20
|
## 0.1.0-beta.3 - 2021-06-20
|
||||||
- No significant changes from `0.1.0-beta.2`.
|
* No significant changes from `0.1.0-beta.2`.
|
||||||
|
|
||||||
|
|
||||||
## 0.1.0-beta.2 - 2021-04-17
|
## 0.1.0-beta.2 - 2021-04-17
|
||||||
- No significant changes from `0.1.0-beta.1`.
|
* No significant changes from `0.1.0-beta.1`.
|
||||||
|
|
||||||
|
|
||||||
## 0.1.0-beta.1 - 2021-04-02
|
## 0.1.0-beta.1 - 2021-04-02
|
||||||
- Move integration testing structs from `actix-web`. [#2112]
|
* Move integration testing structs from `actix-web`. [#2112]
|
||||||
|
|
||||||
[#2112]: https://github.com/actix/actix-web/pull/2112
|
[#2112]: https://github.com/actix/actix-web/pull/2112
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "actix-test"
|
name = "actix-test"
|
||||||
version = "0.1.0-beta.10"
|
version = "0.1.0-beta.8"
|
||||||
authors = [
|
authors = [
|
||||||
"Nikolay Kim <fafhrd91@gmail.com>",
|
"Nikolay Kim <fafhrd91@gmail.com>",
|
||||||
"Rob Ede <robjtede@icloud.com>",
|
"Rob Ede <robjtede@icloud.com>",
|
||||||
@@ -29,13 +29,13 @@ openssl = ["tls-openssl", "actix-http/openssl", "awc/openssl"]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix-codec = "0.4.1"
|
actix-codec = "0.4.1"
|
||||||
actix-http = "3.0.0-beta.17"
|
actix-http = "3.0.0-beta.15"
|
||||||
actix-http-test = "3.0.0-beta.10"
|
actix-http-test = "3.0.0-beta.9"
|
||||||
actix-rt = "2.1"
|
actix-rt = "2.1"
|
||||||
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.16", default-features = false, features = ["cookies"] }
|
actix-web = { version = "4.0.0-beta.14", default-features = false, features = ["cookies"] }
|
||||||
awc = { version = "3.0.0-beta.15", default-features = false, features = ["cookies"] }
|
awc = { version = "3.0.0-beta.13", 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 = [] }
|
||||||
@@ -45,4 +45,4 @@ 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.20.0", optional = true }
|
tls-rustls = { package = "rustls", version = "0.20.0", optional = true }
|
||||||
tokio = { version = "1.8", features = ["sync"] }
|
tokio = { version = "1.2", features = ["sync"] }
|
||||||
|
@@ -37,14 +37,9 @@ extern crate tls_rustls as rustls;
|
|||||||
use std::{fmt, net, thread, time::Duration};
|
use std::{fmt, net, thread, time::Duration};
|
||||||
|
|
||||||
use actix_codec::{AsyncRead, AsyncWrite, Framed};
|
use actix_codec::{AsyncRead, AsyncWrite, Framed};
|
||||||
pub use actix_http::{body::to_bytes, test::TestBuffer};
|
pub use actix_http::test::TestBuffer;
|
||||||
use actix_http::{header::HeaderMap, ws, HttpService, Method, Request, Response};
|
use actix_http::{header::HeaderMap, ws, HttpService, Method, Request, Response};
|
||||||
pub use actix_http_test::unused_addr;
|
|
||||||
use actix_service::{map_config, IntoServiceFactory, ServiceFactory, ServiceFactoryExt as _};
|
use actix_service::{map_config, IntoServiceFactory, ServiceFactory, ServiceFactoryExt as _};
|
||||||
pub use actix_web::test::{
|
|
||||||
call_and_read_body, call_and_read_body_json, call_service, init_service, ok_service,
|
|
||||||
read_body, read_body_json, simple_service, TestRequest,
|
|
||||||
};
|
|
||||||
use actix_web::{
|
use actix_web::{
|
||||||
body::MessageBody,
|
body::MessageBody,
|
||||||
dev::{AppConfig, Server, ServerHandle, Service},
|
dev::{AppConfig, Server, ServerHandle, Service},
|
||||||
@@ -53,6 +48,12 @@ use actix_web::{
|
|||||||
};
|
};
|
||||||
use awc::{error::PayloadError, Client, ClientRequest, ClientResponse, Connector};
|
use awc::{error::PayloadError, Client, ClientRequest, ClientResponse, Connector};
|
||||||
use futures_core::Stream;
|
use futures_core::Stream;
|
||||||
|
|
||||||
|
pub use actix_http_test::unused_addr;
|
||||||
|
pub use actix_web::test::{
|
||||||
|
call_service, default_service, init_service, load_stream, ok_service, read_body,
|
||||||
|
read_body_json, read_response, read_response_json, TestRequest,
|
||||||
|
};
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
|
|
||||||
/// Start default [`TestServer`].
|
/// Start default [`TestServer`].
|
||||||
@@ -340,7 +341,7 @@ where
|
|||||||
Connector::new()
|
Connector::new()
|
||||||
.conn_lifetime(Duration::from_secs(0))
|
.conn_lifetime(Duration::from_secs(0))
|
||||||
.timeout(Duration::from_millis(30000))
|
.timeout(Duration::from_millis(30000))
|
||||||
.openssl(builder.build())
|
.ssl(builder.build())
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "openssl"))]
|
#[cfg(not(feature = "openssl"))]
|
||||||
{
|
{
|
||||||
|
@@ -3,110 +3,106 @@
|
|||||||
## Unreleased - 2021-xx-xx
|
## Unreleased - 2021-xx-xx
|
||||||
|
|
||||||
|
|
||||||
## 4.0.0-beta.9 - 2021-12-27
|
|
||||||
* No significant changes since `4.0.0-beta.8`.
|
|
||||||
|
|
||||||
|
|
||||||
## 4.0.0-beta.8 - 2021-12-11
|
## 4.0.0-beta.8 - 2021-12-11
|
||||||
- Add `ws:WsResponseBuilder` for building WebSocket session response. [#1920]
|
* Add `ws:WsResponseBuilder` for building WebSocket session response. [#1920]
|
||||||
- Deprecate `ws::{start_with_addr, start_with_protocols}`. [#1920]
|
* Deprecate `ws::{start_with_addr, start_with_protocols}`. [#1920]
|
||||||
- Minimum supported Rust version (MSRV) is now 1.52.
|
* Minimum supported Rust version (MSRV) is now 1.52.
|
||||||
|
|
||||||
[#1920]: https://github.com/actix/actix-web/pull/1920
|
[#1920]: https://github.com/actix/actix-web/pull/1920
|
||||||
|
|
||||||
|
|
||||||
## 4.0.0-beta.7 - 2021-09-09
|
## 4.0.0-beta.7 - 2021-09-09
|
||||||
- Minimum supported Rust version (MSRV) is now 1.51.
|
* Minimum supported Rust version (MSRV) is now 1.51.
|
||||||
|
|
||||||
|
|
||||||
## 4.0.0-beta.6 - 2021-06-26
|
## 4.0.0-beta.6 - 2021-06-26
|
||||||
- Update `actix` to `0.12`. [#2277]
|
* Update `actix` to `0.12`. [#2277]
|
||||||
|
|
||||||
[#2277]: https://github.com/actix/actix-web/pull/2277
|
[#2277]: https://github.com/actix/actix-web/pull/2277
|
||||||
|
|
||||||
|
|
||||||
## 4.0.0-beta.5 - 2021-06-17
|
## 4.0.0-beta.5 - 2021-06-17
|
||||||
- No notable changes.
|
* No notable changes.
|
||||||
|
|
||||||
|
|
||||||
## 4.0.0-beta.4 - 2021-04-02
|
## 4.0.0-beta.4 - 2021-04-02
|
||||||
- No notable changes.
|
* No notable changes.
|
||||||
|
|
||||||
|
|
||||||
## 4.0.0-beta.3 - 2021-03-09
|
## 4.0.0-beta.3 - 2021-03-09
|
||||||
- No notable changes.
|
* No notable changes.
|
||||||
|
|
||||||
|
|
||||||
## 4.0.0-beta.2 - 2021-02-10
|
## 4.0.0-beta.2 - 2021-02-10
|
||||||
- No notable changes.
|
* No notable changes.
|
||||||
|
|
||||||
|
|
||||||
## 4.0.0-beta.1 - 2021-01-07
|
## 4.0.0-beta.1 - 2021-01-07
|
||||||
- Update `pin-project` to `1.0`.
|
* Update `pin-project` to `1.0`.
|
||||||
- Update `bytes` to `1.0`. [#1813]
|
* Update `bytes` to `1.0`. [#1813]
|
||||||
- `WebsocketContext::text` now takes an `Into<bytestring::ByteString>`. [#1864]
|
* `WebsocketContext::text` now takes an `Into<bytestring::ByteString>`. [#1864]
|
||||||
|
|
||||||
[#1813]: https://github.com/actix/actix-web/pull/1813
|
[#1813]: https://github.com/actix/actix-web/pull/1813
|
||||||
[#1864]: https://github.com/actix/actix-web/pull/1864
|
[#1864]: https://github.com/actix/actix-web/pull/1864
|
||||||
|
|
||||||
|
|
||||||
## 3.0.0 - 2020-09-11
|
## 3.0.0 - 2020-09-11
|
||||||
- No significant changes from `3.0.0-beta.2`.
|
* No significant changes from `3.0.0-beta.2`.
|
||||||
|
|
||||||
|
|
||||||
## 3.0.0-beta.2 - 2020-09-10
|
## 3.0.0-beta.2 - 2020-09-10
|
||||||
- Update `actix-*` dependencies to latest versions.
|
* Update `actix-*` dependencies to latest versions.
|
||||||
|
|
||||||
|
|
||||||
## [3.0.0-beta.1] - 2020-xx-xx
|
## [3.0.0-beta.1] - 2020-xx-xx
|
||||||
- Update `actix-web` & `actix-http` dependencies to beta.1
|
* Update `actix-web` & `actix-http` dependencies to beta.1
|
||||||
- Bump minimum supported Rust version to 1.40
|
* Bump minimum supported Rust version to 1.40
|
||||||
|
|
||||||
|
|
||||||
## [3.0.0-alpha.1] - 2020-05-08
|
## [3.0.0-alpha.1] - 2020-05-08
|
||||||
- Update the actix-web dependency to 3.0.0-alpha.1
|
* Update the actix-web dependency to 3.0.0-alpha.1
|
||||||
- Update the actix dependency to 0.10.0-alpha.2
|
* Update the actix dependency to 0.10.0-alpha.2
|
||||||
- Update the actix-http dependency to 2.0.0-alpha.3
|
* Update the actix-http dependency to 2.0.0-alpha.3
|
||||||
|
|
||||||
## [2.0.0] - 2019-12-20
|
## [2.0.0] - 2019-12-20
|
||||||
|
|
||||||
- Release
|
* Release
|
||||||
|
|
||||||
## [2.0.0-alpha.1] - 2019-12-15
|
## [2.0.0-alpha.1] - 2019-12-15
|
||||||
|
|
||||||
- Migrate to actix-web 2.0.0
|
* Migrate to actix-web 2.0.0
|
||||||
|
|
||||||
## [1.0.4] - 2019-12-07
|
## [1.0.4] - 2019-12-07
|
||||||
|
|
||||||
- Allow comma-separated websocket subprotocols without spaces (#1172)
|
* Allow comma-separated websocket subprotocols without spaces (#1172)
|
||||||
|
|
||||||
## [1.0.3] - 2019-11-14
|
## [1.0.3] - 2019-11-14
|
||||||
|
|
||||||
- Update actix-web and actix-http dependencies
|
* Update actix-web and actix-http dependencies
|
||||||
|
|
||||||
## [1.0.2] - 2019-07-20
|
## [1.0.2] - 2019-07-20
|
||||||
|
|
||||||
- Add `ws::start_with_addr()`, returning the address of the created actor, along
|
* Add `ws::start_with_addr()`, returning the address of the created actor, along
|
||||||
with the `HttpResponse`.
|
with the `HttpResponse`.
|
||||||
|
|
||||||
- Add support for specifying protocols on websocket handshake #835
|
* Add support for specifying protocols on websocket handshake #835
|
||||||
|
|
||||||
## [1.0.1] - 2019-06-28
|
## [1.0.1] - 2019-06-28
|
||||||
|
|
||||||
- Allow to use custom ws codec with `WebsocketContext` #925
|
* Allow to use custom ws codec with `WebsocketContext` #925
|
||||||
|
|
||||||
## [1.0.0] - 2019-05-29
|
## [1.0.0] - 2019-05-29
|
||||||
|
|
||||||
- Update actix-http and actix-web
|
* Update actix-http and actix-web
|
||||||
|
|
||||||
## [0.1.0-alpha.3] - 2019-04-02
|
## [0.1.0-alpha.3] - 2019-04-02
|
||||||
|
|
||||||
- Update actix-http and actix-web
|
* Update actix-http and actix-web
|
||||||
|
|
||||||
## [0.1.0-alpha.2] - 2019-03-29
|
## [0.1.0-alpha.2] - 2019-03-29
|
||||||
|
|
||||||
- Update actix-http and actix-web
|
* Update actix-http and actix-web
|
||||||
|
|
||||||
## [0.1.0-alpha.1] - 2019-03-28
|
## [0.1.0-alpha.1] - 2019-03-28
|
||||||
|
|
||||||
- Initial impl
|
* Initial impl
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "actix-web-actors"
|
name = "actix-web-actors"
|
||||||
version = "4.0.0-beta.9"
|
version = "4.0.0-beta.8"
|
||||||
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
||||||
description = "Actix actors support for Actix Web"
|
description = "Actix actors support for Actix Web"
|
||||||
keywords = ["actix", "http", "web", "framework", "async"]
|
keywords = ["actix", "http", "web", "framework", "async"]
|
||||||
@@ -16,19 +16,19 @@ 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.1"
|
actix-codec = "0.4.1"
|
||||||
actix-http = "3.0.0-beta.17"
|
actix-http = "3.0.0-beta.15"
|
||||||
actix-web = { version = "4.0.0-beta.16", default-features = false }
|
actix-web = { version = "4.0.0-beta.14", default-features = false }
|
||||||
|
|
||||||
bytes = "1"
|
bytes = "1"
|
||||||
bytestring = "1"
|
bytestring = "1"
|
||||||
futures-core = { version = "0.3.7", default-features = false }
|
futures-core = { version = "0.3.7", default-features = false }
|
||||||
pin-project-lite = "0.2"
|
pin-project-lite = "0.2"
|
||||||
tokio = { version = "1.8", features = ["sync"] }
|
tokio = { version = "1", features = ["sync"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
actix-rt = "2.2"
|
actix-rt = "2.2"
|
||||||
actix-test = "0.1.0-beta.10"
|
actix-test = "0.1.0-beta.8"
|
||||||
awc = { version = "3.0.0-beta.15", default-features = false }
|
awc = { version = "3.0.0-beta.13", default-features = false }
|
||||||
|
|
||||||
env_logger = "0.9"
|
env_logger = "0.9"
|
||||||
futures-util = { version = "0.3.7", default-features = false }
|
futures-util = { version = "0.3.7", default-features = false }
|
||||||
|
@@ -3,11 +3,11 @@
|
|||||||
> Actix actors support for Actix Web.
|
> Actix actors support for Actix Web.
|
||||||
|
|
||||||
[](https://crates.io/crates/actix-web-actors)
|
[](https://crates.io/crates/actix-web-actors)
|
||||||
[](https://docs.rs/actix-web-actors/4.0.0-beta.9)
|
[](https://docs.rs/actix-web-actors/4.0.0-beta.8)
|
||||||
[](https://blog.rust-lang.org/2021/05/06/Rust-1.52.0.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.9)
|
[](https://deps.rs/crate/actix-web-actors/4.0.0-beta.8)
|
||||||
[](https://crates.io/crates/actix-web-actors)
|
[](https://crates.io/crates/actix-web-actors)
|
||||||
[](https://discord.gg/NWpN5mmg3x)
|
[](https://discord.gg/NWpN5mmg3x)
|
||||||
|
|
||||||
|
@@ -4,101 +4,101 @@
|
|||||||
|
|
||||||
|
|
||||||
## 0.5.0-beta.6 - 2021-12-11
|
## 0.5.0-beta.6 - 2021-12-11
|
||||||
- No significant changes since `0.5.0-beta.5`.
|
* No significant changes since `0.5.0-beta.5`.
|
||||||
|
|
||||||
|
|
||||||
## 0.5.0-beta.5 - 2021-10-20
|
## 0.5.0-beta.5 - 2021-10-20
|
||||||
- Improve error recovery potential when macro input is invalid. [#2410]
|
* Improve error recovery potential when macro input is invalid. [#2410]
|
||||||
- Add `#[actix_web::test]` macro for setting up tests with a runtime. [#2409]
|
* Add `#[actix_web::test]` macro for setting up tests with a runtime. [#2409]
|
||||||
- Minimum supported Rust version (MSRV) is now 1.52.
|
* Minimum supported Rust version (MSRV) is now 1.52.
|
||||||
|
|
||||||
[#2410]: https://github.com/actix/actix-web/pull/2410
|
[#2410]: https://github.com/actix/actix-web/pull/2410
|
||||||
[#2409]: https://github.com/actix/actix-web/pull/2409
|
[#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.
|
||||||
|
|
||||||
[#2350]: https://github.com/actix/actix-web/pull/2350
|
[#2350]: https://github.com/actix/actix-web/pull/2350
|
||||||
|
|
||||||
|
|
||||||
## 0.5.0-beta.3 - 2021-06-17
|
## 0.5.0-beta.3 - 2021-06-17
|
||||||
- No notable changes.
|
* No notable changes.
|
||||||
|
|
||||||
|
|
||||||
## 0.5.0-beta.2 - 2021-03-09
|
## 0.5.0-beta.2 - 2021-03-09
|
||||||
- Preserve doc comments when using route macros. [#2022]
|
* Preserve doc comments when using route macros. [#2022]
|
||||||
- Add `name` attribute to `route` macro. [#1934]
|
* Add `name` attribute to `route` macro. [#1934]
|
||||||
|
|
||||||
[#2022]: https://github.com/actix/actix-web/pull/2022
|
[#2022]: https://github.com/actix/actix-web/pull/2022
|
||||||
[#1934]: https://github.com/actix/actix-web/pull/1934
|
[#1934]: https://github.com/actix/actix-web/pull/1934
|
||||||
|
|
||||||
|
|
||||||
## 0.5.0-beta.1 - 2021-02-10
|
## 0.5.0-beta.1 - 2021-02-10
|
||||||
- Use new call signature for `System::new`.
|
* Use new call signature for `System::new`.
|
||||||
|
|
||||||
|
|
||||||
## 0.4.0 - 2020-09-20
|
## 0.4.0 - 2020-09-20
|
||||||
- Added compile success and failure testing. [#1677]
|
* Added compile success and failure testing. [#1677]
|
||||||
- Add `route` macro for supporting multiple HTTP methods guards. [#1674]
|
* Add `route` macro for supporting multiple HTTP methods guards. [#1674]
|
||||||
|
|
||||||
[#1677]: https://github.com/actix/actix-web/pull/1677
|
[#1677]: https://github.com/actix/actix-web/pull/1677
|
||||||
[#1674]: https://github.com/actix/actix-web/pull/1674
|
[#1674]: https://github.com/actix/actix-web/pull/1674
|
||||||
|
|
||||||
|
|
||||||
## 0.3.0 - 2020-09-11
|
## 0.3.0 - 2020-09-11
|
||||||
- No significant changes from `0.3.0-beta.1`.
|
* No significant changes from `0.3.0-beta.1`.
|
||||||
|
|
||||||
|
|
||||||
## 0.3.0-beta.1 - 2020-07-14
|
## 0.3.0-beta.1 - 2020-07-14
|
||||||
- Add main entry-point macro that uses re-exported runtime. [#1559]
|
* Add main entry-point macro that uses re-exported runtime. [#1559]
|
||||||
|
|
||||||
[#1559]: https://github.com/actix/actix-web/pull/1559
|
[#1559]: https://github.com/actix/actix-web/pull/1559
|
||||||
|
|
||||||
|
|
||||||
## 0.2.2 - 2020-05-23
|
## 0.2.2 - 2020-05-23
|
||||||
- Add resource middleware on actix-web-codegen [#1467]
|
* Add resource middleware on actix-web-codegen [#1467]
|
||||||
|
|
||||||
[#1467]: https://github.com/actix/actix-web/pull/1467
|
[#1467]: https://github.com/actix/actix-web/pull/1467
|
||||||
|
|
||||||
|
|
||||||
## 0.2.1 - 2020-02-25
|
## 0.2.1 - 2020-02-25
|
||||||
- Add `#[allow(missing_docs)]` attribute to generated structs [#1368]
|
* Add `#[allow(missing_docs)]` attribute to generated structs [#1368]
|
||||||
- Allow the handler function to be named as `config` [#1290]
|
* Allow the handler function to be named as `config` [#1290]
|
||||||
|
|
||||||
[#1368]: https://github.com/actix/actix-web/issues/1368
|
[#1368]: https://github.com/actix/actix-web/issues/1368
|
||||||
[#1290]: https://github.com/actix/actix-web/issues/1290
|
[#1290]: https://github.com/actix/actix-web/issues/1290
|
||||||
|
|
||||||
|
|
||||||
## 0.2.0 - 2019-12-13
|
## 0.2.0 - 2019-12-13
|
||||||
- Generate code for actix-web 2.0
|
* Generate code for actix-web 2.0
|
||||||
|
|
||||||
|
|
||||||
## 0.1.3 - 2019-10-14
|
## 0.1.3 - 2019-10-14
|
||||||
- Bump up `syn` & `quote` to 1.0
|
* Bump up `syn` & `quote` to 1.0
|
||||||
- Provide better error message
|
* Provide better error message
|
||||||
|
|
||||||
|
|
||||||
## 0.1.2 - 2019-06-04
|
## 0.1.2 - 2019-06-04
|
||||||
- Add macros for head, options, trace, connect and patch http methods
|
* Add macros for head, options, trace, connect and patch http methods
|
||||||
|
|
||||||
|
|
||||||
## 0.1.1 - 2019-06-01
|
## 0.1.1 - 2019-06-01
|
||||||
- Add syn "extra-traits" feature
|
* Add syn "extra-traits" feature
|
||||||
|
|
||||||
|
|
||||||
## 0.1.0 - 2019-05-18
|
## 0.1.0 - 2019-05-18
|
||||||
- Release
|
* Release
|
||||||
|
|
||||||
|
|
||||||
## 0.1.0-beta.1 - 2019-04-20
|
## 0.1.0-beta.1 - 2019-04-20
|
||||||
- Gen code for actix-web 1.0.0-beta.1
|
* Gen code for actix-web 1.0.0-beta.1
|
||||||
|
|
||||||
|
|
||||||
## 0.1.0-alpha.6 - 2019-04-14
|
## 0.1.0-alpha.6 - 2019-04-14
|
||||||
- Gen code for actix-web 1.0.0-alpha.6
|
* Gen code for actix-web 1.0.0-alpha.6
|
||||||
|
|
||||||
|
|
||||||
## 0.1.0-alpha.1 - 2019-03-28
|
## 0.1.0-alpha.1 - 2019-03-28
|
||||||
- Initial impl
|
* Initial impl
|
||||||
|
@@ -18,14 +18,14 @@ proc-macro = true
|
|||||||
quote = "1"
|
quote = "1"
|
||||||
syn = { version = "1", features = ["full", "parsing"] }
|
syn = { version = "1", features = ["full", "parsing"] }
|
||||||
proc-macro2 = "1"
|
proc-macro2 = "1"
|
||||||
actix-router = "0.5.0-beta.3"
|
actix-router = "0.5.0-beta.2"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
actix-macros = "0.2.3"
|
actix-macros = "0.2.3"
|
||||||
actix-rt = "2.2"
|
actix-rt = "2.2"
|
||||||
actix-test = "0.1.0-beta.10"
|
actix-test = "0.1.0-beta.8"
|
||||||
actix-utils = "3.0.0"
|
actix-utils = "3.0.0"
|
||||||
actix-web = "4.0.0-beta.16"
|
actix-web = "4.0.0-beta.14"
|
||||||
|
|
||||||
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"
|
||||||
|
182
awc/CHANGES.md
182
awc/CHANGES.md
@@ -1,84 +1,68 @@
|
|||||||
# Changes
|
# Changes
|
||||||
|
|
||||||
## Unreleased - 2021-xx-xx
|
## Unreleased - 2021-xx-xx
|
||||||
|
* Add `ClientBuilder::add_default_header` and deprecate `ClientBuilder::header`. [#2510]
|
||||||
|
|
||||||
## 3.0.0-beta.15 - 2021-12-27
|
|
||||||
- Rename `Connector::{ssl => openssl}`. [#2503]
|
|
||||||
- Improve `Client` instantiation efficiency when using `openssl` by only building connectors once. [#2503]
|
|
||||||
- `ClientRequest::send_body` now takes an `impl MessageBody`. [#2546]
|
|
||||||
- Rename `MessageBody => ResponseBody` to avoid conflicts with `MessageBody` trait. [#2546]
|
|
||||||
- `impl Future` for `ResponseBody` no longer requires the body type be `Unpin`. [#2546]
|
|
||||||
- `impl Future` for `JsonBody` no longer requires the body type be `Unpin`. [#2546]
|
|
||||||
- `impl Stream` for `ClientResponse` no longer requires the body type be `Unpin`. [#2546]
|
|
||||||
|
|
||||||
[#2503]: https://github.com/actix/actix-web/pull/2503
|
|
||||||
[#2546]: https://github.com/actix/actix-web/pull/2546
|
|
||||||
|
|
||||||
|
|
||||||
## 3.0.0-beta.14 - 2021-12-17
|
|
||||||
- Add `ClientBuilder::add_default_header` and deprecate `ClientBuilder::header`. [#2510]
|
|
||||||
|
|
||||||
[#2510]: https://github.com/actix/actix-web/pull/2510
|
[#2510]: https://github.com/actix/actix-web/pull/2510
|
||||||
|
|
||||||
|
|
||||||
## 3.0.0-beta.13 - 2021-12-11
|
## 3.0.0-beta.13 - 2021-12-11
|
||||||
- No significant changes since `3.0.0-beta.12`.
|
* No significant changes since `3.0.0-beta.12`.
|
||||||
|
|
||||||
|
|
||||||
## 3.0.0-beta.12 - 2021-11-30
|
## 3.0.0-beta.12 - 2021-11-30
|
||||||
- Update `actix-tls` to `3.0.0-rc.1`. [#2474]
|
* Update `actix-tls` to `3.0.0-rc.1`. [#2474]
|
||||||
|
|
||||||
[#2474]: https://github.com/actix/actix-web/pull/2474
|
[#2474]: https://github.com/actix/actix-web/pull/2474
|
||||||
|
|
||||||
|
|
||||||
## 3.0.0-beta.11 - 2021-11-22
|
## 3.0.0-beta.11 - 2021-11-22
|
||||||
- No significant changes from `3.0.0-beta.10`.
|
* No significant changes from `3.0.0-beta.10`.
|
||||||
|
|
||||||
|
|
||||||
## 3.0.0-beta.10 - 2021-11-15
|
## 3.0.0-beta.10 - 2021-11-15
|
||||||
- No significant changes from `3.0.0-beta.9`.
|
* No significant changes from `3.0.0-beta.9`.
|
||||||
|
|
||||||
|
|
||||||
## 3.0.0-beta.9 - 2021-10-20
|
## 3.0.0-beta.9 - 2021-10-20
|
||||||
- Updated rustls to v0.20. [#2414]
|
* Updated rustls to v0.20. [#2414]
|
||||||
|
|
||||||
[#2414]: https://github.com/actix/actix-web/pull/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]
|
||||||
|
|
||||||
[#2310]: https://github.com/actix/actix-web/pull/2310
|
[#2310]: https://github.com/actix/actix-web/pull/2310
|
||||||
|
|
||||||
|
|
||||||
## 3.0.0-beta.7 - 2021-06-26
|
## 3.0.0-beta.7 - 2021-06-26
|
||||||
### Changed
|
### Changed
|
||||||
- Change compression algorithm features flags. [#2250]
|
* Change compression algorithm features flags. [#2250]
|
||||||
|
|
||||||
[#2250]: https://github.com/actix/actix-web/pull/2250
|
[#2250]: https://github.com/actix/actix-web/pull/2250
|
||||||
|
|
||||||
|
|
||||||
## 3.0.0-beta.6 - 2021-06-17
|
## 3.0.0-beta.6 - 2021-06-17
|
||||||
- No significant changes since 3.0.0-beta.5.
|
* No significant changes since 3.0.0-beta.5.
|
||||||
|
|
||||||
|
|
||||||
## 3.0.0-beta.5 - 2021-04-17
|
## 3.0.0-beta.5 - 2021-04-17
|
||||||
### Removed
|
### Removed
|
||||||
- Deprecated methods on `ClientRequest`: `if_true`, `if_some`. [#2148]
|
* Deprecated methods on `ClientRequest`: `if_true`, `if_some`. [#2148]
|
||||||
|
|
||||||
[#2148]: https://github.com/actix/actix-web/pull/2148
|
[#2148]: https://github.com/actix/actix-web/pull/2148
|
||||||
|
|
||||||
|
|
||||||
## 3.0.0-beta.4 - 2021-04-02
|
## 3.0.0-beta.4 - 2021-04-02
|
||||||
### Added
|
### Added
|
||||||
- Add `Client::headers` to get default mut reference of `HeaderMap` of client object. [#2114]
|
* Add `Client::headers` to get default mut reference of `HeaderMap` of client object. [#2114]
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- `ConnectorService` type is renamed to `BoxConnectorService`. [#2081]
|
* `ConnectorService` type is renamed to `BoxConnectorService`. [#2081]
|
||||||
- Fix http/https encoding when enabling `compress` feature. [#2116]
|
* Fix http/https encoding when enabling `compress` feature. [#2116]
|
||||||
- Rename `TestResponse::header` to `append_header`, `set` to `insert_header`. `TestResponse` header
|
* Rename `TestResponse::header` to `append_header`, `set` to `insert_header`. `TestResponse` header
|
||||||
methods now take `TryIntoHeaderPair` tuples. [#2094]
|
methods now take `TryIntoHeaderPair` tuples. [#2094]
|
||||||
|
|
||||||
[#2081]: https://github.com/actix/actix-web/pull/2081
|
[#2081]: https://github.com/actix/actix-web/pull/2081
|
||||||
@@ -89,16 +73,16 @@
|
|||||||
|
|
||||||
## 3.0.0-beta.3 - 2021-03-08
|
## 3.0.0-beta.3 - 2021-03-08
|
||||||
### Added
|
### Added
|
||||||
- `ClientResponse::timeout` for set the timeout of collecting response body. [#1931]
|
* `ClientResponse::timeout` for set the timeout of collecting response body. [#1931]
|
||||||
- `ClientBuilder::local_address` for bind to a local ip address for this client. [#2024]
|
* `ClientBuilder::local_address` for bind to a local ip address for this client. [#2024]
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- Feature `cookies` is now optional and enabled by default. [#1981]
|
* Feature `cookies` is now optional and enabled by default. [#1981]
|
||||||
- `ClientBuilder::connector` method would take `actix_http::client::Connector<T, U>` type. [#2008]
|
* `ClientBuilder::connector` method would take `actix_http::client::Connector<T, U>` type. [#2008]
|
||||||
- Basic auth password now takes blank passwords as an empty string instead of Option. [#2050]
|
* Basic auth password now takes blank passwords as an empty string instead of Option. [#2050]
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
- `ClientBuilder::default` function [#2008]
|
* `ClientBuilder::default` function [#2008]
|
||||||
|
|
||||||
[#1931]: https://github.com/actix/actix-web/pull/1931
|
[#1931]: https://github.com/actix/actix-web/pull/1931
|
||||||
[#1981]: https://github.com/actix/actix-web/pull/1981
|
[#1981]: https://github.com/actix/actix-web/pull/1981
|
||||||
@@ -109,18 +93,18 @@
|
|||||||
|
|
||||||
## 3.0.0-beta.2 - 2021-02-10
|
## 3.0.0-beta.2 - 2021-02-10
|
||||||
### Added
|
### Added
|
||||||
- `ClientRequest::insert_header` method which allows using typed headers. [#1869]
|
* `ClientRequest::insert_header` method which allows using typed headers. [#1869]
|
||||||
- `ClientRequest::append_header` method which allows using typed headers. [#1869]
|
* `ClientRequest::append_header` method which allows using typed headers. [#1869]
|
||||||
- `trust-dns` optional feature to enable `trust-dns-resolver` as client dns resolver. [#1969]
|
* `trust-dns` optional feature to enable `trust-dns-resolver` as client dns resolver. [#1969]
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- Relax default timeout for `Connector` to 5 seconds(original 1 second). [#1905]
|
* Relax default timeout for `Connector` to 5 seconds(original 1 second). [#1905]
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
- `ClientRequest::set`; use `ClientRequest::insert_header`. [#1869]
|
* `ClientRequest::set`; use `ClientRequest::insert_header`. [#1869]
|
||||||
- `ClientRequest::set_header`; use `ClientRequest::insert_header`. [#1869]
|
* `ClientRequest::set_header`; use `ClientRequest::insert_header`. [#1869]
|
||||||
- `ClientRequest::set_header_if_none`; use `ClientRequest::insert_header_if_none`. [#1869]
|
* `ClientRequest::set_header_if_none`; use `ClientRequest::insert_header_if_none`. [#1869]
|
||||||
- `ClientRequest::header`; use `ClientRequest::append_header`. [#1869]
|
* `ClientRequest::header`; use `ClientRequest::append_header`. [#1869]
|
||||||
|
|
||||||
[#1869]: https://github.com/actix/actix-web/pull/1869
|
[#1869]: https://github.com/actix/actix-web/pull/1869
|
||||||
[#1905]: https://github.com/actix/actix-web/pull/1905
|
[#1905]: https://github.com/actix/actix-web/pull/1905
|
||||||
@@ -129,32 +113,32 @@
|
|||||||
|
|
||||||
## 3.0.0-beta.1 - 2021-01-07
|
## 3.0.0-beta.1 - 2021-01-07
|
||||||
### Changed
|
### Changed
|
||||||
- Update `rand` to `0.8`
|
* Update `rand` to `0.8`
|
||||||
- Update `bytes` to `1.0`. [#1813]
|
* Update `bytes` to `1.0`. [#1813]
|
||||||
- Update `rust-tls` to `0.19`. [#1813]
|
* Update `rust-tls` to `0.19`. [#1813]
|
||||||
|
|
||||||
[#1813]: https://github.com/actix/actix-web/pull/1813
|
[#1813]: https://github.com/actix/actix-web/pull/1813
|
||||||
|
|
||||||
|
|
||||||
## 2.0.3 - 2020-11-29
|
## 2.0.3 - 2020-11-29
|
||||||
### Fixed
|
### Fixed
|
||||||
- Ensure `actix-http` dependency uses same `serde_urlencoded`.
|
* Ensure `actix-http` dependency uses same `serde_urlencoded`.
|
||||||
|
|
||||||
|
|
||||||
## 2.0.2 - 2020-11-25
|
## 2.0.2 - 2020-11-25
|
||||||
### Changed
|
### Changed
|
||||||
- Upgrade `serde_urlencoded` to `0.7`. [#1773]
|
* Upgrade `serde_urlencoded` to `0.7`. [#1773]
|
||||||
|
|
||||||
[#1773]: https://github.com/actix/actix-web/pull/1773
|
[#1773]: https://github.com/actix/actix-web/pull/1773
|
||||||
|
|
||||||
|
|
||||||
## 2.0.1 - 2020-10-30
|
## 2.0.1 - 2020-10-30
|
||||||
### Changed
|
### Changed
|
||||||
- Upgrade `base64` to `0.13`. [#1744]
|
* Upgrade `base64` to `0.13`. [#1744]
|
||||||
- Deprecate `ClientRequest::{if_some, if_true}`. [#1760]
|
* Deprecate `ClientRequest::{if_some, if_true}`. [#1760]
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- Use `Accept-Encoding: identity` instead of `Accept-Encoding: br` when no compression feature
|
* Use `Accept-Encoding: identity` instead of `Accept-Encoding: br` when no compression feature
|
||||||
is enabled [#1737]
|
is enabled [#1737]
|
||||||
|
|
||||||
[#1737]: https://github.com/actix/actix-web/pull/1737
|
[#1737]: https://github.com/actix/actix-web/pull/1737
|
||||||
@@ -164,209 +148,209 @@
|
|||||||
|
|
||||||
## 2.0.0 - 2020-09-11
|
## 2.0.0 - 2020-09-11
|
||||||
### Changed
|
### Changed
|
||||||
- `Client::build` was renamed to `Client::builder`.
|
* `Client::build` was renamed to `Client::builder`.
|
||||||
|
|
||||||
|
|
||||||
## 2.0.0-beta.4 - 2020-09-09
|
## 2.0.0-beta.4 - 2020-09-09
|
||||||
### Changed
|
### Changed
|
||||||
- Update actix-codec & actix-tls dependencies.
|
* Update actix-codec & actix-tls dependencies.
|
||||||
|
|
||||||
|
|
||||||
## 2.0.0-beta.3 - 2020-08-17
|
## 2.0.0-beta.3 - 2020-08-17
|
||||||
### Changed
|
### Changed
|
||||||
- Update `rustls` to 0.18
|
* Update `rustls` to 0.18
|
||||||
|
|
||||||
|
|
||||||
## 2.0.0-beta.2 - 2020-07-21
|
## 2.0.0-beta.2 - 2020-07-21
|
||||||
### Changed
|
### Changed
|
||||||
- Update `actix-http` dependency to 2.0.0-beta.2
|
* Update `actix-http` dependency to 2.0.0-beta.2
|
||||||
|
|
||||||
|
|
||||||
## [2.0.0-beta.1] - 2020-07-14
|
## [2.0.0-beta.1] - 2020-07-14
|
||||||
### Changed
|
### Changed
|
||||||
- Update `actix-http` dependency to 2.0.0-beta.1
|
* Update `actix-http` dependency to 2.0.0-beta.1
|
||||||
|
|
||||||
## [2.0.0-alpha.2] - 2020-05-21
|
## [2.0.0-alpha.2] - 2020-05-21
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Implement `std::error::Error` for our custom errors [#1422]
|
* Implement `std::error::Error` for our custom errors [#1422]
|
||||||
- Bump minimum supported Rust version to 1.40
|
* Bump minimum supported Rust version to 1.40
|
||||||
- Update `base64` dependency to 0.12
|
* Update `base64` dependency to 0.12
|
||||||
|
|
||||||
[#1422]: https://github.com/actix/actix-web/pull/1422
|
[#1422]: https://github.com/actix/actix-web/pull/1422
|
||||||
|
|
||||||
## [2.0.0-alpha.1] - 2020-03-11
|
## [2.0.0-alpha.1] - 2020-03-11
|
||||||
|
|
||||||
- Update `actix-http` dependency to 2.0.0-alpha.2
|
* Update `actix-http` dependency to 2.0.0-alpha.2
|
||||||
- Update `rustls` dependency to 0.17
|
* Update `rustls` dependency to 0.17
|
||||||
- ClientBuilder accepts initial_window_size and initial_connection_window_size HTTP2 configuration
|
* ClientBuilder accepts initial_window_size and initial_connection_window_size HTTP2 configuration
|
||||||
- ClientBuilder allowing to set max_http_version to limit HTTP version to be used
|
* ClientBuilder allowing to set max_http_version to limit HTTP version to be used
|
||||||
|
|
||||||
## [1.0.1] - 2019-12-15
|
## [1.0.1] - 2019-12-15
|
||||||
|
|
||||||
- Fix compilation with default features off
|
* Fix compilation with default features off
|
||||||
|
|
||||||
## [1.0.0] - 2019-12-13
|
## [1.0.0] - 2019-12-13
|
||||||
|
|
||||||
- Release
|
* Release
|
||||||
|
|
||||||
## [1.0.0-alpha.3]
|
## [1.0.0-alpha.3]
|
||||||
|
|
||||||
- Migrate to `std::future`
|
* Migrate to `std::future`
|
||||||
|
|
||||||
|
|
||||||
## [0.2.8] - 2019-11-06
|
## [0.2.8] - 2019-11-06
|
||||||
|
|
||||||
- Add support for setting query from Serialize type for client request.
|
* Add support for setting query from Serialize type for client request.
|
||||||
|
|
||||||
|
|
||||||
## [0.2.7] - 2019-09-25
|
## [0.2.7] - 2019-09-25
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Remaining getter methods for `ClientRequest`'s private `head` field #1101
|
* Remaining getter methods for `ClientRequest`'s private `head` field #1101
|
||||||
|
|
||||||
|
|
||||||
## [0.2.6] - 2019-09-12
|
## [0.2.6] - 2019-09-12
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Export frozen request related types.
|
* Export frozen request related types.
|
||||||
|
|
||||||
|
|
||||||
## [0.2.5] - 2019-09-11
|
## [0.2.5] - 2019-09-11
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Add `FrozenClientRequest` to support retries for sending HTTP requests
|
* Add `FrozenClientRequest` to support retries for sending HTTP requests
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Ensure that the `Host` header is set when initiating a WebSocket client connection.
|
* Ensure that the `Host` header is set when initiating a WebSocket client connection.
|
||||||
|
|
||||||
|
|
||||||
## [0.2.4] - 2019-08-13
|
## [0.2.4] - 2019-08-13
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Update percent-encoding to "2.1"
|
* Update percent-encoding to "2.1"
|
||||||
|
|
||||||
- Update serde_urlencoded to "0.6.1"
|
* Update serde_urlencoded to "0.6.1"
|
||||||
|
|
||||||
|
|
||||||
## [0.2.3] - 2019-08-01
|
## [0.2.3] - 2019-08-01
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Add `rustls` support
|
* Add `rustls` support
|
||||||
|
|
||||||
|
|
||||||
## [0.2.2] - 2019-07-01
|
## [0.2.2] - 2019-07-01
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Always append a colon after username in basic auth
|
* Always append a colon after username in basic auth
|
||||||
|
|
||||||
- Upgrade `rand` dependency version to 0.7
|
* Upgrade `rand` dependency version to 0.7
|
||||||
|
|
||||||
|
|
||||||
## [0.2.1] - 2019-06-05
|
## [0.2.1] - 2019-06-05
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Add license files
|
* Add license files
|
||||||
|
|
||||||
## [0.2.0] - 2019-05-12
|
## [0.2.0] - 2019-05-12
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Allow to send headers in `Camel-Case` form.
|
* Allow to send headers in `Camel-Case` form.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Upgrade actix-http dependency.
|
* Upgrade actix-http dependency.
|
||||||
|
|
||||||
|
|
||||||
## [0.1.1] - 2019-04-19
|
## [0.1.1] - 2019-04-19
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Allow to specify server address for http and ws requests.
|
* Allow to specify server address for http and ws requests.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- `ClientRequest::if_true()` and `ClientRequest::if_some()` use instance instead of ref
|
* `ClientRequest::if_true()` and `ClientRequest::if_some()` use instance instead of ref
|
||||||
|
|
||||||
|
|
||||||
## [0.1.0] - 2019-04-16
|
## [0.1.0] - 2019-04-16
|
||||||
|
|
||||||
- No changes
|
* No changes
|
||||||
|
|
||||||
|
|
||||||
## [0.1.0-alpha.6] - 2019-04-14
|
## [0.1.0-alpha.6] - 2019-04-14
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Do not set default headers for websocket request
|
* Do not set default headers for websocket request
|
||||||
|
|
||||||
|
|
||||||
## [0.1.0-alpha.5] - 2019-04-12
|
## [0.1.0-alpha.5] - 2019-04-12
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Do not set any default headers
|
* Do not set any default headers
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Add Debug impl for BoxedSocket
|
* Add Debug impl for BoxedSocket
|
||||||
|
|
||||||
|
|
||||||
## [0.1.0-alpha.4] - 2019-04-08
|
## [0.1.0-alpha.4] - 2019-04-08
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Update actix-http dependency
|
* Update actix-http dependency
|
||||||
|
|
||||||
|
|
||||||
## [0.1.0-alpha.3] - 2019-04-02
|
## [0.1.0-alpha.3] - 2019-04-02
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Export `MessageBody` type
|
* Export `MessageBody` type
|
||||||
|
|
||||||
- `ClientResponse::json()` - Loads and parse `application/json` encoded body
|
* `ClientResponse::json()` - Loads and parse `application/json` encoded body
|
||||||
|
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- `ClientRequest::json()` accepts reference instead of object.
|
* `ClientRequest::json()` accepts reference instead of object.
|
||||||
|
|
||||||
- `ClientResponse::body()` does not consume response object.
|
* `ClientResponse::body()` does not consume response object.
|
||||||
|
|
||||||
- Renamed `ClientRequest::close_connection()` to `ClientRequest::force_close()`
|
* Renamed `ClientRequest::close_connection()` to `ClientRequest::force_close()`
|
||||||
|
|
||||||
|
|
||||||
## [0.1.0-alpha.2] - 2019-03-29
|
## [0.1.0-alpha.2] - 2019-03-29
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Per request and session wide request timeout.
|
* Per request and session wide request timeout.
|
||||||
|
|
||||||
- Session wide headers.
|
* Session wide headers.
|
||||||
|
|
||||||
- Session wide basic and bearer auth.
|
* Session wide basic and bearer auth.
|
||||||
|
|
||||||
- Re-export `actix_http::client::Connector`.
|
* Re-export `actix_http::client::Connector`.
|
||||||
|
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Allow to override request's uri
|
* Allow to override request's uri
|
||||||
|
|
||||||
- Export `ws` sub-module with websockets related types
|
* Export `ws` sub-module with websockets related types
|
||||||
|
|
||||||
|
|
||||||
## [0.1.0-alpha.1] - 2019-03-28
|
## [0.1.0-alpha.1] - 2019-03-28
|
||||||
|
|
||||||
- Initial impl
|
* Initial impl
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "awc"
|
name = "awc"
|
||||||
version = "3.0.0-beta.15"
|
version = "3.0.0-beta.13"
|
||||||
authors = [
|
authors = [
|
||||||
"Nikolay Kim <fafhrd91@gmail.com>",
|
"Nikolay Kim <fafhrd91@gmail.com>",
|
||||||
"fakeshadow <24548779@qq.com>",
|
"fakeshadow <24548779@qq.com>",
|
||||||
@@ -60,9 +60,9 @@ dangerous-h2c = []
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
actix-codec = "0.4.1"
|
actix-codec = "0.4.1"
|
||||||
actix-service = "2.0.0"
|
actix-service = "2.0.0"
|
||||||
actix-http = "3.0.0-beta.17"
|
actix-http = "3.0.0-beta.15"
|
||||||
actix-rt = { version = "2.1", default-features = false }
|
actix-rt = { version = "2.1", default-features = false }
|
||||||
actix-tls = { version = "3.0.0", features = ["connect", "uri"] }
|
actix-tls = { version = "3.0.0-rc.1", features = ["connect", "uri"] }
|
||||||
actix-utils = "3.0.0"
|
actix-utils = "3.0.0"
|
||||||
|
|
||||||
ahash = "0.7"
|
ahash = "0.7"
|
||||||
@@ -70,11 +70,11 @@ base64 = "0.13"
|
|||||||
bytes = "1"
|
bytes = "1"
|
||||||
cfg-if = "1"
|
cfg-if = "1"
|
||||||
derive_more = "0.99.5"
|
derive_more = "0.99.5"
|
||||||
futures-core = { version = "0.3.7", default-features = false, features = ["alloc"] }
|
futures-core = { version = "0.3.7", default-features = false }
|
||||||
futures-util = { version = "0.3.7", default-features = false, features = ["alloc", "sink"] }
|
futures-util = { version = "0.3.7", default-features = false }
|
||||||
h2 = "0.3.9"
|
h2 = "0.3.9"
|
||||||
http = "0.2.5"
|
http = "0.2.5"
|
||||||
itoa = "1"
|
itoa = "0.4"
|
||||||
log =" 0.4"
|
log =" 0.4"
|
||||||
mime = "0.3"
|
mime = "0.3"
|
||||||
percent-encoding = "2.1"
|
percent-encoding = "2.1"
|
||||||
@@ -83,7 +83,7 @@ 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"
|
||||||
tokio = { version = "1.8", features = ["sync"] }
|
tokio = { version = "1", features = ["sync"] }
|
||||||
|
|
||||||
cookie = { version = "0.15", features = ["percent-encode"], optional = true }
|
cookie = { version = "0.15", features = ["percent-encode"], optional = true }
|
||||||
|
|
||||||
@@ -93,13 +93,13 @@ tls-rustls = { package = "rustls", version = "0.20.0", optional = true, features
|
|||||||
trust-dns-resolver = { version = "0.20.0", optional = true }
|
trust-dns-resolver = { version = "0.20.0", optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
actix-http = { version = "3.0.0-beta.17", features = ["openssl"] }
|
actix-http = { version = "3.0.0-beta.15", features = ["openssl"] }
|
||||||
actix-http-test = { version = "3.0.0-beta.10", features = ["openssl"] }
|
actix-http-test = { version = "3.0.0-beta.9", features = ["openssl"] }
|
||||||
actix-server = "2.0.0-rc.2"
|
actix-server = "2.0.0-rc.1"
|
||||||
actix-test = { version = "0.1.0-beta.10", features = ["openssl", "rustls"] }
|
actix-test = { version = "0.1.0-beta.8", features = ["openssl", "rustls"] }
|
||||||
actix-tls = { version = "3.0.0", features = ["openssl", "rustls"] }
|
actix-tls = { version = "3.0.0-rc.1", features = ["openssl", "rustls"] }
|
||||||
actix-utils = "3.0.0"
|
actix-utils = "3.0.0"
|
||||||
actix-web = { version = "4.0.0-beta.16", features = ["openssl"] }
|
actix-web = { version = "4.0.0-beta.14", features = ["openssl"] }
|
||||||
|
|
||||||
brotli2 = "0.3.2"
|
brotli2 = "0.3.2"
|
||||||
env_logger = "0.9"
|
env_logger = "0.9"
|
||||||
|
@@ -3,9 +3,9 @@
|
|||||||
> 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.15)
|
[](https://docs.rs/awc/3.0.0-beta.13)
|
||||||

|

|
||||||
[](https://deps.rs/crate/awc/3.0.0-beta.15)
|
[](https://deps.rs/crate/awc/3.0.0-beta.13)
|
||||||
[](https://discord.gg/NWpN5mmg3x)
|
[](https://discord.gg/NWpN5mmg3x)
|
||||||
|
|
||||||
## Documentation & Resources
|
## Documentation & Resources
|
||||||
|
@@ -45,7 +45,9 @@ impl AnyBody {
|
|||||||
where
|
where
|
||||||
B: MessageBody + 'static,
|
B: MessageBody + 'static,
|
||||||
{
|
{
|
||||||
Self::Body { body: body.boxed() }
|
Self::Body {
|
||||||
|
body: BoxBody::new(body),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Constructs new `AnyBody` instance from a slice of bytes by copying it.
|
/// Constructs new `AnyBody` instance from a slice of bytes by copying it.
|
||||||
@@ -77,27 +79,10 @@ impl<B> AnyBody<B>
|
|||||||
where
|
where
|
||||||
B: MessageBody + 'static,
|
B: MessageBody + 'static,
|
||||||
{
|
{
|
||||||
/// Converts a [`MessageBody`] type into the best possible representation.
|
|
||||||
///
|
|
||||||
/// Checks size for `None` and tries to convert to `Bytes`. Otherwise, uses the `Body` variant.
|
|
||||||
pub fn from_message_body(body: B) -> Self
|
|
||||||
where
|
|
||||||
B: MessageBody,
|
|
||||||
{
|
|
||||||
if matches!(body.size(), BodySize::None) {
|
|
||||||
return Self::None;
|
|
||||||
}
|
|
||||||
|
|
||||||
match body.try_into_bytes() {
|
|
||||||
Ok(body) => Self::Bytes { body },
|
|
||||||
Err(body) => Self::new(body),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn into_boxed(self) -> AnyBody {
|
pub fn into_boxed(self) -> AnyBody {
|
||||||
match self {
|
match self {
|
||||||
Self::None => AnyBody::None,
|
Self::None => AnyBody::None,
|
||||||
Self::Bytes { body } => AnyBody::Bytes { body },
|
Self::Bytes { body: bytes } => AnyBody::Bytes { body: bytes },
|
||||||
Self::Body { body } => AnyBody::new_boxed(body),
|
Self::Body { body } => AnyBody::new_boxed(body),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -9,13 +9,11 @@ use actix_rt::net::{ActixStream, TcpStream};
|
|||||||
use actix_service::{boxed, Service};
|
use actix_service::{boxed, Service};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
client::{
|
client::{ConnectInfo, Connector, ConnectorService, TcpConnectError, TcpConnection},
|
||||||
ClientConfig, ConnectInfo, Connector, ConnectorService, TcpConnectError, TcpConnection,
|
|
||||||
},
|
|
||||||
connect::DefaultConnector,
|
connect::DefaultConnector,
|
||||||
error::SendRequestError,
|
error::SendRequestError,
|
||||||
middleware::{NestTransform, Redirect, Transform},
|
middleware::{NestTransform, Redirect, Transform},
|
||||||
Client, ConnectRequest, ConnectResponse,
|
Client, ClientConfig, ConnectRequest, ConnectResponse,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// An HTTP Client builder
|
/// An HTTP Client builder
|
||||||
|
@@ -267,9 +267,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!("Plain Tcp connection can be used only in Http1 protocol"),
|
||||||
unreachable!("Plain TCP connection can be used only with HTTP/1.1 protocol")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@@ -22,13 +22,11 @@ use futures_core::{future::LocalBoxFuture, ready};
|
|||||||
use http::Uri;
|
use http::Uri;
|
||||||
use pin_project_lite::pin_project;
|
use pin_project_lite::pin_project;
|
||||||
|
|
||||||
use super::{
|
use super::config::ConnectorConfig;
|
||||||
config::ConnectorConfig,
|
use super::connection::{Connection, ConnectionIo};
|
||||||
connection::{Connection, ConnectionIo},
|
use super::error::ConnectError;
|
||||||
error::ConnectError,
|
use super::pool::ConnectionPool;
|
||||||
pool::ConnectionPool,
|
use super::Connect;
|
||||||
Connect,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum OurTlsConnector {
|
enum OurTlsConnector {
|
||||||
#[allow(dead_code)] // only dead when no TLS feature is enabled
|
#[allow(dead_code)] // only dead when no TLS feature is enabled
|
||||||
@@ -37,12 +35,6 @@ enum OurTlsConnector {
|
|||||||
#[cfg(feature = "openssl")]
|
#[cfg(feature = "openssl")]
|
||||||
Openssl(actix_tls::connect::openssl::reexports::SslConnector),
|
Openssl(actix_tls::connect::openssl::reexports::SslConnector),
|
||||||
|
|
||||||
/// Provided because building the OpenSSL context on newer versions can be very slow.
|
|
||||||
/// This prevents unnecessary calls to `.build()` while constructing the client connector.
|
|
||||||
#[cfg(feature = "openssl")]
|
|
||||||
#[allow(dead_code)] // false positive; used in build_ssl
|
|
||||||
OpensslBuilder(actix_tls::connect::openssl::reexports::SslConnectorBuilder),
|
|
||||||
|
|
||||||
#[cfg(feature = "rustls")]
|
#[cfg(feature = "rustls")]
|
||||||
Rustls(std::sync::Arc<actix_tls::connect::rustls::reexports::ClientConfig>),
|
Rustls(std::sync::Arc<actix_tls::connect::rustls::reexports::ClientConfig>),
|
||||||
}
|
}
|
||||||
@@ -65,7 +57,7 @@ pub struct Connector<T> {
|
|||||||
config: ConnectorConfig,
|
config: ConnectorConfig,
|
||||||
|
|
||||||
#[allow(dead_code)] // only dead when no TLS feature is enabled
|
#[allow(dead_code)] // only dead when no TLS feature is enabled
|
||||||
tls: OurTlsConnector,
|
ssl: OurTlsConnector,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Connector<()> {
|
impl Connector<()> {
|
||||||
@@ -80,7 +72,7 @@ impl Connector<()> {
|
|||||||
Connector {
|
Connector {
|
||||||
connector: TcpConnector::new(resolver::resolver()).service(),
|
connector: TcpConnector::new(resolver::resolver()).service(),
|
||||||
config: ConnectorConfig::default(),
|
config: ConnectorConfig::default(),
|
||||||
tls: Self::build_ssl(vec![b"h2".to_vec(), b"http/1.1".to_vec()]),
|
ssl: Self::build_ssl(vec![b"h2".to_vec(), b"http/1.1".to_vec()]),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,7 +116,7 @@ impl Connector<()> {
|
|||||||
log::error!("Can not set ALPN protocol: {:?}", err);
|
log::error!("Can not set ALPN protocol: {:?}", err);
|
||||||
}
|
}
|
||||||
|
|
||||||
OurTlsConnector::OpensslBuilder(ssl)
|
OurTlsConnector::Openssl(ssl.build())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,7 +134,7 @@ impl<S> Connector<S> {
|
|||||||
Connector {
|
Connector {
|
||||||
connector,
|
connector,
|
||||||
config: self.config,
|
config: self.config,
|
||||||
tls: self.tls,
|
ssl: self.ssl,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -175,35 +167,23 @@ where
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Use custom OpenSSL `SslConnector` instance.
|
|
||||||
#[cfg(feature = "openssl")]
|
#[cfg(feature = "openssl")]
|
||||||
pub fn openssl(
|
/// Use custom `SslConnector` instance.
|
||||||
mut self,
|
|
||||||
connector: actix_tls::connect::openssl::reexports::SslConnector,
|
|
||||||
) -> Self {
|
|
||||||
self.tls = OurTlsConnector::Openssl(connector);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// See docs for [`Connector::openssl`].
|
|
||||||
#[doc(hidden)]
|
|
||||||
#[cfg(feature = "openssl")]
|
|
||||||
#[deprecated(since = "3.0.0", note = "Renamed to `Connector::openssl`.")]
|
|
||||||
pub fn ssl(
|
pub fn ssl(
|
||||||
mut self,
|
mut self,
|
||||||
connector: actix_tls::connect::openssl::reexports::SslConnector,
|
connector: actix_tls::connect::openssl::reexports::SslConnector,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
self.tls = OurTlsConnector::Openssl(connector);
|
self.ssl = OurTlsConnector::Openssl(connector);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Use custom Rustls `ClientConfig` instance.
|
|
||||||
#[cfg(feature = "rustls")]
|
#[cfg(feature = "rustls")]
|
||||||
|
/// Use custom `ClientConfig` instance.
|
||||||
pub fn rustls(
|
pub fn rustls(
|
||||||
mut self,
|
mut self,
|
||||||
connector: std::sync::Arc<actix_tls::connect::rustls::reexports::ClientConfig>,
|
connector: std::sync::Arc<actix_tls::connect::rustls::reexports::ClientConfig>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
self.tls = OurTlsConnector::Rustls(connector);
|
self.ssl = OurTlsConnector::Rustls(connector);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,7 +198,7 @@ where
|
|||||||
unimplemented!("actix-http client only supports versions http/1.1 & http/2")
|
unimplemented!("actix-http client only supports versions http/1.1 & http/2")
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
self.tls = Connector::build_ssl(versions);
|
self.ssl = Connector::build_ssl(versions);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -290,8 +270,8 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Finish configuration process and create connector service.
|
/// Finish configuration process and create connector service.
|
||||||
///
|
/// The Connector builder always concludes by calling `finish()` last in
|
||||||
/// The `Connector` builder always concludes by calling `finish()` last in its combinator chain.
|
/// its combinator chain.
|
||||||
pub fn finish(self) -> ConnectorService<S, IO> {
|
pub fn finish(self) -> ConnectorService<S, IO> {
|
||||||
let local_address = self.config.local_address;
|
let local_address = self.config.local_address;
|
||||||
let timeout = self.config.timeout;
|
let timeout = self.config.timeout;
|
||||||
@@ -304,15 +284,7 @@ where
|
|||||||
service: tcp_service_inner.clone(),
|
service: tcp_service_inner.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let tls = match self.tls {
|
let tls_service = match self.ssl {
|
||||||
#[cfg(feature = "openssl")]
|
|
||||||
OurTlsConnector::OpensslBuilder(builder) => {
|
|
||||||
OurTlsConnector::Openssl(builder.build())
|
|
||||||
}
|
|
||||||
tls => tls,
|
|
||||||
};
|
|
||||||
|
|
||||||
let tls_service = match tls {
|
|
||||||
OurTlsConnector::None => {
|
OurTlsConnector::None => {
|
||||||
#[cfg(not(feature = "dangerous-h2c"))]
|
#[cfg(not(feature = "dangerous-h2c"))]
|
||||||
{
|
{
|
||||||
@@ -402,11 +374,6 @@ where
|
|||||||
Some(actix_service::boxed::rc_service(tls_service))
|
Some(actix_service::boxed::rc_service(tls_service))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "openssl")]
|
|
||||||
OurTlsConnector::OpensslBuilder(_) => {
|
|
||||||
unreachable!("OpenSSL builder is built before this match.");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "rustls")]
|
#[cfg(feature = "rustls")]
|
||||||
OurTlsConnector::Rustls(tls) => {
|
OurTlsConnector::Rustls(tls) => {
|
||||||
const H2: &[u8] = b"h2";
|
const H2: &[u8] = b"h2";
|
||||||
@@ -886,7 +853,7 @@ mod tests {
|
|||||||
let connector = Connector {
|
let connector = Connector {
|
||||||
connector: TcpConnector::new(resolver::resolver()).service(),
|
connector: TcpConnector::new(resolver::resolver()).service(),
|
||||||
config: ConnectorConfig::default(),
|
config: ConnectorConfig::default(),
|
||||||
tls: OurTlsConnector::None,
|
ssl: OurTlsConnector::None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let client = Client::builder().connector(connector).finish();
|
let client = Client::builder().connector(connector).finish();
|
||||||
|
@@ -13,17 +13,16 @@ use actix_http::{
|
|||||||
Payload, RequestHeadType, ResponseHead, StatusCode,
|
Payload, RequestHeadType, ResponseHead, StatusCode,
|
||||||
};
|
};
|
||||||
use actix_utils::future::poll_fn;
|
use actix_utils::future::poll_fn;
|
||||||
use bytes::{buf::BufMut, Bytes, BytesMut};
|
use bytes::buf::BufMut;
|
||||||
|
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 pin_project_lite::pin_project;
|
||||||
|
|
||||||
use crate::BoxError;
|
use crate::BoxError;
|
||||||
|
|
||||||
use super::{
|
use super::connection::{ConnectionIo, H1Connection};
|
||||||
connection::{ConnectionIo, H1Connection},
|
use super::error::{ConnectError, SendRequestError};
|
||||||
error::{ConnectError, SendRequestError},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub(crate) async fn send_request<Io, B>(
|
pub(crate) async fn send_request<Io, B>(
|
||||||
io: H1Connection<Io>,
|
io: H1Connection<Io>,
|
||||||
@@ -124,12 +123,7 @@ where
|
|||||||
|
|
||||||
Ok((head, Payload::None))
|
Ok((head, Payload::None))
|
||||||
}
|
}
|
||||||
_ => Ok((
|
_ => Ok((head, Payload::Stream(Box::pin(PlStream::new(framed))))),
|
||||||
head,
|
|
||||||
Payload::Stream {
|
|
||||||
payload: Box::pin(PlStream::new(framed)),
|
|
||||||
},
|
|
||||||
)),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -52,11 +52,9 @@ where
|
|||||||
let _ = match length {
|
let _ = match length {
|
||||||
BodySize::None => None,
|
BodySize::None => None,
|
||||||
|
|
||||||
BodySize::Sized(0) => {
|
BodySize::Sized(0) => req
|
||||||
#[allow(clippy::declare_interior_mutable_const)]
|
.headers_mut()
|
||||||
const HV_ZERO: HeaderValue = HeaderValue::from_static("0");
|
.insert(CONTENT_LENGTH, HeaderValue::from_static("0")),
|
||||||
req.headers_mut().insert(CONTENT_LENGTH, HV_ZERO)
|
|
||||||
}
|
|
||||||
|
|
||||||
BodySize::Sized(len) => {
|
BodySize::Sized(len) => {
|
||||||
let mut buf = itoa::Buffer::new();
|
let mut buf = itoa::Buffer::new();
|
||||||
|
@@ -1,15 +1,6 @@
|
|||||||
//! HTTP client.
|
//! HTTP client.
|
||||||
|
|
||||||
use std::{convert::TryFrom, rc::Rc, time::Duration};
|
use http::Uri;
|
||||||
|
|
||||||
use actix_http::{error::HttpError, header::HeaderMap, Method, RequestHead, Uri};
|
|
||||||
use actix_rt::net::TcpStream;
|
|
||||||
use actix_service::Service;
|
|
||||||
pub use actix_tls::connect::{
|
|
||||||
ConnectError as TcpConnectError, ConnectInfo, Connection as TcpConnection,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{ws, BoxConnectorService, ClientBuilder, ClientRequest};
|
|
||||||
|
|
||||||
mod config;
|
mod config;
|
||||||
mod connection;
|
mod connection;
|
||||||
@@ -19,6 +10,10 @@ mod h1proto;
|
|||||||
mod h2proto;
|
mod h2proto;
|
||||||
mod pool;
|
mod pool;
|
||||||
|
|
||||||
|
pub use actix_tls::connect::{
|
||||||
|
ConnectError as TcpConnectError, ConnectInfo, Connection as TcpConnection,
|
||||||
|
};
|
||||||
|
|
||||||
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};
|
||||||
@@ -28,176 +23,3 @@ pub struct Connect {
|
|||||||
pub uri: Uri,
|
pub uri: Uri,
|
||||||
pub addr: Option<std::net::SocketAddr>,
|
pub addr: Option<std::net::SocketAddr>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An asynchronous HTTP and WebSocket client.
|
|
||||||
///
|
|
||||||
/// You should take care to create, at most, one `Client` per thread. Otherwise, expect higher CPU
|
|
||||||
/// and memory usage.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
/// ```
|
|
||||||
/// use awc::Client;
|
|
||||||
///
|
|
||||||
/// #[actix_rt::main]
|
|
||||||
/// async fn main() {
|
|
||||||
/// let mut client = Client::default();
|
|
||||||
///
|
|
||||||
/// let res = client.get("http://www.rust-lang.org")
|
|
||||||
/// .insert_header(("User-Agent", "my-app/1.2"))
|
|
||||||
/// .send()
|
|
||||||
/// .await;
|
|
||||||
///
|
|
||||||
/// println!("Response: {:?}", res);
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Client(pub(crate) ClientConfig);
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub(crate) struct ClientConfig {
|
|
||||||
pub(crate) connector: BoxConnectorService,
|
|
||||||
pub(crate) default_headers: Rc<HeaderMap>,
|
|
||||||
pub(crate) timeout: Option<Duration>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Client {
|
|
||||||
fn default() -> Self {
|
|
||||||
ClientBuilder::new().finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Client {
|
|
||||||
/// Create new client instance with default settings.
|
|
||||||
pub fn new() -> Client {
|
|
||||||
Client::default()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create `Client` builder.
|
|
||||||
/// This function is equivalent of `ClientBuilder::new()`.
|
|
||||||
pub fn builder() -> ClientBuilder<
|
|
||||||
impl Service<
|
|
||||||
ConnectInfo<Uri>,
|
|
||||||
Response = TcpConnection<Uri, TcpStream>,
|
|
||||||
Error = TcpConnectError,
|
|
||||||
> + Clone,
|
|
||||||
> {
|
|
||||||
ClientBuilder::new()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Construct HTTP request.
|
|
||||||
pub fn request<U>(&self, method: Method, url: U) -> ClientRequest
|
|
||||||
where
|
|
||||||
Uri: TryFrom<U>,
|
|
||||||
<Uri as TryFrom<U>>::Error: Into<HttpError>,
|
|
||||||
{
|
|
||||||
let mut req = ClientRequest::new(method, url, self.0.clone());
|
|
||||||
|
|
||||||
for header in self.0.default_headers.iter() {
|
|
||||||
// header map is empty
|
|
||||||
// TODO: probably append instead
|
|
||||||
req = req.insert_header_if_none(header);
|
|
||||||
}
|
|
||||||
req
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create `ClientRequest` from `RequestHead`
|
|
||||||
///
|
|
||||||
/// It is useful for proxy requests. This implementation
|
|
||||||
/// copies all headers and the method.
|
|
||||||
pub fn request_from<U>(&self, url: U, head: &RequestHead) -> ClientRequest
|
|
||||||
where
|
|
||||||
Uri: TryFrom<U>,
|
|
||||||
<Uri as TryFrom<U>>::Error: Into<HttpError>,
|
|
||||||
{
|
|
||||||
let mut req = self.request(head.method.clone(), url);
|
|
||||||
for header in head.headers.iter() {
|
|
||||||
req = req.insert_header_if_none(header);
|
|
||||||
}
|
|
||||||
req
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Construct HTTP *GET* request.
|
|
||||||
pub fn get<U>(&self, url: U) -> ClientRequest
|
|
||||||
where
|
|
||||||
Uri: TryFrom<U>,
|
|
||||||
<Uri as TryFrom<U>>::Error: Into<HttpError>,
|
|
||||||
{
|
|
||||||
self.request(Method::GET, url)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Construct HTTP *HEAD* request.
|
|
||||||
pub fn head<U>(&self, url: U) -> ClientRequest
|
|
||||||
where
|
|
||||||
Uri: TryFrom<U>,
|
|
||||||
<Uri as TryFrom<U>>::Error: Into<HttpError>,
|
|
||||||
{
|
|
||||||
self.request(Method::HEAD, url)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Construct HTTP *PUT* request.
|
|
||||||
pub fn put<U>(&self, url: U) -> ClientRequest
|
|
||||||
where
|
|
||||||
Uri: TryFrom<U>,
|
|
||||||
<Uri as TryFrom<U>>::Error: Into<HttpError>,
|
|
||||||
{
|
|
||||||
self.request(Method::PUT, url)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Construct HTTP *POST* request.
|
|
||||||
pub fn post<U>(&self, url: U) -> ClientRequest
|
|
||||||
where
|
|
||||||
Uri: TryFrom<U>,
|
|
||||||
<Uri as TryFrom<U>>::Error: Into<HttpError>,
|
|
||||||
{
|
|
||||||
self.request(Method::POST, url)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Construct HTTP *PATCH* request.
|
|
||||||
pub fn patch<U>(&self, url: U) -> ClientRequest
|
|
||||||
where
|
|
||||||
Uri: TryFrom<U>,
|
|
||||||
<Uri as TryFrom<U>>::Error: Into<HttpError>,
|
|
||||||
{
|
|
||||||
self.request(Method::PATCH, url)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Construct HTTP *DELETE* request.
|
|
||||||
pub fn delete<U>(&self, url: U) -> ClientRequest
|
|
||||||
where
|
|
||||||
Uri: TryFrom<U>,
|
|
||||||
<Uri as TryFrom<U>>::Error: Into<HttpError>,
|
|
||||||
{
|
|
||||||
self.request(Method::DELETE, url)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Construct HTTP *OPTIONS* request.
|
|
||||||
pub fn options<U>(&self, url: U) -> ClientRequest
|
|
||||||
where
|
|
||||||
Uri: TryFrom<U>,
|
|
||||||
<Uri as TryFrom<U>>::Error: Into<HttpError>,
|
|
||||||
{
|
|
||||||
self.request(Method::OPTIONS, url)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Initialize a WebSocket connection.
|
|
||||||
/// Returns a WebSocket connection builder.
|
|
||||||
pub fn ws<U>(&self, url: U) -> ws::WebsocketsRequest
|
|
||||||
where
|
|
||||||
Uri: TryFrom<U>,
|
|
||||||
<Uri as TryFrom<U>>::Error: Into<HttpError>,
|
|
||||||
{
|
|
||||||
let mut req = ws::WebsocketsRequest::new(url, self.0.clone());
|
|
||||||
for (key, value) in self.0.default_headers.iter() {
|
|
||||||
req.head.headers.insert(key.clone(), value.clone());
|
|
||||||
}
|
|
||||||
req
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get default HeaderMap of Client.
|
|
||||||
///
|
|
||||||
/// Returns Some(&mut HeaderMap) when Client object is unique
|
|
||||||
/// (No other clone of client exists at the same time).
|
|
||||||
pub fn headers(&mut self) -> Option<&mut HeaderMap> {
|
|
||||||
Rc::get_mut(&mut self.0.default_headers)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@@ -16,7 +16,7 @@ use crate::{
|
|||||||
client::{
|
client::{
|
||||||
Connect as ClientConnect, ConnectError, Connection, ConnectionIo, SendRequestError,
|
Connect as ClientConnect, ConnectError, Connection, ConnectionIo, SendRequestError,
|
||||||
},
|
},
|
||||||
ClientResponse,
|
response::ClientResponse,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub type BoxConnectorService = Rc<
|
pub type BoxConnectorService = Rc<
|
||||||
|
@@ -1,6 +1,5 @@
|
|||||||
//! HTTP client errors
|
//! HTTP client errors
|
||||||
|
|
||||||
// TODO: figure out how best to expose http::Error vs actix_http::Error
|
|
||||||
pub use actix_http::{
|
pub use actix_http::{
|
||||||
error::{HttpError, PayloadError},
|
error::{HttpError, PayloadError},
|
||||||
header::HeaderValue,
|
header::HeaderValue,
|
||||||
|
@@ -5,16 +5,15 @@ use futures_core::Stream;
|
|||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
use actix_http::{
|
use actix_http::{
|
||||||
body::MessageBody,
|
|
||||||
error::HttpError,
|
error::HttpError,
|
||||||
header::{HeaderMap, HeaderName, TryIntoHeaderValue},
|
header::{HeaderMap, HeaderName, TryIntoHeaderValue},
|
||||||
Method, RequestHead, Uri,
|
Method, RequestHead, Uri,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
client::ClientConfig,
|
any_body::AnyBody,
|
||||||
sender::{RequestSender, SendClientRequest},
|
sender::{RequestSender, SendClientRequest},
|
||||||
BoxError,
|
BoxError, ClientConfig,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// `FrozenClientRequest` struct represents cloneable client request.
|
/// `FrozenClientRequest` struct represents cloneable client request.
|
||||||
@@ -47,7 +46,7 @@ impl FrozenClientRequest {
|
|||||||
/// Send a body.
|
/// Send a body.
|
||||||
pub fn send_body<B>(&self, body: B) -> SendClientRequest
|
pub fn send_body<B>(&self, body: B) -> SendClientRequest
|
||||||
where
|
where
|
||||||
B: MessageBody + 'static,
|
B: Into<AnyBody>,
|
||||||
{
|
{
|
||||||
RequestSender::Rc(self.head.clone(), None).send_body(
|
RequestSender::Rc(self.head.clone(), None).send_body(
|
||||||
self.addr,
|
self.addr,
|
||||||
@@ -160,7 +159,7 @@ impl FrozenSendBuilder {
|
|||||||
/// Complete request construction and send a body.
|
/// Complete request construction and send a body.
|
||||||
pub fn send_body<B>(self, body: B) -> SendClientRequest
|
pub fn send_body<B>(self, body: B) -> SendClientRequest
|
||||||
where
|
where
|
||||||
B: MessageBody + 'static,
|
B: Into<AnyBody>,
|
||||||
{
|
{
|
||||||
if let Some(e) = self.err {
|
if let Some(e) = self.err {
|
||||||
return e.into();
|
return e.into();
|
||||||
|
205
awc/src/lib.rs
205
awc/src/lib.rs
@@ -105,11 +105,6 @@
|
|||||||
#![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")]
|
||||||
|
|
||||||
pub use actix_http::body;
|
|
||||||
|
|
||||||
#[cfg(feature = "cookies")]
|
|
||||||
pub use cookie;
|
|
||||||
|
|
||||||
mod any_body;
|
mod any_body;
|
||||||
mod builder;
|
mod builder;
|
||||||
mod client;
|
mod client;
|
||||||
@@ -118,27 +113,203 @@ pub mod error;
|
|||||||
mod frozen;
|
mod frozen;
|
||||||
pub mod middleware;
|
pub mod middleware;
|
||||||
mod request;
|
mod request;
|
||||||
mod responses;
|
mod response;
|
||||||
mod sender;
|
mod sender;
|
||||||
pub mod test;
|
pub mod test;
|
||||||
pub mod ws;
|
pub mod ws;
|
||||||
|
|
||||||
pub mod http {
|
// TODO: hmmmmmm
|
||||||
//! Various HTTP related types.
|
pub use actix_http as http;
|
||||||
|
#[cfg(feature = "cookies")]
|
||||||
// TODO: figure out how best to expose http::Error vs actix_http::Error
|
pub use cookie;
|
||||||
pub use actix_http::{
|
|
||||||
header, uri, ConnectionType, Error, Method, StatusCode, Uri, Version,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub use self::builder::ClientBuilder;
|
pub use self::builder::ClientBuilder;
|
||||||
pub use self::client::{Client, Connector};
|
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;
|
||||||
#[allow(deprecated)]
|
pub use self::response::{ClientResponse, JsonBody, MessageBody};
|
||||||
pub use self::responses::{ClientResponse, JsonBody, MessageBody, ResponseBody};
|
|
||||||
pub use self::sender::SendClientRequest;
|
pub use self::sender::SendClientRequest;
|
||||||
|
|
||||||
|
use std::{convert::TryFrom, rc::Rc, time::Duration};
|
||||||
|
|
||||||
|
use actix_http::{error::HttpError, header::HeaderMap, Method, RequestHead, Uri};
|
||||||
|
use actix_rt::net::TcpStream;
|
||||||
|
use actix_service::Service;
|
||||||
|
|
||||||
|
use self::client::{ConnectInfo, TcpConnectError, TcpConnection};
|
||||||
|
|
||||||
pub(crate) type BoxError = Box<dyn std::error::Error>;
|
pub(crate) type BoxError = Box<dyn std::error::Error>;
|
||||||
|
|
||||||
|
/// An asynchronous HTTP and WebSocket client.
|
||||||
|
///
|
||||||
|
/// You should take care to create, at most, one `Client` per thread. Otherwise, expect higher CPU
|
||||||
|
/// and memory usage.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```
|
||||||
|
/// use awc::Client;
|
||||||
|
///
|
||||||
|
/// #[actix_rt::main]
|
||||||
|
/// async fn main() {
|
||||||
|
/// let mut client = Client::default();
|
||||||
|
///
|
||||||
|
/// let res = client.get("http://www.rust-lang.org")
|
||||||
|
/// .insert_header(("User-Agent", "my-app/1.2"))
|
||||||
|
/// .send()
|
||||||
|
/// .await;
|
||||||
|
///
|
||||||
|
/// println!("Response: {:?}", res);
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Client(ClientConfig);
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub(crate) struct ClientConfig {
|
||||||
|
pub(crate) connector: BoxConnectorService,
|
||||||
|
pub(crate) default_headers: Rc<HeaderMap>,
|
||||||
|
pub(crate) timeout: Option<Duration>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Client {
|
||||||
|
fn default() -> Self {
|
||||||
|
ClientBuilder::new().finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Client {
|
||||||
|
/// Create new client instance with default settings.
|
||||||
|
pub fn new() -> Client {
|
||||||
|
Client::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create `Client` builder.
|
||||||
|
/// This function is equivalent of `ClientBuilder::new()`.
|
||||||
|
pub fn builder() -> ClientBuilder<
|
||||||
|
impl Service<
|
||||||
|
ConnectInfo<Uri>,
|
||||||
|
Response = TcpConnection<Uri, TcpStream>,
|
||||||
|
Error = TcpConnectError,
|
||||||
|
> + Clone,
|
||||||
|
> {
|
||||||
|
ClientBuilder::new()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct HTTP request.
|
||||||
|
pub fn request<U>(&self, method: Method, url: U) -> ClientRequest
|
||||||
|
where
|
||||||
|
Uri: TryFrom<U>,
|
||||||
|
<Uri as TryFrom<U>>::Error: Into<HttpError>,
|
||||||
|
{
|
||||||
|
let mut req = ClientRequest::new(method, url, self.0.clone());
|
||||||
|
|
||||||
|
for header in self.0.default_headers.iter() {
|
||||||
|
// header map is empty
|
||||||
|
// TODO: probably append instead
|
||||||
|
req = req.insert_header_if_none(header);
|
||||||
|
}
|
||||||
|
req
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create `ClientRequest` from `RequestHead`
|
||||||
|
///
|
||||||
|
/// It is useful for proxy requests. This implementation
|
||||||
|
/// copies all headers and the method.
|
||||||
|
pub fn request_from<U>(&self, url: U, head: &RequestHead) -> ClientRequest
|
||||||
|
where
|
||||||
|
Uri: TryFrom<U>,
|
||||||
|
<Uri as TryFrom<U>>::Error: Into<HttpError>,
|
||||||
|
{
|
||||||
|
let mut req = self.request(head.method.clone(), url);
|
||||||
|
for header in head.headers.iter() {
|
||||||
|
req = req.insert_header_if_none(header);
|
||||||
|
}
|
||||||
|
req
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct HTTP *GET* request.
|
||||||
|
pub fn get<U>(&self, url: U) -> ClientRequest
|
||||||
|
where
|
||||||
|
Uri: TryFrom<U>,
|
||||||
|
<Uri as TryFrom<U>>::Error: Into<HttpError>,
|
||||||
|
{
|
||||||
|
self.request(Method::GET, url)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct HTTP *HEAD* request.
|
||||||
|
pub fn head<U>(&self, url: U) -> ClientRequest
|
||||||
|
where
|
||||||
|
Uri: TryFrom<U>,
|
||||||
|
<Uri as TryFrom<U>>::Error: Into<HttpError>,
|
||||||
|
{
|
||||||
|
self.request(Method::HEAD, url)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct HTTP *PUT* request.
|
||||||
|
pub fn put<U>(&self, url: U) -> ClientRequest
|
||||||
|
where
|
||||||
|
Uri: TryFrom<U>,
|
||||||
|
<Uri as TryFrom<U>>::Error: Into<HttpError>,
|
||||||
|
{
|
||||||
|
self.request(Method::PUT, url)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct HTTP *POST* request.
|
||||||
|
pub fn post<U>(&self, url: U) -> ClientRequest
|
||||||
|
where
|
||||||
|
Uri: TryFrom<U>,
|
||||||
|
<Uri as TryFrom<U>>::Error: Into<HttpError>,
|
||||||
|
{
|
||||||
|
self.request(Method::POST, url)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct HTTP *PATCH* request.
|
||||||
|
pub fn patch<U>(&self, url: U) -> ClientRequest
|
||||||
|
where
|
||||||
|
Uri: TryFrom<U>,
|
||||||
|
<Uri as TryFrom<U>>::Error: Into<HttpError>,
|
||||||
|
{
|
||||||
|
self.request(Method::PATCH, url)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct HTTP *DELETE* request.
|
||||||
|
pub fn delete<U>(&self, url: U) -> ClientRequest
|
||||||
|
where
|
||||||
|
Uri: TryFrom<U>,
|
||||||
|
<Uri as TryFrom<U>>::Error: Into<HttpError>,
|
||||||
|
{
|
||||||
|
self.request(Method::DELETE, url)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct HTTP *OPTIONS* request.
|
||||||
|
pub fn options<U>(&self, url: U) -> ClientRequest
|
||||||
|
where
|
||||||
|
Uri: TryFrom<U>,
|
||||||
|
<Uri as TryFrom<U>>::Error: Into<HttpError>,
|
||||||
|
{
|
||||||
|
self.request(Method::OPTIONS, url)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initialize a WebSocket connection.
|
||||||
|
/// Returns a WebSocket connection builder.
|
||||||
|
pub fn ws<U>(&self, url: U) -> ws::WebsocketsRequest
|
||||||
|
where
|
||||||
|
Uri: TryFrom<U>,
|
||||||
|
<Uri as TryFrom<U>>::Error: Into<HttpError>,
|
||||||
|
{
|
||||||
|
let mut req = ws::WebsocketsRequest::new(url, self.0.clone());
|
||||||
|
for (key, value) in self.0.default_headers.iter() {
|
||||||
|
req.head.headers.insert(key.clone(), value.clone());
|
||||||
|
}
|
||||||
|
req
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get default HeaderMap of Client.
|
||||||
|
///
|
||||||
|
/// Returns Some(&mut HeaderMap) when Client object is unique
|
||||||
|
/// (No other clone of client exists at the same time).
|
||||||
|
pub fn headers(&mut self) -> Option<&mut HeaderMap> {
|
||||||
|
Rc::get_mut(&mut self.0.default_headers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -190,7 +190,9 @@ where
|
|||||||
let body_new = if is_redirect {
|
let body_new = if is_redirect {
|
||||||
// try to reuse body
|
// try to reuse body
|
||||||
match body {
|
match body {
|
||||||
Some(ref bytes) => AnyBody::from(bytes.clone()),
|
Some(ref bytes) => AnyBody::Bytes {
|
||||||
|
body: bytes.clone(),
|
||||||
|
},
|
||||||
// TODO: should this be AnyBody::Empty or AnyBody::None.
|
// TODO: should this be AnyBody::Empty or AnyBody::None.
|
||||||
_ => AnyBody::empty(),
|
_ => AnyBody::empty(),
|
||||||
}
|
}
|
||||||
|
@@ -5,18 +5,17 @@ use futures_core::Stream;
|
|||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
use actix_http::{
|
use actix_http::{
|
||||||
body::MessageBody,
|
|
||||||
error::HttpError,
|
error::HttpError,
|
||||||
header::{self, HeaderMap, HeaderValue, TryIntoHeaderPair},
|
header::{self, HeaderMap, HeaderValue, TryIntoHeaderPair},
|
||||||
ConnectionType, Method, RequestHead, Uri, Version,
|
ConnectionType, Method, RequestHead, Uri, Version,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
client::ClientConfig,
|
any_body::AnyBody,
|
||||||
error::{FreezeRequestError, InvalidUrl},
|
error::{FreezeRequestError, InvalidUrl},
|
||||||
frozen::FrozenClientRequest,
|
frozen::FrozenClientRequest,
|
||||||
sender::{PrepForSendingError, RequestSender, SendClientRequest},
|
sender::{PrepForSendingError, RequestSender, SendClientRequest},
|
||||||
BoxError,
|
BoxError, ClientConfig,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "cookies")]
|
#[cfg(feature = "cookies")]
|
||||||
@@ -27,9 +26,9 @@ use crate::cookie::{Cookie, CookieJar};
|
|||||||
/// This type can be used to construct an instance of `ClientRequest` through a
|
/// This type can be used to construct an instance of `ClientRequest` through a
|
||||||
/// builder-like pattern.
|
/// builder-like pattern.
|
||||||
///
|
///
|
||||||
/// ```no_run
|
/// ```
|
||||||
/// # #[actix_rt::main]
|
/// #[actix_rt::main]
|
||||||
/// # async fn main() {
|
/// async fn main() {
|
||||||
/// let response = awc::Client::new()
|
/// let response = awc::Client::new()
|
||||||
/// .get("http://www.rust-lang.org") // <- Create request builder
|
/// .get("http://www.rust-lang.org") // <- Create request builder
|
||||||
/// .insert_header(("User-Agent", "Actix-web"))
|
/// .insert_header(("User-Agent", "Actix-web"))
|
||||||
@@ -40,7 +39,7 @@ use crate::cookie::{Cookie, CookieJar};
|
|||||||
/// println!("Response: {:?}", response);
|
/// println!("Response: {:?}", response);
|
||||||
/// Ok(())
|
/// Ok(())
|
||||||
/// });
|
/// });
|
||||||
/// # }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub struct ClientRequest {
|
pub struct ClientRequest {
|
||||||
pub(crate) head: RequestHead,
|
pub(crate) head: RequestHead,
|
||||||
@@ -175,13 +174,17 @@ impl ClientRequest {
|
|||||||
|
|
||||||
/// Append a header, keeping any that were set with an equivalent field name.
|
/// Append a header, keeping any that were set with an equivalent field name.
|
||||||
///
|
///
|
||||||
/// ```no_run
|
/// ```
|
||||||
/// use awc::{http::header, Client};
|
/// # #[actix_rt::main]
|
||||||
|
/// # async fn main() {
|
||||||
|
/// # use awc::Client;
|
||||||
|
/// use awc::http::header::CONTENT_TYPE;
|
||||||
///
|
///
|
||||||
/// Client::new()
|
/// Client::new()
|
||||||
/// .get("http://www.rust-lang.org")
|
/// .get("http://www.rust-lang.org")
|
||||||
/// .insert_header(("X-TEST", "value"))
|
/// .insert_header(("X-TEST", "value"))
|
||||||
/// .insert_header((header::CONTENT_TYPE, mime::APPLICATION_JSON));
|
/// .insert_header((CONTENT_TYPE, mime::APPLICATION_JSON));
|
||||||
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn append_header(mut self, header: impl TryIntoHeaderPair) -> Self {
|
pub fn append_header(mut self, header: impl TryIntoHeaderPair) -> Self {
|
||||||
match header.try_into_pair() {
|
match header.try_into_pair() {
|
||||||
@@ -249,18 +252,23 @@ impl ClientRequest {
|
|||||||
|
|
||||||
/// Set a cookie
|
/// Set a cookie
|
||||||
///
|
///
|
||||||
/// ```no_run
|
/// ```
|
||||||
/// use awc::{cookie::Cookie, Client};
|
/// #[actix_rt::main]
|
||||||
///
|
/// async fn main() {
|
||||||
/// # #[actix_rt::main]
|
/// let resp = awc::Client::new().get("https://www.rust-lang.org")
|
||||||
/// # async fn main() {
|
/// .cookie(
|
||||||
/// let res = Client::new().get("https://httpbin.org/cookies")
|
/// awc::cookie::Cookie::build("name", "value")
|
||||||
/// .cookie(Cookie::new("name", "value"))
|
/// .domain("www.rust-lang.org")
|
||||||
|
/// .path("/")
|
||||||
|
/// .secure(true)
|
||||||
|
/// .http_only(true)
|
||||||
|
/// .finish(),
|
||||||
|
/// )
|
||||||
/// .send()
|
/// .send()
|
||||||
/// .await;
|
/// .await;
|
||||||
///
|
///
|
||||||
/// println!("Response: {:?}", res);
|
/// println!("Response: {:?}", resp);
|
||||||
/// # }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(feature = "cookies")]
|
#[cfg(feature = "cookies")]
|
||||||
pub fn cookie(mut self, cookie: Cookie<'_>) -> Self {
|
pub fn cookie(mut self, cookie: Cookie<'_>) -> Self {
|
||||||
@@ -332,7 +340,7 @@ impl ClientRequest {
|
|||||||
/// Complete request construction and send body.
|
/// Complete request construction and send body.
|
||||||
pub fn send_body<B>(self, body: B) -> SendClientRequest
|
pub fn send_body<B>(self, body: B) -> SendClientRequest
|
||||||
where
|
where
|
||||||
B: MessageBody + 'static,
|
B: Into<AnyBody>,
|
||||||
{
|
{
|
||||||
let slf = match self.prep_for_sending() {
|
let slf = match self.prep_for_sending() {
|
||||||
Ok(slf) => slf,
|
Ok(slf) => slf,
|
||||||
|
556
awc/src/response.rs
Normal file
556
awc/src/response.rs
Normal file
@@ -0,0 +1,556 @@
|
|||||||
|
use std::{
|
||||||
|
cell::{Ref, RefMut},
|
||||||
|
fmt,
|
||||||
|
future::Future,
|
||||||
|
io,
|
||||||
|
marker::PhantomData,
|
||||||
|
pin::Pin,
|
||||||
|
task::{Context, Poll},
|
||||||
|
time::{Duration, Instant},
|
||||||
|
};
|
||||||
|
|
||||||
|
use actix_http::{
|
||||||
|
error::PayloadError, header, header::HeaderMap, Extensions, HttpMessage, Payload,
|
||||||
|
PayloadStream, ResponseHead, StatusCode, Version,
|
||||||
|
};
|
||||||
|
use actix_rt::time::{sleep, Sleep};
|
||||||
|
use bytes::{Bytes, BytesMut};
|
||||||
|
use futures_core::{ready, Stream};
|
||||||
|
use serde::de::DeserializeOwned;
|
||||||
|
|
||||||
|
#[cfg(feature = "cookies")]
|
||||||
|
use crate::cookie::{Cookie, ParseError as CookieParseError};
|
||||||
|
use crate::error::JsonPayloadError;
|
||||||
|
|
||||||
|
/// Client Response
|
||||||
|
pub struct ClientResponse<S = PayloadStream> {
|
||||||
|
pub(crate) head: ResponseHead,
|
||||||
|
pub(crate) payload: Payload<S>,
|
||||||
|
pub(crate) timeout: ResponseTimeout,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// helper enum with reusable sleep passed from `SendClientResponse`.
|
||||||
|
/// See `ClientResponse::_timeout` for reason.
|
||||||
|
pub(crate) enum ResponseTimeout {
|
||||||
|
Disabled(Option<Pin<Box<Sleep>>>),
|
||||||
|
Enabled(Pin<Box<Sleep>>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ResponseTimeout {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::Disabled(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ResponseTimeout {
|
||||||
|
fn poll_timeout(&mut self, cx: &mut Context<'_>) -> Result<(), PayloadError> {
|
||||||
|
match *self {
|
||||||
|
Self::Enabled(ref mut timeout) => {
|
||||||
|
if timeout.as_mut().poll(cx).is_ready() {
|
||||||
|
Err(PayloadError::Io(io::Error::new(
|
||||||
|
io::ErrorKind::TimedOut,
|
||||||
|
"Response Payload IO timed out",
|
||||||
|
)))
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self::Disabled(_) => Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S> HttpMessage for ClientResponse<S> {
|
||||||
|
type Stream = S;
|
||||||
|
|
||||||
|
fn headers(&self) -> &HeaderMap {
|
||||||
|
&self.head.headers
|
||||||
|
}
|
||||||
|
|
||||||
|
fn take_payload(&mut self) -> Payload<S> {
|
||||||
|
std::mem::replace(&mut self.payload, Payload::None)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extensions(&self) -> Ref<'_, Extensions> {
|
||||||
|
self.head.extensions()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extensions_mut(&self) -> RefMut<'_, Extensions> {
|
||||||
|
self.head.extensions_mut()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S> ClientResponse<S> {
|
||||||
|
/// Create new Request instance
|
||||||
|
pub(crate) fn new(head: ResponseHead, payload: Payload<S>) -> Self {
|
||||||
|
ClientResponse {
|
||||||
|
head,
|
||||||
|
payload,
|
||||||
|
timeout: ResponseTimeout::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn head(&self) -> &ResponseHead {
|
||||||
|
&self.head
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read the Request Version.
|
||||||
|
#[inline]
|
||||||
|
pub fn version(&self) -> Version {
|
||||||
|
self.head().version
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the status from the server.
|
||||||
|
#[inline]
|
||||||
|
pub fn status(&self) -> StatusCode {
|
||||||
|
self.head().status
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
/// Returns request's headers.
|
||||||
|
pub fn headers(&self) -> &HeaderMap {
|
||||||
|
&self.head().headers
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set a body and return previous body value
|
||||||
|
pub fn map_body<F, U>(mut self, f: F) -> ClientResponse<U>
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut ResponseHead, Payload<S>) -> Payload<U>,
|
||||||
|
{
|
||||||
|
let payload = f(&mut self.head, self.payload);
|
||||||
|
|
||||||
|
ClientResponse {
|
||||||
|
payload,
|
||||||
|
head: self.head,
|
||||||
|
timeout: self.timeout,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set a timeout duration for [`ClientResponse`](self::ClientResponse).
|
||||||
|
///
|
||||||
|
/// This duration covers the duration of processing the response body stream
|
||||||
|
/// and would end it as timeout error when deadline met.
|
||||||
|
///
|
||||||
|
/// Disabled by default.
|
||||||
|
pub fn timeout(self, dur: Duration) -> Self {
|
||||||
|
let timeout = match self.timeout {
|
||||||
|
ResponseTimeout::Disabled(Some(mut timeout))
|
||||||
|
| ResponseTimeout::Enabled(mut timeout) => match Instant::now().checked_add(dur) {
|
||||||
|
Some(deadline) => {
|
||||||
|
timeout.as_mut().reset(deadline.into());
|
||||||
|
ResponseTimeout::Enabled(timeout)
|
||||||
|
}
|
||||||
|
None => ResponseTimeout::Enabled(Box::pin(sleep(dur))),
|
||||||
|
},
|
||||||
|
_ => ResponseTimeout::Enabled(Box::pin(sleep(dur))),
|
||||||
|
};
|
||||||
|
|
||||||
|
Self {
|
||||||
|
payload: self.payload,
|
||||||
|
head: self.head,
|
||||||
|
timeout,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This method does not enable timeout. It's used to pass the boxed `Sleep` from
|
||||||
|
/// `SendClientRequest` and reuse it's heap allocation together with it's slot in
|
||||||
|
/// timer wheel.
|
||||||
|
pub(crate) fn _timeout(mut self, timeout: Option<Pin<Box<Sleep>>>) -> Self {
|
||||||
|
self.timeout = ResponseTimeout::Disabled(timeout);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Load request cookies.
|
||||||
|
#[cfg(feature = "cookies")]
|
||||||
|
pub fn cookies(&self) -> Result<Ref<'_, Vec<Cookie<'static>>>, CookieParseError> {
|
||||||
|
struct Cookies(Vec<Cookie<'static>>);
|
||||||
|
|
||||||
|
if self.extensions().get::<Cookies>().is_none() {
|
||||||
|
let mut cookies = Vec::new();
|
||||||
|
for hdr in self.headers().get_all(&header::SET_COOKIE) {
|
||||||
|
let s = std::str::from_utf8(hdr.as_bytes()).map_err(CookieParseError::from)?;
|
||||||
|
cookies.push(Cookie::parse_encoded(s)?.into_owned());
|
||||||
|
}
|
||||||
|
self.extensions_mut().insert(Cookies(cookies));
|
||||||
|
}
|
||||||
|
Ok(Ref::map(self.extensions(), |ext| {
|
||||||
|
&ext.get::<Cookies>().unwrap().0
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return request cookie.
|
||||||
|
#[cfg(feature = "cookies")]
|
||||||
|
pub fn cookie(&self, name: &str) -> Option<Cookie<'static>> {
|
||||||
|
if let Ok(cookies) = self.cookies() {
|
||||||
|
for cookie in cookies.iter() {
|
||||||
|
if cookie.name() == name {
|
||||||
|
return Some(cookie.to_owned());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S> ClientResponse<S>
|
||||||
|
where
|
||||||
|
S: Stream<Item = Result<Bytes, PayloadError>>,
|
||||||
|
{
|
||||||
|
/// Loads HTTP response's body.
|
||||||
|
pub fn body(&mut self) -> MessageBody<S> {
|
||||||
|
MessageBody::new(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Loads and parse `application/json` encoded body.
|
||||||
|
/// Return `JsonBody<T>` future. It resolves to a `T` value.
|
||||||
|
///
|
||||||
|
/// Returns error:
|
||||||
|
///
|
||||||
|
/// * content type is not `application/json`
|
||||||
|
/// * content length is greater than 256k
|
||||||
|
pub fn json<T: DeserializeOwned>(&mut self) -> JsonBody<S, T> {
|
||||||
|
JsonBody::new(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S> Stream for ClientResponse<S>
|
||||||
|
where
|
||||||
|
S: Stream<Item = Result<Bytes, PayloadError>> + Unpin,
|
||||||
|
{
|
||||||
|
type Item = Result<Bytes, PayloadError>;
|
||||||
|
|
||||||
|
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||||
|
let this = self.get_mut();
|
||||||
|
this.timeout.poll_timeout(cx)?;
|
||||||
|
|
||||||
|
Pin::new(&mut this.payload).poll_next(cx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S> fmt::Debug for ClientResponse<S> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
writeln!(f, "\nClientResponse {:?} {}", self.version(), self.status(),)?;
|
||||||
|
writeln!(f, " headers:")?;
|
||||||
|
for (key, val) in self.headers().iter() {
|
||||||
|
writeln!(f, " {:?}: {:?}", key, val)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const DEFAULT_BODY_LIMIT: usize = 2 * 1024 * 1024;
|
||||||
|
|
||||||
|
/// Future that resolves to a complete HTTP message body.
|
||||||
|
pub struct MessageBody<S> {
|
||||||
|
length: Option<usize>,
|
||||||
|
timeout: ResponseTimeout,
|
||||||
|
body: Result<ReadBody<S>, Option<PayloadError>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S> MessageBody<S>
|
||||||
|
where
|
||||||
|
S: Stream<Item = Result<Bytes, PayloadError>>,
|
||||||
|
{
|
||||||
|
/// Create `MessageBody` for request.
|
||||||
|
pub fn new(res: &mut ClientResponse<S>) -> MessageBody<S> {
|
||||||
|
let length = match res.headers().get(&header::CONTENT_LENGTH) {
|
||||||
|
Some(value) => {
|
||||||
|
let len = value.to_str().ok().and_then(|s| s.parse::<usize>().ok());
|
||||||
|
|
||||||
|
match len {
|
||||||
|
None => return Self::err(PayloadError::UnknownLength),
|
||||||
|
len => len,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
MessageBody {
|
||||||
|
length,
|
||||||
|
timeout: std::mem::take(&mut res.timeout),
|
||||||
|
body: Ok(ReadBody::new(res.take_payload(), DEFAULT_BODY_LIMIT)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Change max size of payload. By default max size is 2048kB
|
||||||
|
pub fn limit(mut self, limit: usize) -> Self {
|
||||||
|
if let Ok(ref mut body) = self.body {
|
||||||
|
body.limit = limit;
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn err(e: PayloadError) -> Self {
|
||||||
|
MessageBody {
|
||||||
|
length: None,
|
||||||
|
timeout: ResponseTimeout::default(),
|
||||||
|
body: Err(Some(e)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S> Future for MessageBody<S>
|
||||||
|
where
|
||||||
|
S: Stream<Item = Result<Bytes, PayloadError>> + Unpin,
|
||||||
|
{
|
||||||
|
type Output = Result<Bytes, PayloadError>;
|
||||||
|
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
let this = self.get_mut();
|
||||||
|
|
||||||
|
match this.body {
|
||||||
|
Err(ref mut err) => Poll::Ready(Err(err.take().unwrap())),
|
||||||
|
Ok(ref mut body) => {
|
||||||
|
if let Some(len) = this.length.take() {
|
||||||
|
if len > body.limit {
|
||||||
|
return Poll::Ready(Err(PayloadError::Overflow));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.timeout.poll_timeout(cx)?;
|
||||||
|
|
||||||
|
Pin::new(body).poll(cx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Response's payload json parser, it resolves to a deserialized `T` value.
|
||||||
|
///
|
||||||
|
/// Returns error:
|
||||||
|
///
|
||||||
|
/// * content type is not `application/json`
|
||||||
|
/// * content length is greater than 64k
|
||||||
|
pub struct JsonBody<S, U> {
|
||||||
|
length: Option<usize>,
|
||||||
|
err: Option<JsonPayloadError>,
|
||||||
|
timeout: ResponseTimeout,
|
||||||
|
fut: Option<ReadBody<S>>,
|
||||||
|
_phantom: PhantomData<U>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S, U> JsonBody<S, U>
|
||||||
|
where
|
||||||
|
S: Stream<Item = Result<Bytes, PayloadError>>,
|
||||||
|
U: DeserializeOwned,
|
||||||
|
{
|
||||||
|
/// Create `JsonBody` for request.
|
||||||
|
pub fn new(res: &mut ClientResponse<S>) -> Self {
|
||||||
|
// check content-type
|
||||||
|
let json = if let Ok(Some(mime)) = res.mime_type() {
|
||||||
|
mime.subtype() == mime::JSON || mime.suffix() == Some(mime::JSON)
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
if !json {
|
||||||
|
return JsonBody {
|
||||||
|
length: None,
|
||||||
|
fut: None,
|
||||||
|
timeout: ResponseTimeout::default(),
|
||||||
|
err: Some(JsonPayloadError::ContentType),
|
||||||
|
_phantom: PhantomData,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut len = None;
|
||||||
|
|
||||||
|
if let Some(l) = res.headers().get(&header::CONTENT_LENGTH) {
|
||||||
|
if let Ok(s) = l.to_str() {
|
||||||
|
if let Ok(l) = s.parse::<usize>() {
|
||||||
|
len = Some(l)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonBody {
|
||||||
|
length: len,
|
||||||
|
err: None,
|
||||||
|
timeout: std::mem::take(&mut res.timeout),
|
||||||
|
fut: Some(ReadBody::new(res.take_payload(), 65536)),
|
||||||
|
_phantom: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Change max size of payload. By default max size is 64kB
|
||||||
|
pub fn limit(mut self, limit: usize) -> Self {
|
||||||
|
if let Some(ref mut fut) = self.fut {
|
||||||
|
fut.limit = limit;
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, U> Unpin for JsonBody<T, U>
|
||||||
|
where
|
||||||
|
T: Stream<Item = Result<Bytes, PayloadError>> + Unpin,
|
||||||
|
U: DeserializeOwned,
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, U> Future for JsonBody<T, U>
|
||||||
|
where
|
||||||
|
T: Stream<Item = Result<Bytes, PayloadError>> + Unpin,
|
||||||
|
U: DeserializeOwned,
|
||||||
|
{
|
||||||
|
type Output = Result<U, JsonPayloadError>;
|
||||||
|
|
||||||
|
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
if let Some(err) = self.err.take() {
|
||||||
|
return Poll::Ready(Err(err));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(len) = self.length.take() {
|
||||||
|
if len > self.fut.as_ref().unwrap().limit {
|
||||||
|
return Poll::Ready(Err(JsonPayloadError::Payload(PayloadError::Overflow)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.timeout
|
||||||
|
.poll_timeout(cx)
|
||||||
|
.map_err(JsonPayloadError::Payload)?;
|
||||||
|
|
||||||
|
let body = ready!(Pin::new(&mut self.get_mut().fut.as_mut().unwrap()).poll(cx))?;
|
||||||
|
Poll::Ready(serde_json::from_slice::<U>(&body).map_err(JsonPayloadError::from))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ReadBody<S> {
|
||||||
|
stream: Payload<S>,
|
||||||
|
buf: BytesMut,
|
||||||
|
limit: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S> ReadBody<S> {
|
||||||
|
fn new(stream: Payload<S>, limit: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
stream,
|
||||||
|
buf: BytesMut::new(),
|
||||||
|
limit,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S> Future for ReadBody<S>
|
||||||
|
where
|
||||||
|
S: Stream<Item = Result<Bytes, PayloadError>> + Unpin,
|
||||||
|
{
|
||||||
|
type Output = Result<Bytes, PayloadError>;
|
||||||
|
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
let this = self.get_mut();
|
||||||
|
|
||||||
|
while let Some(chunk) = ready!(Pin::new(&mut this.stream).poll_next(cx)?) {
|
||||||
|
if (this.buf.len() + chunk.len()) > this.limit {
|
||||||
|
return Poll::Ready(Err(PayloadError::Overflow));
|
||||||
|
}
|
||||||
|
this.buf.extend_from_slice(&chunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
Poll::Ready(Ok(this.buf.split().freeze()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::{http::header, test::TestResponse};
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn test_body() {
|
||||||
|
let mut req = TestResponse::with_header((header::CONTENT_LENGTH, "xxxx")).finish();
|
||||||
|
match req.body().await.err().unwrap() {
|
||||||
|
PayloadError::UnknownLength => {}
|
||||||
|
_ => unreachable!("error"),
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut req = TestResponse::with_header((header::CONTENT_LENGTH, "10000000")).finish();
|
||||||
|
match req.body().await.err().unwrap() {
|
||||||
|
PayloadError::Overflow => {}
|
||||||
|
_ => unreachable!("error"),
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut req = TestResponse::default()
|
||||||
|
.set_payload(Bytes::from_static(b"test"))
|
||||||
|
.finish();
|
||||||
|
assert_eq!(req.body().await.ok().unwrap(), Bytes::from_static(b"test"));
|
||||||
|
|
||||||
|
let mut req = TestResponse::default()
|
||||||
|
.set_payload(Bytes::from_static(b"11111111111111"))
|
||||||
|
.finish();
|
||||||
|
match req.body().limit(5).await.err().unwrap() {
|
||||||
|
PayloadError::Overflow => {}
|
||||||
|
_ => unreachable!("error"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
||||||
|
struct MyObject {
|
||||||
|
name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn json_eq(err: JsonPayloadError, other: JsonPayloadError) -> bool {
|
||||||
|
match err {
|
||||||
|
JsonPayloadError::Payload(PayloadError::Overflow) => {
|
||||||
|
matches!(other, JsonPayloadError::Payload(PayloadError::Overflow))
|
||||||
|
}
|
||||||
|
JsonPayloadError::ContentType => matches!(other, JsonPayloadError::ContentType),
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn test_json_body() {
|
||||||
|
let mut req = TestResponse::default().finish();
|
||||||
|
let json = JsonBody::<_, MyObject>::new(&mut req).await;
|
||||||
|
assert!(json_eq(json.err().unwrap(), JsonPayloadError::ContentType));
|
||||||
|
|
||||||
|
let mut req = TestResponse::default()
|
||||||
|
.insert_header((
|
||||||
|
header::CONTENT_TYPE,
|
||||||
|
header::HeaderValue::from_static("application/text"),
|
||||||
|
))
|
||||||
|
.finish();
|
||||||
|
let json = JsonBody::<_, MyObject>::new(&mut req).await;
|
||||||
|
assert!(json_eq(json.err().unwrap(), JsonPayloadError::ContentType));
|
||||||
|
|
||||||
|
let mut req = TestResponse::default()
|
||||||
|
.insert_header((
|
||||||
|
header::CONTENT_TYPE,
|
||||||
|
header::HeaderValue::from_static("application/json"),
|
||||||
|
))
|
||||||
|
.insert_header((
|
||||||
|
header::CONTENT_LENGTH,
|
||||||
|
header::HeaderValue::from_static("10000"),
|
||||||
|
))
|
||||||
|
.finish();
|
||||||
|
|
||||||
|
let json = JsonBody::<_, MyObject>::new(&mut req).limit(100).await;
|
||||||
|
assert!(json_eq(
|
||||||
|
json.err().unwrap(),
|
||||||
|
JsonPayloadError::Payload(PayloadError::Overflow)
|
||||||
|
));
|
||||||
|
|
||||||
|
let mut req = TestResponse::default()
|
||||||
|
.insert_header((
|
||||||
|
header::CONTENT_TYPE,
|
||||||
|
header::HeaderValue::from_static("application/json"),
|
||||||
|
))
|
||||||
|
.insert_header((
|
||||||
|
header::CONTENT_LENGTH,
|
||||||
|
header::HeaderValue::from_static("16"),
|
||||||
|
))
|
||||||
|
.set_payload(Bytes::from_static(b"{\"name\": \"test\"}"))
|
||||||
|
.finish();
|
||||||
|
|
||||||
|
let json = JsonBody::<_, MyObject>::new(&mut req).await;
|
||||||
|
assert_eq!(
|
||||||
|
json.ok().unwrap(),
|
||||||
|
MyObject {
|
||||||
|
name: "test".to_owned()
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@@ -1,192 +0,0 @@
|
|||||||
use std::{
|
|
||||||
future::Future,
|
|
||||||
marker::PhantomData,
|
|
||||||
mem,
|
|
||||||
pin::Pin,
|
|
||||||
task::{Context, Poll},
|
|
||||||
};
|
|
||||||
|
|
||||||
use actix_http::{error::PayloadError, header, HttpMessage};
|
|
||||||
use bytes::Bytes;
|
|
||||||
use futures_core::{ready, Stream};
|
|
||||||
use pin_project_lite::pin_project;
|
|
||||||
use serde::de::DeserializeOwned;
|
|
||||||
|
|
||||||
use super::{read_body::ReadBody, ResponseTimeout, DEFAULT_BODY_LIMIT};
|
|
||||||
use crate::{error::JsonPayloadError, ClientResponse};
|
|
||||||
|
|
||||||
pin_project! {
|
|
||||||
/// A `Future` that reads a body stream, parses JSON, resolving to a deserialized `T`.
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
/// `Future` implementation returns error if:
|
|
||||||
/// - content type is not `application/json`;
|
|
||||||
/// - content length is greater than [limit](JsonBody::limit) (default: 2 MiB).
|
|
||||||
pub struct JsonBody<S, T> {
|
|
||||||
#[pin]
|
|
||||||
body: Option<ReadBody<S>>,
|
|
||||||
length: Option<usize>,
|
|
||||||
timeout: ResponseTimeout,
|
|
||||||
err: Option<JsonPayloadError>,
|
|
||||||
_phantom: PhantomData<T>,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S, T> JsonBody<S, T>
|
|
||||||
where
|
|
||||||
S: Stream<Item = Result<Bytes, PayloadError>>,
|
|
||||||
T: DeserializeOwned,
|
|
||||||
{
|
|
||||||
/// Creates a JSON body stream reader from a response by taking its payload.
|
|
||||||
pub fn new(res: &mut ClientResponse<S>) -> Self {
|
|
||||||
// check content-type
|
|
||||||
let json = if let Ok(Some(mime)) = res.mime_type() {
|
|
||||||
mime.subtype() == mime::JSON || mime.suffix() == Some(mime::JSON)
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
};
|
|
||||||
|
|
||||||
if !json {
|
|
||||||
return JsonBody {
|
|
||||||
length: None,
|
|
||||||
body: None,
|
|
||||||
timeout: ResponseTimeout::default(),
|
|
||||||
err: Some(JsonPayloadError::ContentType),
|
|
||||||
_phantom: PhantomData,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
let length = res
|
|
||||||
.headers()
|
|
||||||
.get(&header::CONTENT_LENGTH)
|
|
||||||
.and_then(|len_hdr| len_hdr.to_str().ok())
|
|
||||||
.and_then(|len_str| len_str.parse::<usize>().ok());
|
|
||||||
|
|
||||||
JsonBody {
|
|
||||||
body: Some(ReadBody::new(res.take_payload(), DEFAULT_BODY_LIMIT)),
|
|
||||||
length,
|
|
||||||
timeout: mem::take(&mut res.timeout),
|
|
||||||
err: None,
|
|
||||||
_phantom: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Change max size of payload. Default limit is 2 MiB.
|
|
||||||
pub fn limit(mut self, limit: usize) -> Self {
|
|
||||||
if let Some(ref mut fut) = self.body {
|
|
||||||
fut.limit = limit;
|
|
||||||
}
|
|
||||||
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S, T> Future for JsonBody<S, T>
|
|
||||||
where
|
|
||||||
S: Stream<Item = Result<Bytes, PayloadError>>,
|
|
||||||
T: DeserializeOwned,
|
|
||||||
{
|
|
||||||
type Output = Result<T, JsonPayloadError>;
|
|
||||||
|
|
||||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
|
||||||
let this = self.project();
|
|
||||||
|
|
||||||
if let Some(err) = this.err.take() {
|
|
||||||
return Poll::Ready(Err(err));
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(len) = this.length.take() {
|
|
||||||
let body = Option::as_ref(&this.body).unwrap();
|
|
||||||
if len > body.limit {
|
|
||||||
return Poll::Ready(Err(JsonPayloadError::Payload(PayloadError::Overflow)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.timeout
|
|
||||||
.poll_timeout(cx)
|
|
||||||
.map_err(JsonPayloadError::Payload)?;
|
|
||||||
|
|
||||||
let body = ready!(this.body.as_pin_mut().unwrap().poll(cx))?;
|
|
||||||
Poll::Ready(serde_json::from_slice::<T>(&body).map_err(JsonPayloadError::from))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use actix_http::BoxedPayloadStream;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use static_assertions::assert_impl_all;
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
use crate::{http::header, test::TestResponse};
|
|
||||||
|
|
||||||
assert_impl_all!(JsonBody<BoxedPayloadStream, String>: Unpin);
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
|
||||||
struct MyObject {
|
|
||||||
name: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn json_eq(err: JsonPayloadError, other: JsonPayloadError) -> bool {
|
|
||||||
match err {
|
|
||||||
JsonPayloadError::Payload(PayloadError::Overflow) => {
|
|
||||||
matches!(other, JsonPayloadError::Payload(PayloadError::Overflow))
|
|
||||||
}
|
|
||||||
JsonPayloadError::ContentType => matches!(other, JsonPayloadError::ContentType),
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[actix_rt::test]
|
|
||||||
async fn read_json_body() {
|
|
||||||
let mut req = TestResponse::default().finish();
|
|
||||||
let json = JsonBody::<_, MyObject>::new(&mut req).await;
|
|
||||||
assert!(json_eq(json.err().unwrap(), JsonPayloadError::ContentType));
|
|
||||||
|
|
||||||
let mut req = TestResponse::default()
|
|
||||||
.insert_header((
|
|
||||||
header::CONTENT_TYPE,
|
|
||||||
header::HeaderValue::from_static("application/text"),
|
|
||||||
))
|
|
||||||
.finish();
|
|
||||||
let json = JsonBody::<_, MyObject>::new(&mut req).await;
|
|
||||||
assert!(json_eq(json.err().unwrap(), JsonPayloadError::ContentType));
|
|
||||||
|
|
||||||
let mut req = TestResponse::default()
|
|
||||||
.insert_header((
|
|
||||||
header::CONTENT_TYPE,
|
|
||||||
header::HeaderValue::from_static("application/json"),
|
|
||||||
))
|
|
||||||
.insert_header((
|
|
||||||
header::CONTENT_LENGTH,
|
|
||||||
header::HeaderValue::from_static("10000"),
|
|
||||||
))
|
|
||||||
.finish();
|
|
||||||
|
|
||||||
let json = JsonBody::<_, MyObject>::new(&mut req).limit(100).await;
|
|
||||||
assert!(json_eq(
|
|
||||||
json.err().unwrap(),
|
|
||||||
JsonPayloadError::Payload(PayloadError::Overflow)
|
|
||||||
));
|
|
||||||
|
|
||||||
let mut req = TestResponse::default()
|
|
||||||
.insert_header((
|
|
||||||
header::CONTENT_TYPE,
|
|
||||||
header::HeaderValue::from_static("application/json"),
|
|
||||||
))
|
|
||||||
.insert_header((
|
|
||||||
header::CONTENT_LENGTH,
|
|
||||||
header::HeaderValue::from_static("16"),
|
|
||||||
))
|
|
||||||
.set_payload(Bytes::from_static(b"{\"name\": \"test\"}"))
|
|
||||||
.finish();
|
|
||||||
|
|
||||||
let json = JsonBody::<_, MyObject>::new(&mut req).await;
|
|
||||||
assert_eq!(
|
|
||||||
json.ok().unwrap(),
|
|
||||||
MyObject {
|
|
||||||
name: "test".to_owned()
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,49 +0,0 @@
|
|||||||
use std::{future::Future, io, pin::Pin, task::Context};
|
|
||||||
|
|
||||||
use actix_http::error::PayloadError;
|
|
||||||
use actix_rt::time::Sleep;
|
|
||||||
|
|
||||||
mod json_body;
|
|
||||||
mod read_body;
|
|
||||||
mod response;
|
|
||||||
mod response_body;
|
|
||||||
|
|
||||||
pub use self::json_body::JsonBody;
|
|
||||||
pub use self::response::ClientResponse;
|
|
||||||
#[allow(deprecated)]
|
|
||||||
pub use self::response_body::{MessageBody, ResponseBody};
|
|
||||||
|
|
||||||
/// Default body size limit: 2 MiB
|
|
||||||
const DEFAULT_BODY_LIMIT: usize = 2 * 1024 * 1024;
|
|
||||||
|
|
||||||
/// Helper enum with reusable sleep passed from `SendClientResponse`.
|
|
||||||
///
|
|
||||||
/// See [`ClientResponse::_timeout`] for reason.
|
|
||||||
pub(crate) enum ResponseTimeout {
|
|
||||||
Disabled(Option<Pin<Box<Sleep>>>),
|
|
||||||
Enabled(Pin<Box<Sleep>>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for ResponseTimeout {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::Disabled(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ResponseTimeout {
|
|
||||||
fn poll_timeout(&mut self, cx: &mut Context<'_>) -> Result<(), PayloadError> {
|
|
||||||
match *self {
|
|
||||||
Self::Enabled(ref mut timeout) => {
|
|
||||||
if timeout.as_mut().poll(cx).is_ready() {
|
|
||||||
Err(PayloadError::Io(io::Error::new(
|
|
||||||
io::ErrorKind::TimedOut,
|
|
||||||
"Response Payload IO timed out",
|
|
||||||
)))
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Self::Disabled(_) => Ok(()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,61 +0,0 @@
|
|||||||
use std::{
|
|
||||||
future::Future,
|
|
||||||
pin::Pin,
|
|
||||||
task::{Context, Poll},
|
|
||||||
};
|
|
||||||
|
|
||||||
use actix_http::{error::PayloadError, Payload};
|
|
||||||
use bytes::{Bytes, BytesMut};
|
|
||||||
use futures_core::{ready, Stream};
|
|
||||||
use pin_project_lite::pin_project;
|
|
||||||
|
|
||||||
pin_project! {
|
|
||||||
pub(crate) struct ReadBody<S> {
|
|
||||||
#[pin]
|
|
||||||
pub(crate) stream: Payload<S>,
|
|
||||||
pub(crate) buf: BytesMut,
|
|
||||||
pub(crate) limit: usize,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S> ReadBody<S> {
|
|
||||||
pub(crate) fn new(stream: Payload<S>, limit: usize) -> Self {
|
|
||||||
Self {
|
|
||||||
stream,
|
|
||||||
buf: BytesMut::new(),
|
|
||||||
limit,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S> Future for ReadBody<S>
|
|
||||||
where
|
|
||||||
S: Stream<Item = Result<Bytes, PayloadError>>,
|
|
||||||
{
|
|
||||||
type Output = Result<Bytes, PayloadError>;
|
|
||||||
|
|
||||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
|
||||||
let mut this = self.project();
|
|
||||||
|
|
||||||
while let Some(chunk) = ready!(this.stream.as_mut().poll_next(cx)?) {
|
|
||||||
if (this.buf.len() + chunk.len()) > *this.limit {
|
|
||||||
return Poll::Ready(Err(PayloadError::Overflow));
|
|
||||||
}
|
|
||||||
|
|
||||||
this.buf.extend_from_slice(&chunk);
|
|
||||||
}
|
|
||||||
|
|
||||||
Poll::Ready(Ok(this.buf.split().freeze()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use static_assertions::assert_impl_all;
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
use crate::any_body::AnyBody;
|
|
||||||
|
|
||||||
assert_impl_all!(ReadBody<()>: Unpin);
|
|
||||||
assert_impl_all!(ReadBody<AnyBody>: Unpin);
|
|
||||||
}
|
|
@@ -1,257 +0,0 @@
|
|||||||
use std::{
|
|
||||||
cell::{Ref, RefMut},
|
|
||||||
fmt, mem,
|
|
||||||
pin::Pin,
|
|
||||||
task::{Context, Poll},
|
|
||||||
time::{Duration, Instant},
|
|
||||||
};
|
|
||||||
|
|
||||||
use actix_http::{
|
|
||||||
error::PayloadError, header, header::HeaderMap, BoxedPayloadStream, Extensions,
|
|
||||||
HttpMessage, Payload, ResponseHead, StatusCode, Version,
|
|
||||||
};
|
|
||||||
use actix_rt::time::{sleep, Sleep};
|
|
||||||
use bytes::Bytes;
|
|
||||||
use futures_core::Stream;
|
|
||||||
use pin_project_lite::pin_project;
|
|
||||||
use serde::de::DeserializeOwned;
|
|
||||||
|
|
||||||
#[cfg(feature = "cookies")]
|
|
||||||
use crate::cookie::{Cookie, ParseError as CookieParseError};
|
|
||||||
|
|
||||||
use super::{JsonBody, ResponseBody, ResponseTimeout};
|
|
||||||
|
|
||||||
pin_project! {
|
|
||||||
/// Client Response
|
|
||||||
pub struct ClientResponse<S = BoxedPayloadStream> {
|
|
||||||
pub(crate) head: ResponseHead,
|
|
||||||
#[pin]
|
|
||||||
pub(crate) payload: Payload<S>,
|
|
||||||
pub(crate) timeout: ResponseTimeout,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S> ClientResponse<S> {
|
|
||||||
/// Create new Request instance
|
|
||||||
pub(crate) fn new(head: ResponseHead, payload: Payload<S>) -> Self {
|
|
||||||
ClientResponse {
|
|
||||||
head,
|
|
||||||
payload,
|
|
||||||
timeout: ResponseTimeout::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub(crate) fn head(&self) -> &ResponseHead {
|
|
||||||
&self.head
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read the Request Version.
|
|
||||||
#[inline]
|
|
||||||
pub fn version(&self) -> Version {
|
|
||||||
self.head().version
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the status from the server.
|
|
||||||
#[inline]
|
|
||||||
pub fn status(&self) -> StatusCode {
|
|
||||||
self.head().status
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
/// Returns request's headers.
|
|
||||||
pub fn headers(&self) -> &HeaderMap {
|
|
||||||
&self.head().headers
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set a body and return previous body value
|
|
||||||
pub fn map_body<F, U>(mut self, f: F) -> ClientResponse<U>
|
|
||||||
where
|
|
||||||
F: FnOnce(&mut ResponseHead, Payload<S>) -> Payload<U>,
|
|
||||||
{
|
|
||||||
let payload = f(&mut self.head, self.payload);
|
|
||||||
|
|
||||||
ClientResponse {
|
|
||||||
payload,
|
|
||||||
head: self.head,
|
|
||||||
timeout: self.timeout,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set a timeout duration for [`ClientResponse`](self::ClientResponse).
|
|
||||||
///
|
|
||||||
/// This duration covers the duration of processing the response body stream
|
|
||||||
/// and would end it as timeout error when deadline met.
|
|
||||||
///
|
|
||||||
/// Disabled by default.
|
|
||||||
pub fn timeout(self, dur: Duration) -> Self {
|
|
||||||
let timeout = match self.timeout {
|
|
||||||
ResponseTimeout::Disabled(Some(mut timeout))
|
|
||||||
| ResponseTimeout::Enabled(mut timeout) => match Instant::now().checked_add(dur) {
|
|
||||||
Some(deadline) => {
|
|
||||||
timeout.as_mut().reset(deadline.into());
|
|
||||||
ResponseTimeout::Enabled(timeout)
|
|
||||||
}
|
|
||||||
None => ResponseTimeout::Enabled(Box::pin(sleep(dur))),
|
|
||||||
},
|
|
||||||
_ => ResponseTimeout::Enabled(Box::pin(sleep(dur))),
|
|
||||||
};
|
|
||||||
|
|
||||||
Self {
|
|
||||||
payload: self.payload,
|
|
||||||
head: self.head,
|
|
||||||
timeout,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This method does not enable timeout. It's used to pass the boxed `Sleep` from
|
|
||||||
/// `SendClientRequest` and reuse it's heap allocation together with it's slot in
|
|
||||||
/// timer wheel.
|
|
||||||
pub(crate) fn _timeout(mut self, timeout: Option<Pin<Box<Sleep>>>) -> Self {
|
|
||||||
self.timeout = ResponseTimeout::Disabled(timeout);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Load request cookies.
|
|
||||||
#[cfg(feature = "cookies")]
|
|
||||||
pub fn cookies(&self) -> Result<Ref<'_, Vec<Cookie<'static>>>, CookieParseError> {
|
|
||||||
struct Cookies(Vec<Cookie<'static>>);
|
|
||||||
|
|
||||||
if self.extensions().get::<Cookies>().is_none() {
|
|
||||||
let mut cookies = Vec::new();
|
|
||||||
for hdr in self.headers().get_all(&header::SET_COOKIE) {
|
|
||||||
let s = std::str::from_utf8(hdr.as_bytes()).map_err(CookieParseError::from)?;
|
|
||||||
cookies.push(Cookie::parse_encoded(s)?.into_owned());
|
|
||||||
}
|
|
||||||
self.extensions_mut().insert(Cookies(cookies));
|
|
||||||
}
|
|
||||||
Ok(Ref::map(self.extensions(), |ext| {
|
|
||||||
&ext.get::<Cookies>().unwrap().0
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return request cookie.
|
|
||||||
#[cfg(feature = "cookies")]
|
|
||||||
pub fn cookie(&self, name: &str) -> Option<Cookie<'static>> {
|
|
||||||
if let Ok(cookies) = self.cookies() {
|
|
||||||
for cookie in cookies.iter() {
|
|
||||||
if cookie.name() == name {
|
|
||||||
return Some(cookie.to_owned());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S> ClientResponse<S>
|
|
||||||
where
|
|
||||||
S: Stream<Item = Result<Bytes, PayloadError>>,
|
|
||||||
{
|
|
||||||
/// Returns a [`Future`] that consumes the body stream and resolves to [`Bytes`].
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
/// `Future` implementation returns error if:
|
|
||||||
/// - content type is not `application/json`
|
|
||||||
/// - content length is greater than [limit](JsonBody::limit) (default: 2 MiB)
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
/// ```no_run
|
|
||||||
/// # use awc::Client;
|
|
||||||
/// # use bytes::Bytes;
|
|
||||||
/// # #[actix_rt::main]
|
|
||||||
/// # async fn async_ctx() -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
/// let client = Client::default();
|
|
||||||
/// let mut res = client.get("https://httpbin.org/robots.txt").send().await?;
|
|
||||||
/// let body: Bytes = res.body().await?;
|
|
||||||
/// # Ok(())
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// [`Future`]: std::future::Future
|
|
||||||
pub fn body(&mut self) -> ResponseBody<S> {
|
|
||||||
ResponseBody::new(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a [`Future`] consumes the body stream, parses JSON, and resolves to a deserialized
|
|
||||||
/// `T` value.
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
/// Future returns error if:
|
|
||||||
/// - content type is not `application/json`;
|
|
||||||
/// - content length is greater than [limit](JsonBody::limit) (default: 2 MiB).
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
/// ```no_run
|
|
||||||
/// # use awc::Client;
|
|
||||||
/// # #[actix_rt::main]
|
|
||||||
/// # async fn async_ctx() -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
/// let client = Client::default();
|
|
||||||
/// let mut res = client.get("https://httpbin.org/json").send().await?;
|
|
||||||
/// let val = res.json::<serde_json::Value>().await?;
|
|
||||||
/// assert!(val.is_object());
|
|
||||||
/// # Ok(())
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// [`Future`]: std::future::Future
|
|
||||||
pub fn json<T: DeserializeOwned>(&mut self) -> JsonBody<S, T> {
|
|
||||||
JsonBody::new(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S> fmt::Debug for ClientResponse<S> {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
writeln!(f, "\nClientResponse {:?} {}", self.version(), self.status(),)?;
|
|
||||||
writeln!(f, " headers:")?;
|
|
||||||
for (key, val) in self.headers().iter() {
|
|
||||||
writeln!(f, " {:?}: {:?}", key, val)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S> HttpMessage for ClientResponse<S> {
|
|
||||||
type Stream = S;
|
|
||||||
|
|
||||||
fn headers(&self) -> &HeaderMap {
|
|
||||||
&self.head.headers
|
|
||||||
}
|
|
||||||
|
|
||||||
fn take_payload(&mut self) -> Payload<S> {
|
|
||||||
mem::replace(&mut self.payload, Payload::None)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn extensions(&self) -> Ref<'_, Extensions> {
|
|
||||||
self.head.extensions()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn extensions_mut(&self) -> RefMut<'_, Extensions> {
|
|
||||||
self.head.extensions_mut()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S> Stream for ClientResponse<S>
|
|
||||||
where
|
|
||||||
S: Stream<Item = Result<Bytes, PayloadError>> + Unpin,
|
|
||||||
{
|
|
||||||
type Item = Result<Bytes, PayloadError>;
|
|
||||||
|
|
||||||
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
|
||||||
let this = self.project();
|
|
||||||
this.timeout.poll_timeout(cx)?;
|
|
||||||
this.payload.poll_next(cx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use static_assertions::assert_impl_all;
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
use crate::any_body::AnyBody;
|
|
||||||
|
|
||||||
assert_impl_all!(ClientResponse: Unpin);
|
|
||||||
assert_impl_all!(ClientResponse<()>: Unpin);
|
|
||||||
assert_impl_all!(ClientResponse<AnyBody>: Unpin);
|
|
||||||
}
|
|
@@ -1,144 +0,0 @@
|
|||||||
use std::{
|
|
||||||
future::Future,
|
|
||||||
mem,
|
|
||||||
pin::Pin,
|
|
||||||
task::{Context, Poll},
|
|
||||||
};
|
|
||||||
|
|
||||||
use actix_http::{error::PayloadError, header, HttpMessage};
|
|
||||||
use bytes::Bytes;
|
|
||||||
use futures_core::Stream;
|
|
||||||
use pin_project_lite::pin_project;
|
|
||||||
|
|
||||||
use super::{read_body::ReadBody, ResponseTimeout, DEFAULT_BODY_LIMIT};
|
|
||||||
use crate::ClientResponse;
|
|
||||||
|
|
||||||
pin_project! {
|
|
||||||
/// A `Future` that reads a body stream, resolving as [`Bytes`].
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
/// `Future` implementation returns error if:
|
|
||||||
/// - content type is not `application/json`;
|
|
||||||
/// - content length is greater than [limit](JsonBody::limit) (default: 2 MiB).
|
|
||||||
pub struct ResponseBody<S> {
|
|
||||||
#[pin]
|
|
||||||
body: Option<ReadBody<S>>,
|
|
||||||
length: Option<usize>,
|
|
||||||
timeout: ResponseTimeout,
|
|
||||||
err: Option<PayloadError>,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[deprecated(since = "3.0.0", note = "Renamed to `ResponseBody`.")]
|
|
||||||
pub type MessageBody<B> = ResponseBody<B>;
|
|
||||||
|
|
||||||
impl<S> ResponseBody<S>
|
|
||||||
where
|
|
||||||
S: Stream<Item = Result<Bytes, PayloadError>>,
|
|
||||||
{
|
|
||||||
/// Creates a body stream reader from a response by taking its payload.
|
|
||||||
pub fn new(res: &mut ClientResponse<S>) -> ResponseBody<S> {
|
|
||||||
let length = match res.headers().get(&header::CONTENT_LENGTH) {
|
|
||||||
Some(value) => {
|
|
||||||
let len = value.to_str().ok().and_then(|s| s.parse::<usize>().ok());
|
|
||||||
|
|
||||||
match len {
|
|
||||||
None => return Self::err(PayloadError::UnknownLength),
|
|
||||||
len => len,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
ResponseBody {
|
|
||||||
body: Some(ReadBody::new(res.take_payload(), DEFAULT_BODY_LIMIT)),
|
|
||||||
length,
|
|
||||||
timeout: mem::take(&mut res.timeout),
|
|
||||||
err: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Change max size limit of payload.
|
|
||||||
///
|
|
||||||
/// The default limit is 2 MiB.
|
|
||||||
pub fn limit(mut self, limit: usize) -> Self {
|
|
||||||
if let Some(ref mut body) = self.body {
|
|
||||||
body.limit = limit;
|
|
||||||
}
|
|
||||||
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn err(err: PayloadError) -> Self {
|
|
||||||
ResponseBody {
|
|
||||||
body: None,
|
|
||||||
length: None,
|
|
||||||
timeout: ResponseTimeout::default(),
|
|
||||||
err: Some(err),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S> Future for ResponseBody<S>
|
|
||||||
where
|
|
||||||
S: Stream<Item = Result<Bytes, PayloadError>>,
|
|
||||||
{
|
|
||||||
type Output = Result<Bytes, PayloadError>;
|
|
||||||
|
|
||||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
|
||||||
let this = self.project();
|
|
||||||
|
|
||||||
if let Some(err) = this.err.take() {
|
|
||||||
return Poll::Ready(Err(err));
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(len) = this.length.take() {
|
|
||||||
let body = Option::as_ref(&this.body).unwrap();
|
|
||||||
if len > body.limit {
|
|
||||||
return Poll::Ready(Err(PayloadError::Overflow));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.timeout.poll_timeout(cx)?;
|
|
||||||
|
|
||||||
this.body.as_pin_mut().unwrap().poll(cx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use static_assertions::assert_impl_all;
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
use crate::{http::header, test::TestResponse};
|
|
||||||
|
|
||||||
assert_impl_all!(ResponseBody<()>: Unpin);
|
|
||||||
|
|
||||||
#[actix_rt::test]
|
|
||||||
async fn read_body() {
|
|
||||||
let mut req = TestResponse::with_header((header::CONTENT_LENGTH, "xxxx")).finish();
|
|
||||||
match req.body().await.err().unwrap() {
|
|
||||||
PayloadError::UnknownLength => {}
|
|
||||||
_ => unreachable!("error"),
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut req = TestResponse::with_header((header::CONTENT_LENGTH, "10000000")).finish();
|
|
||||||
match req.body().await.err().unwrap() {
|
|
||||||
PayloadError::Overflow => {}
|
|
||||||
_ => unreachable!("error"),
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut req = TestResponse::default()
|
|
||||||
.set_payload(Bytes::from_static(b"test"))
|
|
||||||
.finish();
|
|
||||||
assert_eq!(req.body().await.ok().unwrap(), Bytes::from_static(b"test"));
|
|
||||||
|
|
||||||
let mut req = TestResponse::default()
|
|
||||||
.set_payload(Bytes::from_static(b"11111111111111"))
|
|
||||||
.finish();
|
|
||||||
match req.body().limit(5).await.err().unwrap() {
|
|
||||||
PayloadError::Overflow => {}
|
|
||||||
_ => unreachable!("error"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -8,7 +8,7 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use actix_http::{
|
use actix_http::{
|
||||||
body::{BodyStream, MessageBody},
|
body::BodyStream,
|
||||||
error::HttpError,
|
error::HttpError,
|
||||||
header::{self, HeaderMap, HeaderName, TryIntoHeaderValue},
|
header::{self, HeaderMap, HeaderName, TryIntoHeaderValue},
|
||||||
RequestHead, RequestHeadType,
|
RequestHead, RequestHeadType,
|
||||||
@@ -20,13 +20,12 @@ use futures_core::Stream;
|
|||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
#[cfg(feature = "__compress")]
|
#[cfg(feature = "__compress")]
|
||||||
use actix_http::{encoding::Decoder, header::ContentEncoding, Payload};
|
use actix_http::{encoding::Decoder, header::ContentEncoding, Payload, PayloadStream};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
any_body::AnyBody,
|
any_body::AnyBody,
|
||||||
client::ClientConfig,
|
|
||||||
error::{FreezeRequestError, InvalidUrl, SendRequestError},
|
error::{FreezeRequestError, InvalidUrl, SendRequestError},
|
||||||
BoxError, ClientResponse, ConnectRequest, ConnectResponse,
|
BoxError, ClientConfig, ClientResponse, ConnectRequest, ConnectResponse,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, From)]
|
#[derive(Debug, From)]
|
||||||
@@ -92,7 +91,7 @@ impl SendClientRequest {
|
|||||||
|
|
||||||
#[cfg(feature = "__compress")]
|
#[cfg(feature = "__compress")]
|
||||||
impl Future for SendClientRequest {
|
impl Future for SendClientRequest {
|
||||||
type Output = Result<ClientResponse<Decoder<Payload>>, SendRequestError>;
|
type Output = Result<ClientResponse<Decoder<Payload<PayloadStream>>>, SendRequestError>;
|
||||||
|
|
||||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
let this = self.get_mut();
|
let this = self.get_mut();
|
||||||
@@ -109,13 +108,12 @@ impl Future for SendClientRequest {
|
|||||||
res.into_client_response()._timeout(delay.take()).map_body(
|
res.into_client_response()._timeout(delay.take()).map_body(
|
||||||
|head, payload| {
|
|head, payload| {
|
||||||
if *response_decompress {
|
if *response_decompress {
|
||||||
Payload::Stream {
|
Payload::Stream(Decoder::from_headers(payload, &head.headers))
|
||||||
payload: Decoder::from_headers(payload, &head.headers),
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
Payload::Stream {
|
Payload::Stream(Decoder::new(
|
||||||
payload: Decoder::new(payload, ContentEncoding::Identity),
|
payload,
|
||||||
}
|
ContentEncoding::Identity,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@@ -190,17 +188,15 @@ impl RequestSender {
|
|||||||
body: B,
|
body: B,
|
||||||
) -> SendClientRequest
|
) -> SendClientRequest
|
||||||
where
|
where
|
||||||
B: MessageBody + 'static,
|
B: Into<AnyBody>,
|
||||||
{
|
{
|
||||||
let req = match self {
|
let req = match self {
|
||||||
RequestSender::Owned(head) => ConnectRequest::Client(
|
RequestSender::Owned(head) => {
|
||||||
RequestHeadType::Owned(head),
|
ConnectRequest::Client(RequestHeadType::Owned(head), body.into(), addr)
|
||||||
AnyBody::from_message_body(body).into_boxed(),
|
}
|
||||||
addr,
|
|
||||||
),
|
|
||||||
RequestSender::Rc(head, extra_headers) => ConnectRequest::Client(
|
RequestSender::Rc(head, extra_headers) => ConnectRequest::Client(
|
||||||
RequestHeadType::Rc(head, extra_headers),
|
RequestHeadType::Rc(head, extra_headers),
|
||||||
AnyBody::from_message_body(body).into_boxed(),
|
body.into(),
|
||||||
addr,
|
addr,
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
@@ -232,7 +228,9 @@ impl RequestSender {
|
|||||||
response_decompress,
|
response_decompress,
|
||||||
timeout,
|
timeout,
|
||||||
config,
|
config,
|
||||||
AnyBody::from_message_body(body.into_bytes()),
|
AnyBody::Bytes {
|
||||||
|
body: Bytes::from(body),
|
||||||
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -261,7 +259,9 @@ impl RequestSender {
|
|||||||
response_decompress,
|
response_decompress,
|
||||||
timeout,
|
timeout,
|
||||||
config,
|
config,
|
||||||
AnyBody::from_message_body(body.into_bytes()),
|
AnyBody::Bytes {
|
||||||
|
body: Bytes::from(body),
|
||||||
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -65,7 +65,7 @@ impl TestResponse {
|
|||||||
|
|
||||||
/// Set response's payload
|
/// Set response's payload
|
||||||
pub fn set_payload<B: Into<Bytes>>(mut self, data: B) -> Self {
|
pub fn set_payload<B: Into<Bytes>>(mut self, data: B) -> Self {
|
||||||
let (_, mut payload) = h1::Payload::create(true);
|
let mut payload = h1::Payload::empty();
|
||||||
payload.unread_data(data.into());
|
payload.unread_data(data.into());
|
||||||
self.payload = Some(payload.into());
|
self.payload = Some(payload.into());
|
||||||
self
|
self
|
||||||
@@ -90,8 +90,7 @@ impl TestResponse {
|
|||||||
if let Some(pl) = self.payload {
|
if let Some(pl) = self.payload {
|
||||||
ClientResponse::new(head, pl)
|
ClientResponse::new(head, pl)
|
||||||
} else {
|
} else {
|
||||||
let (_, payload) = h1::Payload::create(true);
|
ClientResponse::new(head, h1::Payload::empty().into())
|
||||||
ClientResponse::new(head, payload.into())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -31,19 +31,19 @@ use std::{convert::TryFrom, fmt, net::SocketAddr, str};
|
|||||||
use actix_codec::Framed;
|
use actix_codec::Framed;
|
||||||
use actix_http::{ws, Payload, RequestHead};
|
use actix_http::{ws, Payload, RequestHead};
|
||||||
use actix_rt::time::timeout;
|
use actix_rt::time::timeout;
|
||||||
use actix_service::Service as _;
|
use actix_service::Service;
|
||||||
|
|
||||||
pub use actix_http::ws::{CloseCode, CloseReason, Codec, Frame, Message};
|
pub use actix_http::ws::{CloseCode, CloseReason, Codec, Frame, Message};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
client::ClientConfig,
|
|
||||||
connect::{BoxedSocket, ConnectRequest},
|
connect::{BoxedSocket, ConnectRequest},
|
||||||
error::{HttpError, InvalidUrl, SendRequestError, WsClientError},
|
error::{HttpError, InvalidUrl, SendRequestError, WsClientError},
|
||||||
http::{
|
http::{
|
||||||
header::{self, HeaderName, HeaderValue, TryIntoHeaderValue, AUTHORIZATION},
|
header::{self, HeaderName, HeaderValue, TryIntoHeaderValue, AUTHORIZATION},
|
||||||
ConnectionType, Method, StatusCode, Uri, Version,
|
ConnectionType, Method, StatusCode, Uri, Version,
|
||||||
},
|
},
|
||||||
ClientResponse,
|
response::ClientResponse,
|
||||||
|
ClientConfig,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "cookies")]
|
#[cfg(feature = "cookies")]
|
||||||
@@ -300,16 +300,13 @@ impl WebsocketsRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.head.set_connection_type(ConnectionType::Upgrade);
|
self.head.set_connection_type(ConnectionType::Upgrade);
|
||||||
|
|
||||||
#[allow(clippy::declare_interior_mutable_const)]
|
|
||||||
const HV_WEBSOCKET: HeaderValue = HeaderValue::from_static("websocket");
|
|
||||||
self.head.headers.insert(header::UPGRADE, HV_WEBSOCKET);
|
|
||||||
|
|
||||||
#[allow(clippy::declare_interior_mutable_const)]
|
|
||||||
const HV_THIRTEEN: HeaderValue = HeaderValue::from_static("13");
|
|
||||||
self.head
|
self.head
|
||||||
.headers
|
.headers
|
||||||
.insert(header::SEC_WEBSOCKET_VERSION, HV_THIRTEEN);
|
.insert(header::UPGRADE, HeaderValue::from_static("websocket"));
|
||||||
|
self.head.headers.insert(
|
||||||
|
header::SEC_WEBSOCKET_VERSION,
|
||||||
|
HeaderValue::from_static("13"),
|
||||||
|
);
|
||||||
|
|
||||||
if let Some(protocols) = self.protocols.take() {
|
if let Some(protocols) = self.protocols.take() {
|
||||||
self.head.headers.insert(
|
self.head.headers.insert(
|
||||||
|
@@ -58,7 +58,7 @@ async fn test_connection_window_size() {
|
|||||||
.map_err(|e| log::error!("Can not set alpn protocol: {:?}", e));
|
.map_err(|e| log::error!("Can not set alpn protocol: {:?}", e));
|
||||||
|
|
||||||
let client = awc::Client::builder()
|
let client = awc::Client::builder()
|
||||||
.connector(awc::Connector::new().openssl(builder.build()))
|
.connector(awc::Connector::new().ssl(builder.build()))
|
||||||
.initial_window_size(100)
|
.initial_window_size(100)
|
||||||
.initial_connection_window_size(100)
|
.initial_connection_window_size(100)
|
||||||
.finish();
|
.finish();
|
||||||
|
@@ -72,7 +72,7 @@ async fn test_connection_reuse_h2() {
|
|||||||
.map_err(|e| log::error!("Can not set alpn protocol: {:?}", e));
|
.map_err(|e| log::error!("Can not set alpn protocol: {:?}", e));
|
||||||
|
|
||||||
let client = awc::Client::builder()
|
let client = awc::Client::builder()
|
||||||
.connector(awc::Connector::new().openssl(builder.build()))
|
.connector(awc::Connector::new().ssl(builder.build()))
|
||||||
.finish();
|
.finish();
|
||||||
|
|
||||||
// req 1
|
// req 1
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user