mirror of
https://github.com/fafhrd91/actix-web
synced 2025-08-31 17:07:01 +02:00
Compare commits
16 Commits
body-ergo-
...
actors-v4.
Author | SHA1 | Date | |
---|---|---|---|
|
5b0a50249b | ||
|
60b030ff53 | ||
|
fc4e9ff96b | ||
|
6481a5fb73 | ||
|
0cd7c17682 | ||
|
ed2f5b40b9 | ||
|
cc37be9700 | ||
|
e1cdabe5cb | ||
|
d0f4c809ca | ||
|
65dd5dfa7b | ||
|
f62383a975 | ||
|
f9348d7129 | ||
|
774ac7fec4 | ||
|
69fa17f66f | ||
|
816d68dee8 | ||
|
7dc034f0fb |
11
CHANGES.md
11
CHANGES.md
@@ -1,6 +1,9 @@
|
|||||||
# Changes
|
# Changes
|
||||||
|
|
||||||
## Unreleased - 2021-xx-xx
|
## Unreleased - 2021-xx-xx
|
||||||
|
|
||||||
|
|
||||||
|
## 4.0.0-beta.14 - 2021-12-11
|
||||||
### Added
|
### Added
|
||||||
* Methods on `AcceptLanguage`: `ranked` and `preference`. [#2480]
|
* Methods on `AcceptLanguage`: `ranked` and `preference`. [#2480]
|
||||||
* `AcceptEncoding` typed header. [#2482]
|
* `AcceptEncoding` typed header. [#2482]
|
||||||
@@ -8,6 +11,8 @@
|
|||||||
* `HttpResponse::map_into_{left,right}_body` and `HttpResponse::map_into_boxed_body`. [#2468]
|
* `HttpResponse::map_into_{left,right}_body` and `HttpResponse::map_into_boxed_body`. [#2468]
|
||||||
* `ServiceResponse::map_into_{left,right}_body` and `HttpResponse::map_into_boxed_body`. [#2468]
|
* `ServiceResponse::map_into_{left,right}_body` and `HttpResponse::map_into_boxed_body`. [#2468]
|
||||||
* Connection data set through the `HttpServer::on_connect` callback is now accessible only from the new `HttpRequest::conn_data()` and `ServiceRequest::conn_data()` methods. [#2491]
|
* Connection data set through the `HttpServer::on_connect` callback is now accessible only from the new `HttpRequest::conn_data()` and `ServiceRequest::conn_data()` methods. [#2491]
|
||||||
|
* `HttpRequest::{req_data,req_data_mut}`. [#2487]
|
||||||
|
* `ServiceResponse::into_parts`. [#2499]
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
* Rename `Accept::{mime_precedence => ranked}`. [#2480]
|
* Rename `Accept::{mime_precedence => ranked}`. [#2480]
|
||||||
@@ -16,21 +21,27 @@
|
|||||||
* `HttpRequest::url_for` no longer constructs URLs with query or fragment components. [#2430]
|
* `HttpRequest::url_for` no longer constructs URLs with query or fragment components. [#2430]
|
||||||
* Remove `B` (body) type parameter on `App`. [#2493]
|
* Remove `B` (body) type parameter on `App`. [#2493]
|
||||||
* Add `B` (body) type parameter on `Scope`. [#2492]
|
* Add `B` (body) type parameter on `Scope`. [#2492]
|
||||||
|
* Request-local data container is no longer part of a `RequestHead`. Instead it is a distinct part of a `Request`. [#2487]
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
* Accept wildcard `*` items in `AcceptLanguage`. [#2480]
|
* Accept wildcard `*` items in `AcceptLanguage`. [#2480]
|
||||||
* Re-exports `dev::{BodySize, MessageBody, SizedStream}`. They are exposed through the `body` module. [#2468]
|
* Re-exports `dev::{BodySize, MessageBody, SizedStream}`. They are exposed through the `body` module. [#2468]
|
||||||
* Typed headers containing lists that require one or more items now enforce this minimum. [#2482]
|
* Typed headers containing lists that require one or more items now enforce this minimum. [#2482]
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
* `ConnectionInfo::get`. [#2487]
|
||||||
|
|
||||||
[#2430]: https://github.com/actix/actix-web/pull/2430
|
[#2430]: https://github.com/actix/actix-web/pull/2430
|
||||||
[#2468]: https://github.com/actix/actix-web/pull/2468
|
[#2468]: https://github.com/actix/actix-web/pull/2468
|
||||||
[#2480]: https://github.com/actix/actix-web/pull/2480
|
[#2480]: https://github.com/actix/actix-web/pull/2480
|
||||||
[#2482]: https://github.com/actix/actix-web/pull/2482
|
[#2482]: https://github.com/actix/actix-web/pull/2482
|
||||||
[#2484]: https://github.com/actix/actix-web/pull/2484
|
[#2484]: https://github.com/actix/actix-web/pull/2484
|
||||||
[#2485]: https://github.com/actix/actix-web/pull/2485
|
[#2485]: https://github.com/actix/actix-web/pull/2485
|
||||||
|
[#2487]: https://github.com/actix/actix-web/pull/2487
|
||||||
[#2491]: https://github.com/actix/actix-web/pull/2491
|
[#2491]: https://github.com/actix/actix-web/pull/2491
|
||||||
[#2492]: https://github.com/actix/actix-web/pull/2492
|
[#2492]: https://github.com/actix/actix-web/pull/2492
|
||||||
[#2493]: https://github.com/actix/actix-web/pull/2493
|
[#2493]: https://github.com/actix/actix-web/pull/2493
|
||||||
|
[#2499]: https://github.com/actix/actix-web/pull/2499
|
||||||
|
|
||||||
|
|
||||||
## 4.0.0-beta.13 - 2021-11-30
|
## 4.0.0-beta.13 - 2021-11-30
|
||||||
|
10
Cargo.toml
10
Cargo.toml
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "actix-web"
|
name = "actix-web"
|
||||||
version = "4.0.0-beta.13"
|
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"]
|
||||||
@@ -77,9 +77,9 @@ actix-service = "2.0.0"
|
|||||||
actix-utils = "3.0.0"
|
actix-utils = "3.0.0"
|
||||||
actix-tls = { version = "3.0.0-rc.1", default-features = false, optional = true }
|
actix-tls = { version = "3.0.0-rc.1", default-features = false, optional = true }
|
||||||
|
|
||||||
actix-http = "3.0.0-beta.14"
|
actix-http = "3.0.0-beta.15"
|
||||||
actix-router = "0.5.0-beta.2"
|
actix-router = "0.5.0-beta.2"
|
||||||
actix-web-codegen = "0.5.0-beta.5"
|
actix-web-codegen = "0.5.0-beta.6"
|
||||||
|
|
||||||
ahash = "0.7"
|
ahash = "0.7"
|
||||||
bytes = "1"
|
bytes = "1"
|
||||||
@@ -107,8 +107,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.7", features = ["openssl", "rustls"] }
|
actix-test = { version = "0.1.0-beta.8", features = ["openssl", "rustls"] }
|
||||||
awc = { version = "3.0.0-beta.11", 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"] }
|
||||||
|
@@ -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.13)
|
[](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.13)
|
[](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)
|
||||||
|
@@ -3,6 +3,10 @@
|
|||||||
## Unreleased - 2021-xx-xx
|
## Unreleased - 2021-xx-xx
|
||||||
|
|
||||||
|
|
||||||
|
## 0.6.0-beta.10 - 2021-12-11
|
||||||
|
* 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]
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "actix-files"
|
name = "actix-files"
|
||||||
version = "0.6.0-beta.9"
|
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,17 +22,17 @@ 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-web = { version = "4.0.0-beta.11", default-features = false }
|
actix-http = "3.0.0-beta.15"
|
||||||
actix-http = "3.0.0-beta.14"
|
|
||||||
actix-service = "2"
|
actix-service = "2"
|
||||||
actix-utils = "3"
|
actix-utils = "3"
|
||||||
|
actix-web = { version = "4.0.0-beta.14", default-features = false }
|
||||||
|
|
||||||
askama_escape = "0.10"
|
askama_escape = "0.10"
|
||||||
bitflags = "1"
|
bitflags = "1"
|
||||||
bytes = "1"
|
bytes = "1"
|
||||||
|
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, features = ["alloc"] }
|
||||||
http-range = "0.1.4"
|
http-range = "0.1.4"
|
||||||
derive_more = "0.99.5"
|
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
mime = "0.3"
|
mime = "0.3"
|
||||||
mime_guess = "2.0.1"
|
mime_guess = "2.0.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-web = "4.0.0-beta.11"
|
actix-test = "0.1.0-beta.8"
|
||||||
actix-test = "0.1.0-beta.7"
|
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.9)
|
[](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.9)
|
[](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,6 +3,10 @@
|
|||||||
## Unreleased - 2021-xx-xx
|
## Unreleased - 2021-xx-xx
|
||||||
|
|
||||||
|
|
||||||
|
## 3.0.0-beta.9 - 2021-12-11
|
||||||
|
* 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]
|
||||||
|
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "actix-http-test"
|
name = "actix-http-test"
|
||||||
version = "3.0.0-beta.8"
|
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"]
|
||||||
@@ -35,7 +35,7 @@ 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.1"
|
actix-server = "2.0.0-rc.1"
|
||||||
awc = { version = "3.0.0-beta.11", default-features = false }
|
awc = { version = "3.0.0-beta.13", default-features = false }
|
||||||
|
|
||||||
base64 = "0.13"
|
base64 = "0.13"
|
||||||
bytes = "1"
|
bytes = "1"
|
||||||
@@ -51,5 +51,5 @@ tls-openssl = { version = "0.10.9", package = "openssl", optional = true }
|
|||||||
tokio = { version = "1.2", features = ["sync"] }
|
tokio = { version = "1.2", features = ["sync"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
actix-web = { version = "4.0.0-beta.11", default-features = false, features = ["cookies"] }
|
actix-web = { version = "4.0.0-beta.14", default-features = false, features = ["cookies"] }
|
||||||
actix-http = "3.0.0-beta.14"
|
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.8)
|
[](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.8)
|
[](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)
|
||||||
|
|
||||||
|
@@ -1,6 +1,9 @@
|
|||||||
# Changes
|
# Changes
|
||||||
|
|
||||||
## Unreleased - 2021-xx-xx
|
## Unreleased - 2021-xx-xx
|
||||||
|
|
||||||
|
|
||||||
|
## 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]
|
||||||
@@ -16,6 +19,9 @@
|
|||||||
* `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]
|
||||||
|
* `impl Clone` for `RequestHead`. [#2487]
|
||||||
|
* 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]
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
* Rename `body::BoxBody::{from_body => new}`. [#2468]
|
* Rename `body::BoxBody::{from_body => new}`. [#2468]
|
||||||
@@ -40,8 +46,10 @@
|
|||||||
[#2468]: https://github.com/actix/actix-web/pull/2468
|
[#2468]: https://github.com/actix/actix-web/pull/2468
|
||||||
[#1920]: https://github.com/actix/actix-web/pull/1920
|
[#1920]: https://github.com/actix/actix-web/pull/1920
|
||||||
[#2486]: https://github.com/actix/actix-web/pull/2486
|
[#2486]: https://github.com/actix/actix-web/pull/2486
|
||||||
|
[#2487]: https://github.com/actix/actix-web/pull/2487
|
||||||
[#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
|
||||||
|
|
||||||
|
|
||||||
## 3.0.0-beta.14 - 2021-11-30
|
## 3.0.0-beta.14 - 2021-11-30
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "actix-http"
|
name = "actix-http"
|
||||||
version = "3.0.0-beta.14"
|
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"]
|
||||||
@@ -56,7 +56,7 @@ 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"] }
|
futures-util = { version = "0.3.7", default-features = false, features = ["alloc", "sink"] }
|
||||||
h2 = "0.3.1"
|
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"
|
||||||
@@ -81,10 +81,11 @@ 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.9", features = ["openssl"] }
|
||||||
actix-server = "2.0.0-rc.1"
|
actix-server = "2.0.0-rc.1"
|
||||||
actix-http-test = { version = "3.0.0-beta.7", features = ["openssl"] }
|
|
||||||
actix-tls = { version = "3.0.0-rc.1", features = ["openssl"] }
|
actix-tls = { version = "3.0.0-rc.1", features = ["openssl"] }
|
||||||
actix-web = "4.0.0-beta.13"
|
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"
|
||||||
|
@@ -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.14)
|
[](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.14)
|
[](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)
|
||||||
|
|
||||||
|
@@ -1,8 +1,9 @@
|
|||||||
use std::{convert::Infallible, io};
|
use std::{convert::Infallible, io};
|
||||||
|
|
||||||
use actix_http::{HttpService, Response, StatusCode};
|
use actix_http::{
|
||||||
|
header::HeaderValue, HttpMessage, HttpService, Request, Response, StatusCode,
|
||||||
|
};
|
||||||
use actix_server::Server;
|
use actix_server::Server;
|
||||||
use http::header::HeaderValue;
|
|
||||||
|
|
||||||
#[actix_rt::main]
|
#[actix_rt::main]
|
||||||
async fn main() -> io::Result<()> {
|
async fn main() -> io::Result<()> {
|
||||||
@@ -13,12 +14,21 @@ async fn main() -> io::Result<()> {
|
|||||||
HttpService::build()
|
HttpService::build()
|
||||||
.client_timeout(1000)
|
.client_timeout(1000)
|
||||||
.client_disconnect(1000)
|
.client_disconnect(1000)
|
||||||
.finish(|req| async move {
|
.on_connect_ext(|_, ext| {
|
||||||
|
ext.insert(42u32);
|
||||||
|
})
|
||||||
|
.finish(|req: Request| async move {
|
||||||
log::info!("{:?}", req);
|
log::info!("{:?}", req);
|
||||||
|
|
||||||
let mut res = Response::build(StatusCode::OK);
|
let mut res = Response::build(StatusCode::OK);
|
||||||
res.insert_header(("x-head", HeaderValue::from_static("dummy value!")));
|
res.insert_header(("x-head", HeaderValue::from_static("dummy value!")));
|
||||||
|
|
||||||
|
let forty_two = req.extensions().get::<u32>().unwrap().to_string();
|
||||||
|
res.insert_header((
|
||||||
|
"x-forty-two",
|
||||||
|
HeaderValue::from_str(&forty_two).unwrap(),
|
||||||
|
));
|
||||||
|
|
||||||
Ok::<_, Infallible>(res.body("Hello world!"))
|
Ok::<_, Infallible>(res.body("Hello world!"))
|
||||||
})
|
})
|
||||||
.tcp()
|
.tcp()
|
||||||
|
@@ -51,6 +51,34 @@ impl MessageBody for BoxBody {
|
|||||||
.poll_next(cx)
|
.poll_next(cx)
|
||||||
.map_err(|err| Error::new_body().with_cause(err))
|
.map_err(|err| Error::new_body().with_cause(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_complete_body(&self) -> bool {
|
||||||
|
self.0.is_complete_body()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn take_complete_body(&mut self) -> Bytes {
|
||||||
|
debug_assert!(
|
||||||
|
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",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@@ -67,6 +67,20 @@ where
|
|||||||
.map_err(|err| Error::new_body().with_cause(err)),
|
.map_err(|err| Error::new_body().with_cause(err)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_complete_body(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
EitherBody::Left { body } => body.is_complete_body(),
|
||||||
|
EitherBody::Right { body } => body.is_complete_body(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn take_complete_body(&mut self) -> Bytes {
|
||||||
|
match self {
|
||||||
|
EitherBody::Left { body } => body.take_complete_body(),
|
||||||
|
EitherBody::Right { body } => body.take_complete_body(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@@ -25,10 +25,58 @@ pub trait MessageBody {
|
|||||||
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.
|
||||||
|
// TODO: expand documentation
|
||||||
fn poll_next(
|
fn poll_next(
|
||||||
self: Pin<&mut Self>,
|
self: Pin<&mut Self>,
|
||||||
cx: &mut Context<'_>,
|
cx: &mut Context<'_>,
|
||||||
) -> Poll<Option<Result<Bytes, Self::Error>>>;
|
) -> Poll<Option<Result<Bytes, Self::Error>>>;
|
||||||
|
|
||||||
|
/// Returns true if entire body bytes chunk is obtainable in one call to `poll_next`.
|
||||||
|
///
|
||||||
|
/// This method's implementation should agree with [`take_complete_body`] and should always be
|
||||||
|
/// checked before taking the body.
|
||||||
|
///
|
||||||
|
/// The default implementation returns `false.
|
||||||
|
///
|
||||||
|
/// [`take_complete_body`]: MessageBody::take_complete_body
|
||||||
|
fn is_complete_body(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the complete chunk of body bytes.
|
||||||
|
///
|
||||||
|
/// Implementors of this method should note the following:
|
||||||
|
/// - It is acceptable to skip the omit checks of [`is_complete_body`]. The responsibility of
|
||||||
|
/// performing this check is delegated to the caller.
|
||||||
|
/// - If the result of [`is_complete_body`] is conditional, that condition should be given
|
||||||
|
/// 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>()
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod foreign_impls {
|
mod foreign_impls {
|
||||||
@@ -49,6 +97,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 () {
|
||||||
@@ -66,6 +122,16 @@ mod foreign_impls {
|
|||||||
) -> Poll<Option<Result<Bytes, Self::Error>>> {
|
) -> Poll<Option<Result<Bytes, Self::Error>>> {
|
||||||
Poll::Ready(None)
|
Poll::Ready(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn is_complete_body(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn take_complete_body(&mut self) -> Bytes {
|
||||||
|
Bytes::new()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<B> MessageBody for Box<B>
|
impl<B> MessageBody for Box<B>
|
||||||
@@ -86,6 +152,16 @@ 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>>
|
||||||
@@ -106,6 +182,38 @@ mod foreign_impls {
|
|||||||
) -> Poll<Option<Result<Bytes, Self::Error>>> {
|
) -> Poll<Option<Result<Bytes, Self::Error>>> {
|
||||||
self.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>()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MessageBody for &'static [u8] {
|
impl MessageBody for &'static [u8] {
|
||||||
@@ -116,17 +224,23 @@ mod foreign_impls {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
||||||
let bytes = mem::take(self.get_mut());
|
Poll::Ready(Some(Ok(self.take_complete_body())))
|
||||||
let bytes = Bytes::from_static(bytes);
|
|
||||||
Poll::Ready(Some(Ok(bytes)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_complete_body(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn take_complete_body(&mut self) -> Bytes {
|
||||||
|
Bytes::from_static(mem::take(self))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MessageBody for Bytes {
|
impl MessageBody for Bytes {
|
||||||
@@ -137,16 +251,23 @@ mod foreign_impls {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
||||||
let bytes = mem::take(self.get_mut());
|
Poll::Ready(Some(Ok(self.take_complete_body())))
|
||||||
Poll::Ready(Some(Ok(bytes)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_complete_body(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn take_complete_body(&mut self) -> Bytes {
|
||||||
|
mem::take(self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MessageBody for BytesMut {
|
impl MessageBody for BytesMut {
|
||||||
@@ -157,16 +278,23 @@ mod foreign_impls {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
||||||
let bytes = mem::take(self.get_mut()).freeze();
|
Poll::Ready(Some(Ok(self.take_complete_body())))
|
||||||
Poll::Ready(Some(Ok(bytes)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_complete_body(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn take_complete_body(&mut self) -> Bytes {
|
||||||
|
mem::take(self).freeze()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MessageBody for Vec<u8> {
|
impl MessageBody for Vec<u8> {
|
||||||
@@ -177,16 +305,23 @@ mod foreign_impls {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
||||||
let bytes = mem::take(self.get_mut());
|
Poll::Ready(Some(Ok(self.take_complete_body())))
|
||||||
Poll::Ready(Some(Ok(Bytes::from(bytes))))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_complete_body(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn take_complete_body(&mut self) -> Bytes {
|
||||||
|
Bytes::from(mem::take(self))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MessageBody for &'static str {
|
impl MessageBody for &'static str {
|
||||||
@@ -208,6 +343,14 @@ mod foreign_impls {
|
|||||||
Poll::Ready(Some(Ok(bytes)))
|
Poll::Ready(Some(Ok(bytes)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_complete_body(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn take_complete_body(&mut self) -> Bytes {
|
||||||
|
Bytes::from_static(mem::take(self).as_bytes())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MessageBody for String {
|
impl MessageBody for String {
|
||||||
@@ -228,6 +371,14 @@ mod foreign_impls {
|
|||||||
Poll::Ready(Some(Ok(Bytes::from(string))))
|
Poll::Ready(Some(Ok(Bytes::from(string))))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_complete_body(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn take_complete_body(&mut self) -> Bytes {
|
||||||
|
Bytes::from(mem::take(self))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MessageBody for bytestring::ByteString {
|
impl MessageBody for bytestring::ByteString {
|
||||||
@@ -244,6 +395,14 @@ mod foreign_impls {
|
|||||||
let string = mem::take(self.get_mut());
|
let string = mem::take(self.get_mut());
|
||||||
Poll::Ready(Some(Ok(string.into_bytes())))
|
Poll::Ready(Some(Ok(string.into_bytes())))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_complete_body(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn take_complete_body(&mut self) -> Bytes {
|
||||||
|
mem::take(self).into_bytes()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -406,6 +565,51 @@ mod tests {
|
|||||||
assert_poll_next!(pl, Bytes::from("test"));
|
assert_poll_next!(pl, Bytes::from("test"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn take_string() {
|
||||||
|
let mut data = "test".repeat(2);
|
||||||
|
let data_bytes = Bytes::from(data.clone());
|
||||||
|
assert!(data.is_complete_body());
|
||||||
|
assert_eq!(data.take_complete_body(), data_bytes);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn take_boxed_equivalence() {
|
||||||
|
let mut data = Bytes::from_static(b"test");
|
||||||
|
assert!(data.is_complete_body());
|
||||||
|
assert_eq!(data.take_complete_body(), b"test".as_ref());
|
||||||
|
|
||||||
|
let mut data = Box::new(Bytes::from_static(b"test"));
|
||||||
|
assert!(data.is_complete_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());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn take_policy() {
|
||||||
|
let mut data = Bytes::from_static(b"test");
|
||||||
|
// first call returns chunk
|
||||||
|
assert_eq!(data.take_complete_body(), b"test".as_ref());
|
||||||
|
// second call returns empty
|
||||||
|
assert_eq!(data.take_complete_body(), b"".as_ref());
|
||||||
|
|
||||||
|
let waker = futures_util::task::noop_waker();
|
||||||
|
let mut cx = Context::from_waker(&waker);
|
||||||
|
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
|
||||||
// test is kept to demonstrate equivalence of Any trait
|
// test is kept to demonstrate equivalence of Any trait
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
|
@@ -40,4 +40,14 @@ impl MessageBody for None {
|
|||||||
) -> Poll<Option<Result<Bytes, Self::Error>>> {
|
) -> Poll<Option<Result<Bytes, Self::Error>>> {
|
||||||
Poll::Ready(Option::None)
|
Poll::Ready(Option::None)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn is_complete_body(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn take_complete_body(&mut self) -> Bytes {
|
||||||
|
Bytes::new()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -53,32 +53,32 @@ impl<B: MessageBody> Encoder<B> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
|| encoding == ContentEncoding::Identity
|
|| encoding == ContentEncoding::Identity
|
||||||
|| encoding == ContentEncoding::Auto);
|
|| encoding == ContentEncoding::Auto);
|
||||||
|
|
||||||
match body.size() {
|
// no need to compress an empty body
|
||||||
// no need to compress an empty body
|
if matches!(body.size(), BodySize::None) {
|
||||||
BodySize::None => return Self::none(),
|
return Self::none();
|
||||||
|
|
||||||
// we cannot assume that Sized is not a stream
|
|
||||||
BodySize::Sized(_) | BodySize::Stream => {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO potentially some optimisation for single-chunk responses here by trying to read the
|
let body = if body.is_complete_body() {
|
||||||
// payload eagerly, stopping after 2 polls if the first is a chunk and the second is None
|
let body = body.take_complete_body();
|
||||||
|
EncoderBody::Full { body }
|
||||||
|
} else {
|
||||||
|
EncoderBody::Stream { body }
|
||||||
|
};
|
||||||
|
|
||||||
if can_encode {
|
if can_encode {
|
||||||
// Modify response body only if encoder is set
|
// Modify response body only if encoder is set
|
||||||
if let Some(enc) = ContentEncoder::encoder(encoding) {
|
if let Some(enc) = ContentEncoder::encoder(encoding) {
|
||||||
update_head(encoding, head);
|
update_head(encoding, head);
|
||||||
head.no_chunking(false);
|
|
||||||
|
|
||||||
return Encoder {
|
return Encoder {
|
||||||
body: EncoderBody::Stream { body },
|
body,
|
||||||
encoder: Some(enc),
|
encoder: Some(enc),
|
||||||
fut: None,
|
fut: None,
|
||||||
eof: false,
|
eof: false,
|
||||||
@@ -87,7 +87,7 @@ impl<B: MessageBody> Encoder<B> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Encoder {
|
Encoder {
|
||||||
body: EncoderBody::Stream { body },
|
body,
|
||||||
encoder: None,
|
encoder: None,
|
||||||
fut: None,
|
fut: None,
|
||||||
eof: false,
|
eof: false,
|
||||||
@@ -99,6 +99,7 @@ pin_project! {
|
|||||||
#[project = EncoderBodyProj]
|
#[project = EncoderBodyProj]
|
||||||
enum EncoderBody<B> {
|
enum EncoderBody<B> {
|
||||||
None,
|
None,
|
||||||
|
Full { body: Bytes },
|
||||||
Stream { #[pin] body: B },
|
Stream { #[pin] body: B },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -112,6 +113,7 @@ where
|
|||||||
fn size(&self) -> BodySize {
|
fn size(&self) -> BodySize {
|
||||||
match self {
|
match self {
|
||||||
EncoderBody::None => BodySize::None,
|
EncoderBody::None => BodySize::None,
|
||||||
|
EncoderBody::Full { body } => body.size(),
|
||||||
EncoderBody::Stream { body } => body.size(),
|
EncoderBody::Stream { body } => body.size(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -122,12 +124,32 @@ where
|
|||||||
) -> Poll<Option<Result<Bytes, Self::Error>>> {
|
) -> Poll<Option<Result<Bytes, Self::Error>>> {
|
||||||
match self.project() {
|
match self.project() {
|
||||||
EncoderBodyProj::None => Poll::Ready(None),
|
EncoderBodyProj::None => Poll::Ready(None),
|
||||||
|
EncoderBodyProj::Full { body } => {
|
||||||
|
Pin::new(body).poll_next(cx).map_err(|err| match err {})
|
||||||
|
}
|
||||||
EncoderBodyProj::Stream { body } => body
|
EncoderBodyProj::Stream { body } => body
|
||||||
.poll_next(cx)
|
.poll_next(cx)
|
||||||
.map_err(|err| EncoderError::Body(err.into())),
|
.map_err(|err| EncoderError::Body(err.into())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_complete_body(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
EncoderBody::None => true,
|
||||||
|
EncoderBody::Full { .. } => true,
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<B> MessageBody for Encoder<B>
|
impl<B> MessageBody for Encoder<B>
|
||||||
@@ -137,10 +159,10 @@ where
|
|||||||
type Error = EncoderError;
|
type Error = EncoderError;
|
||||||
|
|
||||||
fn size(&self) -> BodySize {
|
fn size(&self) -> BodySize {
|
||||||
if self.encoder.is_none() {
|
if self.encoder.is_some() {
|
||||||
self.body.size()
|
|
||||||
} else {
|
|
||||||
BodySize::Stream
|
BodySize::Stream
|
||||||
|
} else {
|
||||||
|
self.body.size()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,6 +233,22 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_complete_body(&self) -> bool {
|
||||||
|
if self.encoder.is_some() {
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
self.body.is_complete_body()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_head(encoding: ContentEncoding, head: &mut ResponseHead) {
|
fn update_head(encoding: ContentEncoding, head: &mut ResponseHead) {
|
||||||
@@ -218,6 +256,8 @@ fn update_head(encoding: ContentEncoding, head: &mut ResponseHead) {
|
|||||||
header::CONTENT_ENCODING,
|
header::CONTENT_ENCODING,
|
||||||
HeaderValue::from_static(encoding.as_str()),
|
HeaderValue::from_static(encoding.as_str()),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
head.no_chunking(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ContentEncoder {
|
enum ContentEncoder {
|
||||||
|
@@ -19,7 +19,7 @@ impl Extensions {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn new() -> Extensions {
|
pub fn new() -> Extensions {
|
||||||
Extensions {
|
Extensions {
|
||||||
map: AHashMap::default(),
|
map: AHashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -19,13 +19,13 @@ use h2::{
|
|||||||
server::{Connection, SendResponse},
|
server::{Connection, SendResponse},
|
||||||
Ping, PingPong,
|
Ping, PingPong,
|
||||||
};
|
};
|
||||||
use http::header::{HeaderValue, CONNECTION, CONTENT_LENGTH, DATE, TRANSFER_ENCODING};
|
|
||||||
use log::{error, trace};
|
use log::{error, trace};
|
||||||
use pin_project_lite::pin_project;
|
use pin_project_lite::pin_project;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
body::{BodySize, BoxBody, MessageBody},
|
body::{BodySize, BoxBody, MessageBody},
|
||||||
config::ServiceConfig,
|
config::ServiceConfig,
|
||||||
|
header::{HeaderValue, CONNECTION, CONTENT_LENGTH, DATE, TRANSFER_ENCODING},
|
||||||
service::HttpFlow,
|
service::HttpFlow,
|
||||||
Extensions, OnConnectData, Payload, Request, Response, ResponseHead,
|
Extensions, OnConnectData, Payload, Request, Response, ResponseHead,
|
||||||
};
|
};
|
||||||
@@ -217,25 +217,28 @@ where
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
// poll response body and send chunks to client.
|
// poll response body and send chunks to client
|
||||||
actix_rt::pin!(body);
|
actix_rt::pin!(body);
|
||||||
|
|
||||||
while let Some(res) = poll_fn(|cx| body.as_mut().poll_next(cx)).await {
|
while let Some(res) = poll_fn(|cx| body.as_mut().poll_next(cx)).await {
|
||||||
let mut chunk = res.map_err(|err| DispatchError::ResponseBody(err.into()))?;
|
let mut chunk = res.map_err(|err| DispatchError::ResponseBody(err.into()))?;
|
||||||
|
|
||||||
'send: loop {
|
'send: loop {
|
||||||
|
let chunk_size = cmp::min(chunk.len(), CHUNK_SIZE);
|
||||||
|
|
||||||
// reserve enough space and wait for stream ready.
|
// reserve enough space and wait for stream ready.
|
||||||
stream.reserve_capacity(cmp::min(chunk.len(), CHUNK_SIZE));
|
stream.reserve_capacity(chunk_size);
|
||||||
|
|
||||||
match poll_fn(|cx| stream.poll_capacity(cx)).await {
|
match poll_fn(|cx| stream.poll_capacity(cx)).await {
|
||||||
// No capacity left. drop body and return.
|
// No capacity left. drop body and return.
|
||||||
None => return Ok(()),
|
None => return Ok(()),
|
||||||
Some(res) => {
|
|
||||||
// Split chuck to writeable size and send to client.
|
|
||||||
let cap = res.map_err(DispatchError::SendData)?;
|
|
||||||
|
|
||||||
|
Some(Err(err)) => return Err(DispatchError::SendData(err)),
|
||||||
|
|
||||||
|
Some(Ok(cap)) => {
|
||||||
|
// split chunk to writeable size and send to client
|
||||||
let len = chunk.len();
|
let len = chunk.len();
|
||||||
let bytes = chunk.split_to(cmp::min(cap, len));
|
let bytes = chunk.split_to(cmp::min(len, cap));
|
||||||
|
|
||||||
stream
|
stream
|
||||||
.send_data(bytes, false)
|
.send_data(bytes, false)
|
||||||
|
@@ -44,13 +44,12 @@ pub trait Head: Default + 'static {
|
|||||||
F: FnOnce(&MessagePool<Self>) -> R;
|
F: FnOnce(&MessagePool<Self>) -> R;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct RequestHead {
|
pub struct RequestHead {
|
||||||
pub method: Method,
|
pub method: Method,
|
||||||
pub uri: Uri,
|
pub uri: Uri,
|
||||||
pub version: Version,
|
pub version: Version,
|
||||||
pub headers: HeaderMap,
|
pub headers: HeaderMap,
|
||||||
pub extensions: RefCell<Extensions>,
|
|
||||||
pub peer_addr: Option<net::SocketAddr>,
|
pub peer_addr: Option<net::SocketAddr>,
|
||||||
flags: Flags,
|
flags: Flags,
|
||||||
}
|
}
|
||||||
@@ -62,7 +61,6 @@ impl Default for RequestHead {
|
|||||||
uri: Uri::default(),
|
uri: Uri::default(),
|
||||||
version: Version::HTTP_11,
|
version: Version::HTTP_11,
|
||||||
headers: HeaderMap::with_capacity(16),
|
headers: HeaderMap::with_capacity(16),
|
||||||
extensions: RefCell::new(Extensions::new()),
|
|
||||||
peer_addr: None,
|
peer_addr: None,
|
||||||
flags: Flags::empty(),
|
flags: Flags::empty(),
|
||||||
}
|
}
|
||||||
@@ -73,7 +71,6 @@ impl Head for RequestHead {
|
|||||||
fn clear(&mut self) {
|
fn clear(&mut self) {
|
||||||
self.flags = Flags::empty();
|
self.flags = Flags::empty();
|
||||||
self.headers.clear();
|
self.headers.clear();
|
||||||
self.extensions.get_mut().clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_pool<F, R>(f: F) -> R
|
fn with_pool<F, R>(f: F) -> R
|
||||||
@@ -85,18 +82,6 @@ impl Head for RequestHead {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl RequestHead {
|
impl RequestHead {
|
||||||
/// 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()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read the message headers.
|
/// Read the message headers.
|
||||||
pub fn headers(&self) -> &HeaderMap {
|
pub fn headers(&self) -> &HeaderMap {
|
||||||
&self.headers
|
&self.headers
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
//! HTTP requests.
|
//! HTTP requests.
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
cell::{Ref, RefMut},
|
cell::{Ref, RefCell, RefMut},
|
||||||
fmt, net,
|
fmt, mem, net,
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
str,
|
str,
|
||||||
};
|
};
|
||||||
@@ -22,6 +22,7 @@ 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>>,
|
||||||
|
pub(crate) req_data: RefCell<Extensions>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P> HttpMessage for Request<P> {
|
impl<P> HttpMessage for Request<P> {
|
||||||
@@ -33,19 +34,19 @@ impl<P> HttpMessage for Request<P> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn take_payload(&mut self) -> Payload<P> {
|
fn take_payload(&mut self) -> Payload<P> {
|
||||||
std::mem::replace(&mut self.payload, Payload::None)
|
mem::replace(&mut self.payload, Payload::None)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Request extensions
|
/// Request extensions
|
||||||
#[inline]
|
#[inline]
|
||||||
fn extensions(&self) -> Ref<'_, Extensions> {
|
fn extensions(&self) -> Ref<'_, Extensions> {
|
||||||
self.head.extensions()
|
self.req_data.borrow()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mutable reference to a the request's extensions
|
/// Mutable reference to a the request's extensions
|
||||||
#[inline]
|
#[inline]
|
||||||
fn extensions_mut(&self) -> RefMut<'_, Extensions> {
|
fn extensions_mut(&self) -> RefMut<'_, Extensions> {
|
||||||
self.head.extensions_mut()
|
self.req_data.borrow_mut()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,6 +55,7 @@ impl From<Message<RequestHead>> for Request<PayloadStream> {
|
|||||||
Request {
|
Request {
|
||||||
head,
|
head,
|
||||||
payload: Payload::None,
|
payload: Payload::None,
|
||||||
|
req_data: RefCell::new(Extensions::default()),
|
||||||
conn_data: None,
|
conn_data: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -65,6 +67,7 @@ impl Request<PayloadStream> {
|
|||||||
Request {
|
Request {
|
||||||
head: Message::new(),
|
head: Message::new(),
|
||||||
payload: Payload::None,
|
payload: Payload::None,
|
||||||
|
req_data: RefCell::new(Extensions::default()),
|
||||||
conn_data: None,
|
conn_data: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -76,6 +79,7 @@ impl<P> Request<P> {
|
|||||||
Request {
|
Request {
|
||||||
payload,
|
payload,
|
||||||
head: Message::new(),
|
head: Message::new(),
|
||||||
|
req_data: RefCell::new(Extensions::default()),
|
||||||
conn_data: None,
|
conn_data: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -88,6 +92,7 @@ impl<P> Request<P> {
|
|||||||
Request {
|
Request {
|
||||||
payload,
|
payload,
|
||||||
head: self.head,
|
head: self.head,
|
||||||
|
req_data: self.req_data,
|
||||||
conn_data: self.conn_data,
|
conn_data: self.conn_data,
|
||||||
},
|
},
|
||||||
pl,
|
pl,
|
||||||
@@ -101,7 +106,7 @@ impl<P> Request<P> {
|
|||||||
|
|
||||||
/// Get request's payload
|
/// Get request's payload
|
||||||
pub fn take_payload(&mut self) -> Payload<P> {
|
pub fn take_payload(&mut self) -> Payload<P> {
|
||||||
std::mem::replace(&mut self.payload, Payload::None)
|
mem::replace(&mut self.payload, Payload::None)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Split request into request head and payload
|
/// Split request into request head and payload
|
||||||
@@ -124,7 +129,7 @@ impl<P> Request<P> {
|
|||||||
|
|
||||||
/// Mutable reference to the message's headers.
|
/// Mutable reference to the message's headers.
|
||||||
pub fn headers_mut(&mut self) -> &mut HeaderMap {
|
pub fn headers_mut(&mut self) -> &mut HeaderMap {
|
||||||
&mut self.head_mut().headers
|
&mut self.head.headers
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Request's uri.
|
/// Request's uri.
|
||||||
@@ -136,7 +141,7 @@ impl<P> Request<P> {
|
|||||||
/// Mutable reference to the request's uri.
|
/// Mutable reference to the request's uri.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn uri_mut(&mut self) -> &mut Uri {
|
pub fn uri_mut(&mut self) -> &mut Uri {
|
||||||
&mut self.head_mut().uri
|
&mut self.head.uri
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read the Request method.
|
/// Read the Request method.
|
||||||
@@ -198,6 +203,11 @@ impl<P> Request<P> {
|
|||||||
pub fn take_conn_data(&mut self) -> Option<Rc<Extensions>> {
|
pub fn take_conn_data(&mut self) -> Option<Rc<Extensions>> {
|
||||||
self.conn_data.take()
|
self.conn_data.take()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the request data container, leaving an empty one in it's place.
|
||||||
|
pub fn take_req_data(&mut self) -> Extensions {
|
||||||
|
mem::take(&mut self.req_data.get_mut())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P> fmt::Debug for Request<P> {
|
impl<P> fmt::Debug for Request<P> {
|
||||||
|
@@ -101,7 +101,7 @@ async fn test_h2_1() -> io::Result<()> {
|
|||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_h2_body() -> io::Result<()> {
|
async fn test_h2_body() -> io::Result<()> {
|
||||||
let data = "HELLOWORLD".to_owned().repeat(64 * 1024);
|
let data = "HELLOWORLD".to_owned().repeat(64 * 1024); // 640 KiB
|
||||||
let mut srv = test_server(move || {
|
let mut srv = test_server(move || {
|
||||||
HttpService::build()
|
HttpService::build()
|
||||||
.h2(|mut req: Request<_>| async move {
|
.h2(|mut req: Request<_>| async move {
|
||||||
|
@@ -3,6 +3,10 @@
|
|||||||
## Unreleased - 2021-xx-xx
|
## Unreleased - 2021-xx-xx
|
||||||
|
|
||||||
|
|
||||||
|
## 0.4.0-beta.10 - 2021-12-11
|
||||||
|
* 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]
|
||||||
|
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "actix-multipart"
|
name = "actix-multipart"
|
||||||
version = "0.4.0-beta.9"
|
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"]
|
||||||
@@ -14,8 +14,8 @@ name = "actix_multipart"
|
|||||||
path = "src/lib.rs"
|
path = "src/lib.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix-web = { version = "4.0.0-beta.11", default-features = false }
|
|
||||||
actix-utils = "3.0.0"
|
actix-utils = "3.0.0"
|
||||||
|
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.14"
|
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", 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.9)
|
[](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.9)
|
[](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)
|
||||||
|
|
||||||
|
@@ -3,6 +3,10 @@
|
|||||||
## Unreleased - 2021-xx-xx
|
## Unreleased - 2021-xx-xx
|
||||||
|
|
||||||
|
|
||||||
|
## 0.1.0-beta.8 - 2021-12-11
|
||||||
|
* 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]
|
||||||
|
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "actix-test"
|
name = "actix-test"
|
||||||
version = "0.1.0-beta.7"
|
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.14"
|
actix-http = "3.0.0-beta.15"
|
||||||
actix-http-test = "3.0.0-beta.7"
|
actix-http-test = "3.0.0-beta.9"
|
||||||
|
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.11", default-features = false, features = ["cookies"] }
|
actix-web = { version = "4.0.0-beta.14", default-features = false, features = ["cookies"] }
|
||||||
actix-rt = "2.1"
|
awc = { version = "3.0.0-beta.13", default-features = false, features = ["cookies"] }
|
||||||
awc = { version = "3.0.0-beta.11", 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 = [] }
|
||||||
|
@@ -1,6 +1,9 @@
|
|||||||
# Changes
|
# Changes
|
||||||
|
|
||||||
## Unreleased - 2021-xx-xx
|
## Unreleased - 2021-xx-xx
|
||||||
|
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "actix-web-actors"
|
name = "actix-web-actors"
|
||||||
version = "4.0.0-beta.7"
|
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,8 +16,8 @@ path = "src/lib.rs"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
actix = { version = "0.12.0", default-features = false }
|
actix = { version = "0.12.0", default-features = false }
|
||||||
actix-codec = "0.4.1"
|
actix-codec = "0.4.1"
|
||||||
actix-http = "3.0.0-beta.14"
|
actix-http = "3.0.0-beta.15"
|
||||||
actix-web = { version = "4.0.0-beta.11", default-features = false }
|
actix-web = { version = "4.0.0-beta.14", default-features = false }
|
||||||
|
|
||||||
bytes = "1"
|
bytes = "1"
|
||||||
bytestring = "1"
|
bytestring = "1"
|
||||||
@@ -27,8 +27,8 @@ tokio = { version = "1", features = ["sync"] }
|
|||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
actix-rt = "2.2"
|
actix-rt = "2.2"
|
||||||
actix-test = "0.1.0-beta.7"
|
actix-test = "0.1.0-beta.8"
|
||||||
|
awc = { version = "3.0.0-beta.13", default-features = false }
|
||||||
|
|
||||||
awc = { version = "3.0.0-beta.11", 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.7)
|
[](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.7)
|
[](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)
|
||||||
|
|
||||||
|
@@ -3,6 +3,10 @@
|
|||||||
## Unreleased - 2021-xx-xx
|
## Unreleased - 2021-xx-xx
|
||||||
|
|
||||||
|
|
||||||
|
## 0.5.0-beta.6 - 2021-12-11
|
||||||
|
* 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]
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "actix-web-codegen"
|
name = "actix-web-codegen"
|
||||||
version = "0.5.0-beta.5"
|
version = "0.5.0-beta.6"
|
||||||
description = "Routing and runtime macros for Actix Web"
|
description = "Routing and runtime macros for Actix Web"
|
||||||
homepage = "https://actix.rs"
|
homepage = "https://actix.rs"
|
||||||
repository = "https://github.com/actix/actix-web.git"
|
repository = "https://github.com/actix/actix-web.git"
|
||||||
@@ -21,11 +21,11 @@ proc-macro2 = "1"
|
|||||||
actix-router = "0.5.0-beta.2"
|
actix-router = "0.5.0-beta.2"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
actix-rt = "2.2"
|
|
||||||
actix-macros = "0.2.3"
|
actix-macros = "0.2.3"
|
||||||
actix-test = "0.1.0-beta.7"
|
actix-rt = "2.2"
|
||||||
|
actix-test = "0.1.0-beta.8"
|
||||||
actix-utils = "3.0.0"
|
actix-utils = "3.0.0"
|
||||||
actix-web = "4.0.0-beta.11"
|
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"
|
||||||
|
@@ -3,11 +3,11 @@
|
|||||||
> Routing and runtime macros for Actix Web.
|
> Routing and runtime macros for Actix Web.
|
||||||
|
|
||||||
[](https://crates.io/crates/actix-web-codegen)
|
[](https://crates.io/crates/actix-web-codegen)
|
||||||
[](https://docs.rs/actix-web-codegen/0.5.0-beta.5)
|
[](https://docs.rs/actix-web-codegen/0.5.0-beta.6)
|
||||||
[](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-codegen/0.5.0-beta.5)
|
[](https://deps.rs/crate/actix-web-codegen/0.5.0-beta.6)
|
||||||
[](https://crates.io/crates/actix-web-codegen)
|
[](https://crates.io/crates/actix-web-codegen)
|
||||||
[](https://discord.gg/NWpN5mmg3x)
|
[](https://discord.gg/NWpN5mmg3x)
|
||||||
|
|
||||||
|
@@ -3,6 +3,10 @@
|
|||||||
## Unreleased - 2021-xx-xx
|
## Unreleased - 2021-xx-xx
|
||||||
|
|
||||||
|
|
||||||
|
## 3.0.0-beta.13 - 2021-12-11
|
||||||
|
* 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]
|
||||||
|
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "awc"
|
name = "awc"
|
||||||
version = "3.0.0-beta.12"
|
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,7 +60,7 @@ 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.14"
|
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-rc.1", features = ["connect", "uri"] }
|
actix-tls = { version = "3.0.0-rc.1", features = ["connect", "uri"] }
|
||||||
actix-utils = "3.0.0"
|
actix-utils = "3.0.0"
|
||||||
@@ -72,7 +72,7 @@ cfg-if = "1"
|
|||||||
derive_more = "0.99.5"
|
derive_more = "0.99.5"
|
||||||
futures-core = { version = "0.3.7", default-features = false }
|
futures-core = { version = "0.3.7", default-features = false }
|
||||||
futures-util = { version = "0.3.7", default-features = false }
|
futures-util = { version = "0.3.7", default-features = false }
|
||||||
h2 = "0.3"
|
h2 = "0.3.9"
|
||||||
http = "0.2.5"
|
http = "0.2.5"
|
||||||
itoa = "0.4"
|
itoa = "0.4"
|
||||||
log =" 0.4"
|
log =" 0.4"
|
||||||
@@ -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-web = { version = "4.0.0-beta.11", features = ["openssl"] }
|
actix-http = { version = "3.0.0-beta.15", features = ["openssl"] }
|
||||||
actix-http = { version = "3.0.0-beta.14", features = ["openssl"] }
|
actix-http-test = { version = "3.0.0-beta.9", features = ["openssl"] }
|
||||||
actix-http-test = { version = "3.0.0-beta.7", features = ["openssl"] }
|
|
||||||
actix-utils = "3.0.0"
|
|
||||||
actix-server = "2.0.0-rc.1"
|
actix-server = "2.0.0-rc.1"
|
||||||
|
actix-test = { version = "0.1.0-beta.8", features = ["openssl", "rustls"] }
|
||||||
actix-tls = { version = "3.0.0-rc.1", features = ["openssl", "rustls"] }
|
actix-tls = { version = "3.0.0-rc.1", features = ["openssl", "rustls"] }
|
||||||
actix-test = { version = "0.1.0-beta.7", features = ["openssl", "rustls"] }
|
actix-utils = "3.0.0"
|
||||||
|
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.12)
|
[](https://docs.rs/awc/3.0.0-beta.13)
|
||||||

|

|
||||||
[](https://deps.rs/crate/awc/3.0.0-beta.12)
|
[](https://deps.rs/crate/awc/3.0.0-beta.13)
|
||||||
[](https://discord.gg/NWpN5mmg3x)
|
[](https://discord.gg/NWpN5mmg3x)
|
||||||
|
|
||||||
## Documentation & Resources
|
## Documentation & Resources
|
||||||
|
31
scripts/bump
31
scripts/bump
@@ -41,6 +41,8 @@ cat "$CHANGELOG_FILE" |
|
|||||||
# if word count of changelog chunk is 0 then insert filler changelog chunk
|
# if word count of changelog chunk is 0 then insert filler changelog chunk
|
||||||
if [ "$(wc -w "$CHANGE_CHUNK_FILE" | awk '{ print $1 }')" = "0" ]; then
|
if [ "$(wc -w "$CHANGE_CHUNK_FILE" | awk '{ print $1 }')" = "0" ]; then
|
||||||
echo "* No significant changes since \`$CURRENT_VERSION\`." >"$CHANGE_CHUNK_FILE"
|
echo "* No significant changes since \`$CURRENT_VERSION\`." >"$CHANGE_CHUNK_FILE"
|
||||||
|
echo >>"$CHANGE_CHUNK_FILE"
|
||||||
|
echo >>"$CHANGE_CHUNK_FILE"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -n "${2-}" ]; then
|
if [ -n "${2-}" ]; then
|
||||||
@@ -82,8 +84,33 @@ rm -f $README_FILE.bak
|
|||||||
echo "manifest, changelog, and readme updated"
|
echo "manifest, changelog, and readme updated"
|
||||||
echo
|
echo
|
||||||
echo "check other references:"
|
echo "check other references:"
|
||||||
rg "$PACKAGE_NAME =" || true
|
rg --glob='**/Cargo.toml' "\
|
||||||
rg "package = \"$PACKAGE_NAME\"" || true
|
${PACKAGE_NAME} ?= ?\"[^\"]+\"\
|
||||||
|
|${PACKAGE_NAME} ?=.*version ?= ?\"([^\"]+)\"\
|
||||||
|
|package ?= ?\"${PACKAGE_NAME}\".*version ?= ?\"([^\"]+)\"\
|
||||||
|
|version ?= ?\"([^\"]+)\".*package ?= ?\"${PACKAGE_NAME}\"" || true
|
||||||
|
|
||||||
|
echo
|
||||||
|
read -p "Update all references: (y/N) " UPDATE_REFERENCES
|
||||||
|
UPDATE_REFERENCES="${UPDATE_REFERENCES:-n}"
|
||||||
|
|
||||||
|
if [ "$UPDATE_REFERENCES" = 'y' ] || [ "$UPDATE_REFERENCES" = 'Y' ]; then
|
||||||
|
|
||||||
|
for f in $(fd Cargo.toml); do
|
||||||
|
sed -i.bak -E \
|
||||||
|
"s/^(${PACKAGE_NAME} ?= ?\")[^\"]+(\")$/\1${NEW_VERSION}\2/g" $f
|
||||||
|
sed -i.bak -E \
|
||||||
|
"s/^(${PACKAGE_NAME} ?=.*version ?= ?\")[^\"]+(\".*)$/\1${NEW_VERSION}\2/g" $f
|
||||||
|
sed -i.bak -E \
|
||||||
|
"s/^(.*package ?= ?\"${PACKAGE_NAME}\".*version ?= ?\")[^\"]+(\".*)$/\1${NEW_VERSION}\2/g" $f
|
||||||
|
sed -i.bak -E \
|
||||||
|
"s/^(.*version ?= ?\")[^\"]+(\".*package ?= ?\"${PACKAGE_NAME}\".*)$/\1${NEW_VERSION}\2/g" $f
|
||||||
|
|
||||||
|
# remove backup file
|
||||||
|
rm -f $f.bak
|
||||||
|
done
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
if [ $MACOS ]; then
|
if [ $MACOS ]; then
|
||||||
printf "prepare $PACKAGE_NAME release $NEW_VERSION" | pbcopy
|
printf "prepare $PACKAGE_NAME release $NEW_VERSION" | pbcopy
|
||||||
|
@@ -198,6 +198,7 @@ where
|
|||||||
actix_service::forward_ready!(service);
|
actix_service::forward_ready!(service);
|
||||||
|
|
||||||
fn call(&self, mut req: Request) -> Self::Future {
|
fn call(&self, mut req: Request) -> Self::Future {
|
||||||
|
let req_data = Rc::new(RefCell::new(req.take_req_data()));
|
||||||
let conn_data = req.take_conn_data();
|
let conn_data = req.take_conn_data();
|
||||||
let (head, payload) = req.into_parts();
|
let (head, payload) = req.into_parts();
|
||||||
|
|
||||||
@@ -207,6 +208,7 @@ where
|
|||||||
inner.path.reset();
|
inner.path.reset();
|
||||||
inner.head = head;
|
inner.head = head;
|
||||||
inner.conn_data = conn_data;
|
inner.conn_data = conn_data;
|
||||||
|
inner.req_data = req_data;
|
||||||
req
|
req
|
||||||
} else {
|
} else {
|
||||||
HttpRequest::new(
|
HttpRequest::new(
|
||||||
@@ -215,6 +217,7 @@ where
|
|||||||
self.app_state.clone(),
|
self.app_state.clone(),
|
||||||
self.app_data.clone(),
|
self.app_data.clone(),
|
||||||
conn_data,
|
conn_data,
|
||||||
|
req_data,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
self.service.call(ServiceRequest::new(req, payload))
|
self.service.call(ServiceRequest::new(req, payload))
|
||||||
|
12
src/info.rs
12
src/info.rs
@@ -1,4 +1,4 @@
|
|||||||
use std::{cell::Ref, convert::Infallible, net::SocketAddr};
|
use std::{convert::Infallible, net::SocketAddr};
|
||||||
|
|
||||||
use actix_utils::future::{err, ok, Ready};
|
use actix_utils::future::{err, ok, Ready};
|
||||||
use derive_more::{Display, Error};
|
use derive_more::{Display, Error};
|
||||||
@@ -72,15 +72,7 @@ pub struct ConnectionInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ConnectionInfo {
|
impl ConnectionInfo {
|
||||||
/// Create *ConnectionInfo* instance for a request.
|
pub(crate) fn new(req: &RequestHead, cfg: &AppConfig) -> ConnectionInfo {
|
||||||
pub fn get<'a>(req: &'a RequestHead, cfg: &AppConfig) -> Ref<'a, Self> {
|
|
||||||
if !req.extensions().contains::<ConnectionInfo>() {
|
|
||||||
req.extensions_mut().insert(ConnectionInfo::new(req, cfg));
|
|
||||||
}
|
|
||||||
Ref::map(req.extensions(), |e| e.get().unwrap())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new(req: &RequestHead, cfg: &AppConfig) -> ConnectionInfo {
|
|
||||||
let mut host = None;
|
let mut host = None;
|
||||||
let mut scheme = None;
|
let mut scheme = None;
|
||||||
let mut realip_remote_addr = None;
|
let mut realip_remote_addr = None;
|
||||||
|
@@ -38,6 +38,7 @@ pub(crate) struct HttpRequestInner {
|
|||||||
pub(crate) path: Path<Url>,
|
pub(crate) path: Path<Url>,
|
||||||
pub(crate) app_data: SmallVec<[Rc<Extensions>; 4]>,
|
pub(crate) app_data: SmallVec<[Rc<Extensions>; 4]>,
|
||||||
pub(crate) conn_data: Option<Rc<Extensions>>,
|
pub(crate) conn_data: Option<Rc<Extensions>>,
|
||||||
|
pub(crate) req_data: Rc<RefCell<Extensions>>,
|
||||||
app_state: Rc<AppInitServiceState>,
|
app_state: Rc<AppInitServiceState>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,6 +50,7 @@ impl HttpRequest {
|
|||||||
app_state: Rc<AppInitServiceState>,
|
app_state: Rc<AppInitServiceState>,
|
||||||
app_data: Rc<Extensions>,
|
app_data: Rc<Extensions>,
|
||||||
conn_data: Option<Rc<Extensions>>,
|
conn_data: Option<Rc<Extensions>>,
|
||||||
|
req_data: Rc<RefCell<Extensions>>,
|
||||||
) -> HttpRequest {
|
) -> HttpRequest {
|
||||||
let mut data = SmallVec::<[Rc<Extensions>; 4]>::new();
|
let mut data = SmallVec::<[Rc<Extensions>; 4]>::new();
|
||||||
data.push(app_data);
|
data.push(app_data);
|
||||||
@@ -60,6 +62,7 @@ impl HttpRequest {
|
|||||||
app_state,
|
app_state,
|
||||||
app_data: data,
|
app_data: data,
|
||||||
conn_data,
|
conn_data,
|
||||||
|
req_data,
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -156,16 +159,12 @@ impl HttpRequest {
|
|||||||
self.resource_map().match_name(self.path())
|
self.resource_map().match_name(self.path())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Request extensions
|
pub fn req_data(&self) -> Ref<'_, Extensions> {
|
||||||
#[inline]
|
self.inner.req_data.borrow()
|
||||||
pub fn extensions(&self) -> Ref<'_, Extensions> {
|
|
||||||
self.head().extensions()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mutable reference to a the request's extensions
|
pub fn req_data_mut(&self) -> RefMut<'_, Extensions> {
|
||||||
#[inline]
|
self.inner.req_data.borrow_mut()
|
||||||
pub fn extensions_mut(&self) -> RefMut<'_, Extensions> {
|
|
||||||
self.head().extensions_mut()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a reference a piece of connection data set in an [on-connect] callback.
|
/// Returns a reference a piece of connection data set in an [on-connect] callback.
|
||||||
@@ -248,7 +247,12 @@ impl HttpRequest {
|
|||||||
/// borrowed.
|
/// borrowed.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn connection_info(&self) -> Ref<'_, ConnectionInfo> {
|
pub fn connection_info(&self) -> Ref<'_, ConnectionInfo> {
|
||||||
ConnectionInfo::get(self.head(), self.app_config())
|
if !self.extensions().contains::<ConnectionInfo>() {
|
||||||
|
let info = ConnectionInfo::new(self.head(), &*self.app_config());
|
||||||
|
self.extensions_mut().insert(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref::map(self.extensions(), |e| e.get().unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// App config
|
/// App config
|
||||||
@@ -321,21 +325,18 @@ impl HttpMessage for HttpRequest {
|
|||||||
type Stream = ();
|
type Stream = ();
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
/// Returns Request's headers.
|
|
||||||
fn headers(&self) -> &HeaderMap {
|
fn headers(&self) -> &HeaderMap {
|
||||||
&self.head().headers
|
&self.head().headers
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Request extensions
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn extensions(&self) -> Ref<'_, Extensions> {
|
fn extensions(&self) -> Ref<'_, Extensions> {
|
||||||
self.inner.head.extensions()
|
self.req_data()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mutable reference to a the request's extensions
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn extensions_mut(&self) -> RefMut<'_, Extensions> {
|
fn extensions_mut(&self) -> RefMut<'_, Extensions> {
|
||||||
self.inner.head.extensions_mut()
|
self.req_data_mut()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@@ -348,14 +349,15 @@ impl Drop for HttpRequest {
|
|||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
// if possible, contribute to current worker's HttpRequest allocation pool
|
// if possible, contribute to current worker's HttpRequest allocation pool
|
||||||
|
|
||||||
// This relies on no Weak<HttpRequestInner> exists anywhere.(There is none)
|
// This relies on no Weak<HttpRequestInner> exists anywhere. (There is none.)
|
||||||
if let Some(inner) = Rc::get_mut(&mut self.inner) {
|
if let Some(inner) = Rc::get_mut(&mut self.inner) {
|
||||||
if inner.app_state.pool().is_available() {
|
if inner.app_state.pool().is_available() {
|
||||||
// clear additional app_data and keep the root one for reuse.
|
// clear additional app_data and keep the root one for reuse.
|
||||||
inner.app_data.truncate(1);
|
inner.app_data.truncate(1);
|
||||||
// inner is borrowed mut here. get head's Extension mutably
|
|
||||||
// to reduce borrow check
|
// Inner is borrowed mut here and; get req data mutably to reduce borrow check. Also
|
||||||
inner.head.extensions.get_mut().clear();
|
// we know the req_data Rc will not have any cloned at this point to unwrap is okay.
|
||||||
|
Rc::get_mut(&mut inner.req_data).unwrap().get_mut().clear();
|
||||||
|
|
||||||
// a re-borrow of pool is necessary here.
|
// a re-borrow of pool is necessary here.
|
||||||
let req = self.inner.clone();
|
let req = self.inner.clone();
|
||||||
|
@@ -33,12 +33,11 @@ use crate::{dev::Payload, error::ErrorInternalServerError, Error, FromRequest, H
|
|||||||
/// req: HttpRequest,
|
/// req: HttpRequest,
|
||||||
/// opt_flag: Option<web::ReqData<FlagFromMiddleware>>,
|
/// opt_flag: Option<web::ReqData<FlagFromMiddleware>>,
|
||||||
/// ) -> impl Responder {
|
/// ) -> impl Responder {
|
||||||
/// // use an optional extractor if the middleware is
|
/// // use an option extractor if middleware is not guaranteed to add this type of req data
|
||||||
/// // not guaranteed to add this type of requests data
|
|
||||||
/// if let Some(flag) = opt_flag {
|
/// if let Some(flag) = opt_flag {
|
||||||
/// assert_eq!(&flag.into_inner(), req.extensions().get::<FlagFromMiddleware>().unwrap());
|
/// assert_eq!(&flag.into_inner(), req.req_data().get::<FlagFromMiddleware>().unwrap());
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// HttpResponse::Ok()
|
/// HttpResponse::Ok()
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
@@ -68,7 +67,7 @@ impl<T: Clone + 'static> FromRequest for ReqData<T> {
|
|||||||
type Future = Ready<Result<Self, Error>>;
|
type Future = Ready<Result<Self, Error>>;
|
||||||
|
|
||||||
fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
|
fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
|
||||||
if let Some(st) = req.extensions().get::<T>() {
|
if let Some(st) = req.req_data().get::<T>() {
|
||||||
ok(ReqData(st.clone()))
|
ok(ReqData(st.clone()))
|
||||||
} else {
|
} else {
|
||||||
log::debug!(
|
log::debug!(
|
||||||
|
@@ -194,7 +194,7 @@ impl ServiceRequest {
|
|||||||
/// Get *ConnectionInfo* for the current request.
|
/// Get *ConnectionInfo* for the current request.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn connection_info(&self) -> Ref<'_, ConnectionInfo> {
|
pub fn connection_info(&self) -> Ref<'_, ConnectionInfo> {
|
||||||
ConnectionInfo::get(self.head(), &*self.app_config())
|
self.req.connection_info()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a reference to the Path parameters.
|
/// Get a reference to the Path parameters.
|
||||||
@@ -410,6 +410,12 @@ impl<B> ServiceResponse<B> {
|
|||||||
self.response.headers_mut()
|
self.response.headers_mut()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Destructures `ServiceResponse` into request and response components.
|
||||||
|
#[inline]
|
||||||
|
pub fn into_parts(self) -> (HttpRequest, HttpResponse<B>) {
|
||||||
|
(self.request, self.response)
|
||||||
|
}
|
||||||
|
|
||||||
/// Extract response body
|
/// Extract response body
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn into_body(self) -> B {
|
pub fn into_body(self) -> B {
|
||||||
|
27
src/test.rs
27
src/test.rs
@@ -581,7 +581,14 @@ impl TestRequest {
|
|||||||
let app_state = AppInitServiceState::new(Rc::new(self.rmap), self.config.clone());
|
let app_state = AppInitServiceState::new(Rc::new(self.rmap), self.config.clone());
|
||||||
|
|
||||||
ServiceRequest::new(
|
ServiceRequest::new(
|
||||||
HttpRequest::new(self.path, head, app_state, Rc::new(self.app_data), None),
|
HttpRequest::new(
|
||||||
|
self.path,
|
||||||
|
head,
|
||||||
|
app_state,
|
||||||
|
Rc::new(self.app_data),
|
||||||
|
None,
|
||||||
|
Default::default(),
|
||||||
|
),
|
||||||
payload,
|
payload,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -599,7 +606,14 @@ impl TestRequest {
|
|||||||
|
|
||||||
let app_state = AppInitServiceState::new(Rc::new(self.rmap), self.config.clone());
|
let app_state = AppInitServiceState::new(Rc::new(self.rmap), self.config.clone());
|
||||||
|
|
||||||
HttpRequest::new(self.path, head, app_state, Rc::new(self.app_data), None)
|
HttpRequest::new(
|
||||||
|
self.path,
|
||||||
|
head,
|
||||||
|
app_state,
|
||||||
|
Rc::new(self.app_data),
|
||||||
|
None,
|
||||||
|
Default::default(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Complete request creation and generate `HttpRequest` and `Payload` instances
|
/// Complete request creation and generate `HttpRequest` and `Payload` instances
|
||||||
@@ -610,7 +624,14 @@ impl TestRequest {
|
|||||||
|
|
||||||
let app_state = AppInitServiceState::new(Rc::new(self.rmap), self.config.clone());
|
let app_state = AppInitServiceState::new(Rc::new(self.rmap), self.config.clone());
|
||||||
|
|
||||||
let req = HttpRequest::new(self.path, head, app_state, Rc::new(self.app_data), None);
|
let req = HttpRequest::new(
|
||||||
|
self.path,
|
||||||
|
head,
|
||||||
|
app_state,
|
||||||
|
Rc::new(self.app_data),
|
||||||
|
None,
|
||||||
|
Default::default(),
|
||||||
|
);
|
||||||
|
|
||||||
(req, payload)
|
(req, payload)
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user