diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index fa06a137a..a26842245 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -3,34 +3,40 @@ name: Bug Report
about: Create a bug report.
---
-Your issue may already be reported!
-Please search on the [Actix Web issue tracker](https://github.com/actix/actix-web/issues) before creating one.
+Your issue may already be reported! Please search on the [Actix Web issue tracker](https://github.com/actix/actix-web/issues) before creating one.
## Expected Behavior
+
## Current Behavior
+
## Possible Solution
+
## Steps to Reproduce (for bugs)
+
+
1.
2.
3.
4.
## Context
+
## Your Environment
+
- Rust Version (I.e, output of `rustc -V`):
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index d617cf708..e0d17fb27 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -2,12 +2,14 @@
## PR Type
+
+
PR_TYPE
-
## PR Checklist
+
@@ -17,11 +19,10 @@ PR_TYPE
- [ ] Format code with the latest stable rustfmt.
- [ ] (Team) Label with affected crates and semver status.
-
## Overview
+
-
diff --git a/.prettierrc.json b/.prettierrc.json
deleted file mode 100644
index 677ba8ef2..000000000
--- a/.prettierrc.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "proseWrap": "never"
-}
diff --git a/.prettierrc.yaml b/.prettierrc.yaml
new file mode 100644
index 000000000..7b5590248
--- /dev/null
+++ b/.prettierrc.yaml
@@ -0,0 +1 @@
+proseWrap: never
diff --git a/Cargo.toml b/Cargo.toml
index 26b5b91b2..65e3c6ae8 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -5,6 +5,7 @@ members = [
"actix-http-test",
"actix-http",
"actix-multipart",
+ "actix-multipart-derive",
"actix-router",
"actix-test",
"actix-web-actors",
@@ -27,6 +28,7 @@ actix-files = { path = "actix-files" }
actix-http = { path = "actix-http" }
actix-http-test = { path = "actix-http-test" }
actix-multipart = { path = "actix-multipart" }
+actix-multipart-derive = { path = "actix-multipart-derive" }
actix-router = { path = "actix-router" }
actix-test = { path = "actix-test" }
actix-web = { path = "actix-web" }
diff --git a/actix-files/CHANGES.md b/actix-files/CHANGES.md
index 6e57bf7a7..b4b2fd8c1 100644
--- a/actix-files/CHANGES.md
+++ b/actix-files/CHANGES.md
@@ -1,19 +1,24 @@
# Changes
## Unreleased - 2022-xx-xx
+
+## 0.6.3 - 2023-01-21
+
- XHTML files now use `Content-Disposition: inline` instead of `attachment`. [#2903]
- Minimum supported Rust version (MSRV) is now 1.59 due to transitive `time` dependency.
+- Update `tokio-uring` dependency to `0.4`.
[#2903]: https://github.com/actix/actix-web/pull/2903
## 0.6.2 - 2022-07-23
+
- Allow partial range responses for video content to start streaming sooner. [#2817]
- Minimum supported Rust version (MSRV) is now 1.57 due to transitive `time` dependency.
[#2817]: https://github.com/actix/actix-web/pull/2817
-
## 0.6.1 - 2022-06-11
+
- Add `NamedFile::{modified, metadata, content_type, content_disposition, encoding}()` getters. [#2021]
- Update `tokio-uring` dependency to `0.3`.
- Audio files now use `Content-Disposition: inline` instead of `attachment`. [#2645]
@@ -22,46 +27,46 @@
[#2021]: https://github.com/actix/actix-web/pull/2021
[#2645]: https://github.com/actix/actix-web/pull/2645
-
## 0.6.0 - 2022-02-25
+
- No significant changes since `0.6.0-beta.16`.
-
## 0.6.0-beta.16 - 2022-01-31
+
- No significant changes since `0.6.0-beta.15`.
-
## 0.6.0-beta.15 - 2022-01-21
+
- No significant changes since `0.6.0-beta.14`.
-
## 0.6.0-beta.14 - 2022-01-14
+
- The `prefer_utf8` option introduced in `0.4.0` is now true by default. [#2583]
[#2583]: https://github.com/actix/actix-web/pull/2583
-
## 0.6.0-beta.13 - 2022-01-04
+
- The `Files` service now rejects requests with URL paths that include `%2F` (decoded: `/`). [#2398]
- The `Files` service now correctly decodes `%25` in the URL path to `%` for the file path. [#2398]
- Minimum supported Rust version (MSRV) is now 1.54.
[#2398]: https://github.com/actix/actix-web/pull/2398
-
## 0.6.0-beta.12 - 2021-12-29
+
- No significant changes since `0.6.0-beta.11`.
-
## 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
+
- No significant changes since `0.6.0-beta.9`.
-
## 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 `NamedFile::open_async`. [#2408]
- Fix 304 Not Modified responses to omit the Content-Length header, as per the spec. [#2453]
@@ -72,24 +77,24 @@
[#2408]: https://github.com/actix/actix-web/pull/2408
[#2453]: https://github.com/actix/actix-web/pull/2453
-
## 0.6.0-beta.8 - 2021-10-20
+
- Minimum supported Rust version (MSRV) is now 1.52.
-
## 0.6.0-beta.7 - 2021-09-09
+
- Minimum supported Rust version (MSRV) is now 1.51.
-
## 0.6.0-beta.6 - 2021-06-26
+
- 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]
[#2274]: https://github.com/actix/actix-web/pull/2274
[#2228]: https://github.com/actix/actix-web/pull/2228
-
## 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]
- 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]
@@ -100,58 +105,58 @@
[#2225]: https://github.com/actix/actix-web/pull/2225
[#2257]: https://github.com/actix/actix-web/pull/2257
-
## 0.6.0-beta.4 - 2021-04-02
+
- Add support for `.guard` in `Files` to selectively filter `Files` services. [#2046]
[#2046]: https://github.com/actix/actix-web/pull/2046
-
## 0.6.0-beta.3 - 2021-03-09
+
- No notable changes.
-
## 0.6.0-beta.2 - 2021-02-10
+
- Fix If-Modified-Since and If-Unmodified-Since to not compare using sub-second timestamps. [#1887]
- Replace `v_htmlescape` with `askama_escape`. [#1953]
[#1887]: https://github.com/actix/actix-web/pull/1887
[#1953]: https://github.com/actix/actix-web/pull/1953
-
## 0.6.0-beta.1 - 2021-01-07
+
- `HttpRange::parse` now has its own error type.
- Update `bytes` to `1.0`. [#1813]
[#1813]: https://github.com/actix/actix-web/pull/1813
-
## 0.5.0 - 2020-12-26
+
- Optionally support hidden files/directories. [#1811]
[#1811]: https://github.com/actix/actix-web/pull/1811
-
## 0.4.1 - 2020-11-24
+
- Clarify order of parameters in `Files::new` and improve docs.
-
## 0.4.0 - 2020-10-06
+
- Add `Files::prefer_utf8` option that adds UTF-8 charset on certain response types. [#1714]
[#1714]: https://github.com/actix/actix-web/pull/1714
-
## 0.3.0 - 2020-09-11
+
- No significant changes from 0.3.0-beta.1.
-
## 0.3.0-beta.1 - 2020-07-15
+
- Update `v_htmlescape` to 0.10
- Update `actix-web` and `actix-http` dependencies to beta.1
-
## 0.3.0-alpha.1 - 2020-05-23
+
- Update `actix-web` and `actix-http` dependencies to alpha
- Fix some typos in the docs
- Bump minimum supported Rust version to 1.40
@@ -159,73 +164,73 @@
[#1384]: https://github.com/actix/actix-web/pull/1384
-
## 0.2.1 - 2019-12-22
+
- Use the same format for file URLs regardless of platforms
-
## 0.2.0 - 2019-12-20
+
- Fix BodyEncoding trait import #1220
-
## 0.2.0-alpha.1 - 2019-12-07
+
- Migrate to `std::future`
-
## 0.1.7 - 2019-11-06
-- Add an additional `filename*` param in the `Content-Disposition` header of
- `actix_files::NamedFile` to be more compatible. (#1151)
+
+- Add an additional `filename*` param in the `Content-Disposition` header of `actix_files::NamedFile` to be more compatible. (#1151)
## 0.1.6 - 2019-10-14
+
- Add option to redirect to a slash-ended path `Files` #1132
-
## 0.1.5 - 2019-10-08
+
- Bump up `mime_guess` crate version to 2.0.1
- Bump up `percent-encoding` crate version to 2.1
- Allow user defined request guards for `Files` #1113
-
## 0.1.4 - 2019-07-20
+
- Allow to disable `Content-Disposition` header #686
-
## 0.1.3 - 2019-06-28
+
- Do not set `Content-Length` header, let actix-http set it #930
-
## 0.1.2 - 2019-06-13
+
- Content-Length is 0 for NamedFile HEAD request #914
- Fix ring dependency from actix-web default features for #741
-
## 0.1.1 - 2019-06-01
+
- Static files are incorrectly served as both chunked and with length #812
-
## 0.1.0 - 2019-05-25
+
- NamedFile last-modified check always fails due to nano-seconds in file modified date #820
-
## 0.1.0-beta.4 - 2019-05-12
+
- Update actix-web to beta.4
-
## 0.1.0-beta.1 - 2019-04-20
+
- Update actix-web to beta.1
-
## 0.1.0-alpha.6 - 2019-04-14
+
- Update actix-web to alpha6
-
## 0.1.0-alpha.4 - 2019-04-08
+
- Update actix-web to alpha4
-
## 0.1.0-alpha.2 - 2019-04-02
+
- Add default handler support
-
## 0.1.0-alpha.1 - 2019-03-28
+
- Initial impl
diff --git a/actix-files/Cargo.toml b/actix-files/Cargo.toml
index 01dc2928a..4c29c95b2 100644
--- a/actix-files/Cargo.toml
+++ b/actix-files/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "actix-files"
-version = "0.6.2"
+version = "0.6.3"
authors = [
"Nikolay Kim ",
"Rob Ede ",
@@ -40,8 +40,8 @@ v_htmlescape= "0.15"
# experimental-io-uring
[target.'cfg(target_os = "linux")'.dependencies]
-tokio-uring = { version = "0.3", optional = true, features = ["bytes"] }
-actix-server = { version = "2.1", optional = true } # ensure matching tokio-uring versions
+tokio-uring = { version = "0.4", optional = true, features = ["bytes"] }
+actix-server = { version = "2.2", optional = true } # ensure matching tokio-uring versions
[dev-dependencies]
actix-rt = "2.7"
diff --git a/actix-files/README.md b/actix-files/README.md
index a5078c8d5..8869ca436 100644
--- a/actix-files/README.md
+++ b/actix-files/README.md
@@ -3,11 +3,11 @@
> Static file serving for Actix Web
[](https://crates.io/crates/actix-files)
-[](https://docs.rs/actix-files/0.6.2)
+[](https://docs.rs/actix-files/0.6.3)


-[](https://deps.rs/crate/actix-files/0.6.2)
+[](https://deps.rs/crate/actix-files/0.6.3)
[](https://crates.io/crates/actix-files)
[](https://discord.gg/NWpN5mmg3x)
@@ -15,4 +15,4 @@
- [API Documentation](https://docs.rs/actix-files)
- [Example Project](https://github.com/actix/examples/tree/master/basics/static-files)
-- Minimum Supported Rust Version (MSRV): 1.54
+- Minimum Supported Rust Version (MSRV): 1.59
diff --git a/actix-files/src/files.rs b/actix-files/src/files.rs
index a30ce6fd3..be2a450d2 100644
--- a/actix-files/src/files.rs
+++ b/actix-files/src/files.rs
@@ -142,7 +142,7 @@ impl Files {
self
}
- /// Set custom directory renderer
+ /// Set custom directory renderer.
pub fn files_listing_renderer(mut self, f: F) -> Self
where
for<'r, 's> F:
@@ -152,7 +152,7 @@ impl Files {
self
}
- /// Specifies mime override callback
+ /// Specifies MIME override callback.
pub fn mime_override(mut self, f: F) -> Self
where
F: Fn(&mime::Name<'_>) -> DispositionType + 'static,
@@ -390,3 +390,42 @@ impl ServiceFactory for Files {
}
}
}
+
+#[cfg(test)]
+mod tests {
+ use actix_web::{
+ http::StatusCode,
+ test::{self, TestRequest},
+ App, HttpResponse,
+ };
+
+ use super::*;
+
+ #[actix_web::test]
+ async fn custom_files_listing_renderer() {
+ let srv = test::init_service(
+ App::new().service(
+ Files::new("/", "./tests")
+ .show_files_listing()
+ .files_listing_renderer(|dir, req| {
+ Ok(ServiceResponse::new(
+ req.clone(),
+ HttpResponse::Ok().body(dir.path.to_str().unwrap().to_owned()),
+ ))
+ }),
+ ),
+ )
+ .await;
+
+ let req = TestRequest::with_uri("/").to_request();
+ let res = test::call_service(&srv, req).await;
+
+ assert_eq!(res.status(), StatusCode::OK);
+ let body = test::read_body(res).await;
+ assert!(
+ body.ends_with(b"actix-files/tests/"),
+ "body {:?} does not end with `actix-files/tests/`",
+ body
+ );
+ }
+}
diff --git a/actix-files/src/path_buf.rs b/actix-files/src/path_buf.rs
index 9ee1338c6..650f55247 100644
--- a/actix-files/src/path_buf.rs
+++ b/actix-files/src/path_buf.rs
@@ -30,7 +30,7 @@ impl PathBufWrap {
let mut segment_count = path.matches('/').count() + 1;
// we can decode the whole path here (instead of per-segment decoding)
- // because we will reject `%2F` in paths using `segement_count`.
+ // because we will reject `%2F` in paths using `segment_count`.
let path = percent_encoding::percent_decode_str(path)
.decode_utf8()
.map_err(|_| UriSegmentError::NotValidUtf8)?;
diff --git a/actix-http-test/CHANGES.md b/actix-http-test/CHANGES.md
index 028fe3ddc..e56883c19 100644
--- a/actix-http-test/CHANGES.md
+++ b/actix-http-test/CHANGES.md
@@ -1,10 +1,13 @@
# Changes
## Unreleased - 2022-xx-xx
+
+## 3.1.0 - 2023-01-21
+
- Minimum supported Rust version (MSRV) is now 1.59.
-
## 3.0.0 - 2022-07-24
+
- `TestServer::stop` is now async and will wait for the server and system to shutdown. [#2442]
- Added `TestServer::client_headers` method. [#2097]
- Update `actix-server` dependency to `2`.
@@ -16,71 +19,71 @@
[#2097]: https://github.com/actix/actix-web/pull/2097
[#1813]: https://github.com/actix/actix-web/pull/1813
-
3.0.0 Pre-Releases
## 3.0.0-beta.13 - 2022-02-16
+
- No significant changes since `3.0.0-beta.12`.
-
## 3.0.0-beta.12 - 2022-01-31
+
- No significant changes since `3.0.0-beta.11`.
-
## 3.0.0-beta.11 - 2022-01-04
+
- Minimum supported Rust version (MSRV) is now 1.54.
-
## 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
+
- No significant changes since `3.0.0-beta.8`.
-
## 3.0.0-beta.8 - 2021-11-30
+
- Update `actix-tls` to `3.0.0-rc.1`. [#2474]
[#2474]: https://github.com/actix/actix-web/pull/2474
-
## 3.0.0-beta.7 - 2021-11-22
+
- Fix compatibility with experimental `io-uring` feature of `actix-rt`. [#2408]
[#2408]: https://github.com/actix/actix-web/pull/2408
-
## 3.0.0-beta.6 - 2021-11-15
+
- `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]
- Minimum supported Rust version (MSRV) is now 1.52.
[#2442]: https://github.com/actix/actix-web/pull/2442
-
## 3.0.0-beta.5 - 2021-09-09
+
- Minimum supported Rust version (MSRV) is now 1.51.
-
## 3.0.0-beta.4 - 2021-04-02
+
- Added `TestServer::client_headers` method. [#2097]
[#2097]: https://github.com/actix/actix-web/pull/2097
-
## 3.0.0-beta.3 - 2021-03-09
-- No notable changes.
+- No notable changes.
## 3.0.0-beta.2 - 2021-02-10
+
- No notable changes.
-
## 3.0.0-beta.1 - 2021-01-07
+
- Update `bytes` to `1.0`. [#1813]
[#1813]: https://github.com/actix/actix-web/pull/1813
@@ -88,6 +91,7 @@
## 2.1.0 - 2020-11-25
+
- Add ability to set address for `TestServer`. [#1645]
- Upgrade `base64` to `0.13`.
- Upgrade `serde_urlencoded` to `0.7`. [#1773]
@@ -95,12 +99,12 @@
[#1773]: https://github.com/actix/actix-web/pull/1773
[#1645]: https://github.com/actix/actix-web/pull/1645
-
## 2.0.0 - 2020-09-11
+
- Update actix-codec and actix-utils dependencies.
-
## 2.0.0-alpha.1 - 2020-05-23
+
- Update the `time` dependency to 0.2.7
- Update `actix-connect` dependency to 2.0.0-alpha.2
- Make `test_server` `async` fn.
@@ -110,55 +114,56 @@
- Update `env_logger` dependency to 0.7
## 1.0.0 - 2019-12-13
+
- Replaced `TestServer::start()` with `test_server()`
-
## 1.0.0-alpha.3 - 2019-12-07
+
- Migrate to `std::future`
-
## 0.2.5 - 2019-09-17
+
- Update serde_urlencoded to "0.6.1"
- Increase TestServerRuntime timeouts from 500ms to 3000ms
- Do not override current `System`
-
## 0.2.4 - 2019-07-18
+
- Update actix-server to 0.6
-
## 0.2.3 - 2019-07-16
+
- Add `delete`, `options`, `patch` methods to `TestServerRunner`
-
## 0.2.2 - 2019-06-16
+
- Add .put() and .sput() methods
-
## 0.2.1 - 2019-06-05
+
- Add license files
-
## 0.2.0 - 2019-05-12
+
- Update awc and actix-http deps
-
## 0.1.1 - 2019-04-24
+
- Always make new connection for http client
-
## 0.1.0 - 2019-04-16
+
- No changes
-
## 0.1.0-alpha.3 - 2019-04-02
+
- Request functions accept path #743
-
## 0.1.0-alpha.2 - 2019-03-29
+
- Added TestServerRuntime::load_body() method
- Update actix-http and awc libraries
-
## 0.1.0-alpha.1 - 2019-03-28
+
- Initial impl
diff --git a/actix-http-test/Cargo.toml b/actix-http-test/Cargo.toml
index 86338fb06..cd5e87162 100644
--- a/actix-http-test/Cargo.toml
+++ b/actix-http-test/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "actix-http-test"
-version = "3.0.0"
+version = "3.1.0"
authors = ["Nikolay Kim "]
description = "Various helpers for Actix applications to use during testing"
keywords = ["http", "web", "framework", "async", "futures"]
@@ -37,7 +37,6 @@ actix-rt = "2.2"
actix-server = "2"
awc = { version = "3", default-features = false }
-base64 = "0.13"
bytes = "1"
futures-core = { version = "0.3.17", default-features = false }
http = "0.2.5"
@@ -48,7 +47,7 @@ serde_json = "1.0"
slab = "0.4"
serde_urlencoded = "0.7"
tls-openssl = { version = "0.10.9", package = "openssl", optional = true }
-tokio = { version = "1.8.4", features = ["sync"] }
+tokio = { version = "1.24.2", features = ["sync"] }
[dev-dependencies]
actix-web = { version = "4", default-features = false, features = ["cookies"] }
diff --git a/actix-http-test/README.md b/actix-http-test/README.md
index 25e7c684e..94f0e88a5 100644
--- a/actix-http-test/README.md
+++ b/actix-http-test/README.md
@@ -3,15 +3,15 @@
> Various helpers for Actix applications to use during testing.
[](https://crates.io/crates/actix-http-test)
-[](https://docs.rs/actix-http-test/3.0.0)
+[](https://docs.rs/actix-http-test/3.1.0)


-[](https://deps.rs/crate/actix-http-test/3.0.0)
+[](https://deps.rs/crate/actix-http-test/3.1.0)
[](https://crates.io/crates/actix-http-test)
[](https://discord.gg/NWpN5mmg3x)
## Documentation & Resources
- [API Documentation](https://docs.rs/actix-http-test)
-- Minimum Supported Rust Version (MSRV): 1.54
+- Minimum Supported Rust Version (MSRV): 1.59
diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md
index 7feec2a1a..033cd994c 100644
--- a/actix-http/CHANGES.md
+++ b/actix-http/CHANGES.md
@@ -1,13 +1,19 @@
# Changes
## Unreleased - 2022-xx-xx
+
+## 3.3.0 - 2023-01-21
+
### Added
+
- Implement `MessageBody` for `Cow<'static, str>` and `Cow<'static, [u8]>`. [#2959]
- Implement `MessageBody` for `&mut B` where `B: MessageBody + Unpin`. [#2868]
- Implement `MessageBody` for `Pin` where `B::Target: MessageBody`. [#2868]
- Automatic h2c detection via new service finalizer `HttpService::tcp_auto_h2c()`. [#2957]
-- `HeaderMap::retain()` [#2955].
-- Header name constants in `header` module. [#2956]
+- `HeaderMap::retain()`. [#2955]
+- Header name constants in `header` module. [#2956] [#2968]
+ - `CACHE_STATUS`
+ - `CDN_CACHE_CONTROL`
- `CROSS_ORIGIN_EMBEDDER_POLICY`
- `CROSS_ORIGIN_OPENER_POLICY`
- `PERMISSIONS_POLICY`
@@ -15,84 +21,103 @@
- `X_FORWARDED_HOST`
- `X_FORWARDED_PROTO`
+### Fixed
+
+- Fix non-empty body of HTTP/2 HEAD responses. [#2920]
+
### Performance
+
- Improve overall performance of operations on `Extensions`. [#2890]
[#2959]: https://github.com/actix/actix-web/pull/2959
[#2868]: https://github.com/actix/actix-web/pull/2868
[#2890]: https://github.com/actix/actix-web/pull/2890
+[#2920]: https://github.com/actix/actix-web/pull/2920
[#2957]: https://github.com/actix/actix-web/pull/2957
[#2955]: https://github.com/actix/actix-web/pull/2955
[#2956]: https://github.com/actix/actix-web/pull/2956
-
+[#2968]: https://github.com/actix/actix-web/pull/2968
## 3.2.2 - 2022-09-11
+
### Changed
+
- Minimum supported Rust version (MSRV) is now 1.59 due to transitive `time` dependency.
### Fixed
+
- Avoid possibility of dispatcher getting stuck while back-pressuring I/O. [#2369]
[#2369]: https://github.com/actix/actix-web/pull/2369
-
## 3.2.1 - 2022-07-02
+
### Fixed
+
- Fix parsing ambiguity in Transfer-Encoding and Content-Length headers for HTTP/1.0 requests. [#2794]
[#2794]: https://github.com/actix/actix-web/pull/2794
-
## 3.2.0 - 2022-06-30
+
### Changed
+
- Minimum supported Rust version (MSRV) is now 1.57 due to transitive `time` dependency.
### Fixed
+
- Websocket parser no longer throws endless overflow errors after receiving an oversized frame. [#2790]
- Retain previously set Vary headers when using compression encoder. [#2798]
[#2790]: https://github.com/actix/actix-web/pull/2790
[#2798]: https://github.com/actix/actix-web/pull/2798
-
## 3.1.0 - 2022-06-11
+
### Changed
+
- Minimum supported Rust version (MSRV) is now 1.56 due to transitive `hashbrown` dependency.
### Fixed
+
- Revert broken fix in [#2624] that caused erroneous 500 error responses. Temporarily re-introduces [#2357] bug. [#2779]
[#2624]: https://github.com/actix/actix-web/pull/2624
[#2357]: https://github.com/actix/actix-web/issues/2357
[#2779]: https://github.com/actix/actix-web/pull/2779
-
## 3.0.4 - 2022-03-09
+
### Fixed
+
- Document on docs.rs with `ws` feature enabled.
-
## 3.0.3 - 2022-03-08
+
### Fixed
+
- Allow spaces between header name and colon when parsing responses. [#2684]
[#2684]: https://github.com/actix/actix-web/pull/2684
-
## 3.0.2 - 2022-03-05
+
### Fixed
+
- Fix encoding camel-case header names with more than one hyphen. [#2683]
[#2683]: https://github.com/actix/actix-web/pull/2683
-
## 3.0.1 - 2022-03-04
+
- Fix panic in H1 dispatcher when pipelining is used with keep-alive. [#2678]
[#2678]: https://github.com/actix/actix-web/issues/2678
## 3.0.0 - 2022-02-25
+
### Dependencies
+
- Updated `actix-*` to Tokio v1-based versions. [#1813]
- Updated `bytes` to `1.0`. [#1813]
- Updated `h2` to `0.3`. [#1813]
@@ -101,6 +126,7 @@
- Updated `tokio` to `1`.
### Added
+
- Crate Features:
- `ws`; disabled by default. [#2618]
- `http2`; disabled by default. [#2618]
@@ -169,6 +195,7 @@
- `#[must_use]` for `ws::Codec` to prevent subtle bugs. [#1920]
### Changed
+
- Traits:
- Rename `IntoHeaderValue => TryIntoHeaderValue`. [#2510]
- `MessageBody` now has an associated `Error` type. [#2183]
@@ -224,6 +251,7 @@
- Minimum supported Rust version (MSRV) is now 1.54.
### Fixed
+
- A `Vary` header is now correctly sent along with compressed content. [#2501]
- HTTP/1.1 dispatcher correctly uses client request timeout. [#2611]
- Fixed issue where handlers that took payload but then dropped without reading it to EOF it would cause keep-alive connections to become stuck. [#2624]
@@ -236,6 +264,7 @@
- Fixed quality parse error in Accept-Encoding header. [#2344]
### Removed
+
- Crate Features:
- `compress` feature. [#2065]
- `cookies` feature. [#2065]
@@ -298,7 +327,6 @@
- `downcast` and `downcast_get_type_id` macros. [#2291]
- Down-casting for `MessageBody` types; use standard `Any` trait. [#2183]
-
[#1813]: https://github.com/actix/actix-web/pull/1813
[#1845]: https://github.com/actix/actix-web/pull/1845
[#1857]: https://github.com/actix/actix-web/pull/1857
@@ -371,37 +399,42 @@
[#2660]: https://github.com/actix/actix-web/pull/2660
[00ba8d55]: https://github.com/actix/actix-web/commit/00ba8d55492284581695d824648590715a8bd386
-
3.0.0 Pre-Releases
## 3.0.0-rc.4 - 2022-02-22
+
### Fixed
+
- Fix h1 dispatcher panic. [1ce58ecb]
[1ce58ecb]: https://github.com/actix/actix-web/commit/1ce58ecb305c60e51db06e6c913b7a1344e229ca
-
## 3.0.0-rc.3 - 2022-02-16
+
- No significant changes since `3.0.0-rc.2`.
-
## 3.0.0-rc.2 - 2022-02-08
+
### Added
+
- Implement `From>` for `Response>`. [#2625]
### Changed
+
- `error::DispatcherError` enum is now marked `#[non_exhaustive]`. [#2624]
### Fixed
+
- Issue where handlers that took payload but then dropped without reading it to EOF it would cause keep-alive connections to become stuck. [#2624]
[#2624]: https://github.com/actix/actix-web/pull/2624
[#2625]: https://github.com/actix/actix-web/pull/2625
-
## 3.0.0-rc.1 - 2022-01-31
+
### Added
+
- Implement `Default` for `KeepAlive`. [#2611]
- Implement `From` for `KeepAlive`. [#2611]
- Implement `From
-
## 2.2.2 - 2022-01-21
+
### Changed
+
- Migrate to `brotli` crate. [ad7e3c06]
[ad7e3c06]: https://github.com/actix/actix-web/commit/ad7e3c06
-
## 2.2.1 - 2021-08-09
+
### Fixed
+
- Potential HTTP request smuggling vulnerabilities. [RUSTSEC-2021-0081](https://github.com/rustsec/advisory-db/pull/977)
-
## 2.2.0 - 2020-11-25
+
### Added
+
- HttpResponse builders for 1xx status codes. [#1768]
- `Accept::mime_precedence` and `Accept::mime_preference`. [#1793]
- `TryFrom` and `TryFrom` for `http::header::Quality`. [#1797]
### Fixed
+
- Started dropping `transfer-encoding: chunked` and `Content-Length` for 1XX and 204 responses. [#1767]
### Changed
+
- Upgrade `serde_urlencoded` to `0.7`. [#1773]
[#1773]: https://github.com/actix/actix-web/pull/1773
@@ -877,12 +953,14 @@
[#1793]: https://github.com/actix/actix-web/pull/1793
[#1797]: https://github.com/actix/actix-web/pull/1797
-
## 2.1.0 - 2020-10-30
+
### Added
+
- Added more flexible `on_connect_ext` methods for on-connect handling. [#1754]
### Changed
+
- Upgrade `base64` to `0.13`. [#1744]
- Upgrade `pin-project` to `1.0`. [#1733]
- Deprecate `ResponseBuilder::{if_some, if_true}`. [#1760]
@@ -892,37 +970,42 @@
[#1733]: https://github.com/actix/actix-web/pull/1733
[#1744]: https://github.com/actix/actix-web/pull/1744
-
## 2.0.0 - 2020-09-11
+
- No significant changes from `2.0.0-beta.4`.
-
## 2.0.0-beta.4 - 2020-09-09
+
### Changed
+
- Update actix-codec and actix-utils dependencies.
- Update actix-connect and actix-tls dependencies.
-
## 2.0.0-beta.3 - 2020-08-14
+
### Fixed
+
- Memory leak of `client::pool::ConnectorPoolSupport`. [#1626]
[#1626]: https://github.com/actix/actix-web/pull/1626
-
## 2.0.0-beta.2 - 2020-07-21
+
### Fixed
+
- Potential UB in h1 decoder using uninitialized memory. [#1614]
### Changed
+
- Fix illegal chunked encoding. [#1615]
[#1614]: https://github.com/actix/actix-web/pull/1614
[#1615]: https://github.com/actix/actix-web/pull/1615
-
## 2.0.0-beta.1 - 2020-07-11
+
### Changed
+
- Migrate cookie handling to `cookie` crate. [#1558]
- Update `sha-1` to 0.9. [#1586]
- Fix leak in client pool. [#1580]
@@ -932,278 +1015,319 @@
[#1586]: https://github.com/actix/actix-web/pull/1586
[#1580]: https://github.com/actix/actix-web/pull/1580
-
## 2.0.0-alpha.4 - 2020-05-21
+
### Changed
+
- Bump minimum supported Rust version to 1.40
-- content_length function is removed, and you can set Content-Length by calling
- no_chunking function [#1439]
-- `BodySize::Sized64` variant has been removed. `BodySize::Sized` now receives a
- `u64` instead of a `usize`.
+- content_length function is removed, and you can set Content-Length by calling no_chunking function [#1439]
+- `BodySize::Sized64` variant has been removed. `BodySize::Sized` now receives a `u64` instead of a `usize`.
- Update `base64` dependency to 0.12
### Fixed
+
- Support parsing of `SameSite=None` [#1503]
[#1439]: https://github.com/actix/actix-web/pull/1439
[#1503]: https://github.com/actix/actix-web/pull/1503
-
## 2.0.0-alpha.3 - 2020-05-08
+
### Fixed
+
- Correct spelling of ConnectError::Unresolved [#1487]
-- Fix a mistake in the encoding of websocket continuation messages wherein
- Item::FirstText and Item::FirstBinary are each encoded as the other.
+- Fix a mistake in the encoding of websocket continuation messages wherein Item::FirstText and Item::FirstBinary are each encoded as the other.
### Changed
+
- Implement `std::error::Error` for our custom errors [#1422]
-- Remove `failure` support for `ResponseError` since that crate
- will be deprecated in the near future.
+- Remove `failure` support for `ResponseError` since that crate will be deprecated in the near future.
[#1422]: https://github.com/actix/actix-web/pull/1422
[#1487]: https://github.com/actix/actix-web/pull/1487
-
## 2.0.0-alpha.2 - 2020-03-07
+
### Changed
+
- 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
- respectively to improve download speed for awc when downloading large objects. [#1394]
-- client::Connector accepts initial_window_size and initial_connection_window_size
- HTTP2 configuration. [#1394]
+- 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]
+- client::Connector accepts initial_window_size and initial_connection_window_size HTTP2 configuration. [#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
[#1395]: https://github.com/actix/actix-web/pull/1395
-
## 2.0.0-alpha.1 - 2020-02-27
+
### Changed
+
- Update the `time` dependency to 0.2.7.
- Moved actors messages support from actix crate, enabled with feature `actors`.
-- Breaking change: trait MessageBody requires Unpin and accepting `Pin<&mut Self>` instead of
- `&mut self` in the poll_next().
+- Breaking change: trait MessageBody requires Unpin and accepting `Pin<&mut Self>` instead of `&mut self` in the poll_next().
- MessageBody is not implemented for &'static [u8] anymore.
### Fixed
+
- Allow `SameSite=None` cookies to be sent in a response.
-
## 1.0.1 - 2019-12-20
+
### Fixed
+
- Poll upgrade service's readiness from HTTP service handlers
- Replace brotli with brotli2 #1224
-
## 1.0.0 - 2019-12-13
+
### Added
+
- Add websockets continuation frame support
### Changed
+
- Replace `flate2-xxx` features with `compress`
-
## 1.0.0-alpha.5 - 2019-12-09
+
### Fixed
+
- Check `Upgrade` service readiness before calling it
- Fix buffer remaining capacity calculation
### Changed
+
- Websockets: Ping and Pong should have binary data #1049
-
## 1.0.0-alpha.4 - 2019-12-08
+
### Added
+
- Add impl ResponseBuilder for Error
### Changed
+
- Use rust based brotli compression library
## 1.0.0-alpha.3 - 2019-12-07
+
### Changed
+
- Migrate to tokio 0.2
- Migrate to `std::future`
-
## 0.2.11 - 2019-11-06
+
### Added
+
- 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
- `actix_files::NamedFile` to be more compatible. (#1151)
+- Add an additional `filename*` param in the `Content-Disposition` header of `actix_files::NamedFile` to be more compatible. (#1151)
- Allow to use `std::convert::Infallible` as `actix_http::error::Error`
### Fixed
-- To be compatible with non-English error responses, `ResponseError` rendered with `text/plain;
- charset=utf-8` header [#1118]
+
+- To be compatible with non-English error responses, `ResponseError` rendered with `text/plain; charset=utf-8` header [#1118]
[#1878]: https://github.com/actix/actix-web/pull/1878
-
## 0.2.10 - 2019-09-11
+
### Added
-- Add support for sending HTTP requests with `Rc` in addition to sending HTTP requests
- with `RequestHead`
+
+- Add support for sending HTTP requests with `Rc` in addition to sending HTTP requests with `RequestHead`
### Fixed
+
- h2 will use error response #1080
- on_connect result isn't added to request extensions for http2 requests #1009
-
## 0.2.9 - 2019-08-13
+
### Changed
+
- Dropped the `byteorder`-dependency in favor of `stdlib`-implementation
- Update percent-encoding to 2.1
- Update serde_urlencoded to 0.6.1
### Fixed
+
- Fixed a panic in the HTTP2 handshake in client HTTP requests (#1031)
-
## 0.2.8 - 2019-08-01
+
### Added
+
- Add `rustls` support
- Add `Clone` impl for `HeaderMap`
### Fixed
-- awc client panic #1016
-- Invalid response with compression middleware enabled, but compression-related features
- disabled #997
+- awc client panic #1016
+- Invalid response with compression middleware enabled, but compression-related features disabled #997
## 0.2.7 - 2019-07-18
+
### Added
+
- Add support for downcasting response errors #986
-
## 0.2.6 - 2019-07-17
+
### Changed
+
- Replace `ClonableService` with local copy
- Upgrade `rand` dependency version to 0.7
-
## 0.2.5 - 2019-06-28
+
### Added
+
- Add `on-connect` callback, `HttpServiceBuilder::on_connect()` #946
### Changed
+
- Use `encoding_rs` crate instead of unmaintained `encoding` crate
- Add `Copy` and `Clone` impls for `ws::Codec`
-
## 0.2.4 - 2019-06-16
+
### Fixed
+
- Do not compress NoContent (204) responses #918
-
## 0.2.3 - 2019-06-02
+
### Added
+
- Debug impl for ResponseBuilder
- From SizedStream and BodyStream for Body
### Changed
+
- SizedStream uses u64
-
## 0.2.2 - 2019-05-29
+
### Fixed
+
- Parse incoming stream before closing stream on disconnect #868
-
## 0.2.1 - 2019-05-25
+
### Fixed
+
- Handle socket read disconnect
-
## 0.2.0 - 2019-05-12
+
### Changed
+
- Update actix-service to 0.4
- Expect and upgrade services accept `ServerConfig` config.
### Deleted
+
- `OneRequest` service
-
## 0.1.5 - 2019-05-04
+
### Fixed
+
- Clean up response extensions in response pool #817
-
## 0.1.4 - 2019-04-24
+
### Added
+
- Allow to render h1 request headers in `Camel-Case`
### Fixed
+
- Read until eof for http/1.0 responses #771
-
## 0.1.3 - 2019-04-23
+
### Fixed
+
- Fix http client pool management
- Fix http client wait queue management #794
-
## 0.1.2 - 2019-04-23
+
### Fixed
+
- Fix BorrowMutError panic in client connector #793
-
## 0.1.1 - 2019-04-19
+
### Changed
+
- Cookie::max_age() accepts value in seconds
- Cookie::max_age_time() accepts value in time::Duration
- Allow to specify server address for client connector
-
## 0.1.0 - 2019-04-16
+
### Added
+
- Expose peer addr via `Request::peer_addr()` and `RequestHead::peer_addr`
### Changed
+
- `actix_http::encoding` always available
- use trust-dns-resolver 0.11.0
-
## 0.1.0-alpha.5 - 2019-04-12
+
### Added
+
- Allow to use custom service for upgrade requests
- Added `h1::SendResponse` future.
### Changed
+
- MessageBody::length() renamed to MessageBody::size() for consistency
- ws handshake verification functions take RequestHead instead of Request
-
## 0.1.0-alpha.4 - 2019-04-08
+
### Added
+
- Allow to use custom `Expect` handler
- Add minimal `std::error::Error` impl for `Error`
### Changed
+
- Export IntoHeaderValue
- Render error and return as response body
- Use thread pool for response body compression
### Deleted
+
- Removed PayloadBuffer
-
## 0.1.0-alpha.3 - 2019-04-02
+
### Added
+
- Warn when an unsealed private cookie isn't valid UTF-8
### Fixed
+
- Rust 1.31.0 compatibility
- Preallocate read buffer for h1 codec
- Detect socket disconnection during protocol selection
-
## 0.1.0-alpha.2 - 2019-03-29
+
### Added
+
- Added ws::Message::Nop, no-op websockets message
### Changed
+
- Do not use thread pool for decompression if chunk size is smaller than 2048.
-
## 0.1.0-alpha.1 - 2019-03-28
+
- Initial impl
diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml
index 9f3977f0f..108b2314a 100644
--- a/actix-http/Cargo.toml
+++ b/actix-http/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "actix-http"
-version = "3.2.2"
+version = "3.3.0"
authors = [
"Nikolay Kim ",
"Rob Ede ",
@@ -61,7 +61,7 @@ actix-codec = "0.5"
actix-utils = "3"
actix-rt = { version = "2.2", default-features = false }
-ahash = "0.7"
+ahash = "0.8"
bitflags = "1.2"
bytes = "1"
bytestring = "1"
@@ -77,7 +77,7 @@ mime = "0.3"
percent-encoding = "2.1"
pin-project-lite = "0.2"
smallvec = "1.6.1"
-tokio = { version = "1.13.1", features = [] }
+tokio = { version = "1.24.2", features = [] }
tokio-util = { version = "0.7", features = ["io", "codec"] }
tracing = { version = "0.1.30", default-features = false, features = ["log"] }
@@ -86,7 +86,7 @@ h2 = { version = "0.3.9", optional = true }
# websockets
local-channel = { version = "0.1", optional = true }
-base64 = { version = "0.13", optional = true }
+base64 = { version = "0.21", optional = true }
rand = { version = "0.8", optional = true }
sha1 = { version = "0.10", optional = true }
@@ -119,7 +119,7 @@ serde_json = "1.0"
static_assertions = "1"
tls-openssl = { package = "openssl", version = "0.10.9" }
tls-rustls = { package = "rustls", version = "0.20.0" }
-tokio = { version = "1.8.4", features = ["net", "rt", "macros"] }
+tokio = { version = "1.24.2", features = ["net", "rt", "macros"] }
[[example]]
name = "ws"
diff --git a/actix-http/README.md b/actix-http/README.md
index 994cf97c6..aa98f953f 100644
--- a/actix-http/README.md
+++ b/actix-http/README.md
@@ -3,18 +3,18 @@
> HTTP primitives for the Actix ecosystem.
[](https://crates.io/crates/actix-http)
-[](https://docs.rs/actix-http/3.2.2)
+[](https://docs.rs/actix-http/3.3.0)


-[](https://deps.rs/crate/actix-http/3.2.2)
+[](https://deps.rs/crate/actix-http/3.3.0)
[](https://crates.io/crates/actix-http)
[](https://discord.gg/NWpN5mmg3x)
## Documentation & Resources
- [API Documentation](https://docs.rs/actix-http)
-- Minimum Supported Rust Version (MSRV): 1.54
+- Minimum Supported Rust Version (MSRV): 1.59
## Example
@@ -49,18 +49,3 @@ async fn main() -> io::Result<()> {
.await
}
```
-
-## License
-
-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))
-- MIT license ([LICENSE-MIT](LICENSE-MIT) or [http://opensource.org/licenses/MIT](http://opensource.org/licenses/MIT))
-
-at your option.
-
-## Code of Conduct
-
-Contribution to the actix-http crate is organized under the terms of the
-Contributor Covenant, the maintainer of actix-http, @fafhrd91, promises to
-intervene to uphold that code of conduct.
diff --git a/actix-http/src/h1/dispatcher_tests.rs b/actix-http/src/h1/dispatcher_tests.rs
index d39c5bd69..db46cdefc 100644
--- a/actix-http/src/h1/dispatcher_tests.rs
+++ b/actix-http/src/h1/dispatcher_tests.rs
@@ -932,7 +932,6 @@ fn http_msg(msg: impl AsRef) -> BytesMut {
.as_ref()
.trim()
.split('\n')
- .into_iter()
.map(|line| [line.trim_start(), "\r"].concat())
.collect::>()
.join("\n");
diff --git a/actix-http/src/h2/dispatcher.rs b/actix-http/src/h2/dispatcher.rs
index 680936f0f..3e618820e 100644
--- a/actix-http/src/h2/dispatcher.rs
+++ b/actix-http/src/h2/dispatcher.rs
@@ -29,7 +29,7 @@ use crate::{
HeaderName, HeaderValue, CONNECTION, CONTENT_LENGTH, DATE, TRANSFER_ENCODING, UPGRADE,
},
service::HttpFlow,
- Extensions, OnConnectData, Payload, Request, Response, ResponseHead,
+ Extensions, Method, OnConnectData, Payload, Request, Response, ResponseHead,
};
const CHUNK_SIZE: usize = 16_384;
@@ -118,6 +118,7 @@ where
let payload = crate::h2::Payload::new(body);
let pl = Payload::H2 { payload };
let mut req = Request::with_payload(pl);
+ let head_req = parts.method == Method::HEAD;
let head = req.head_mut();
head.uri = parts.uri;
@@ -135,10 +136,10 @@ where
actix_rt::spawn(async move {
// resolve service call and send response.
let res = match fut.await {
- Ok(res) => handle_response(res.into(), tx, config).await,
+ Ok(res) => handle_response(res.into(), tx, config, head_req).await,
Err(err) => {
let res: Response = err.into();
- handle_response(res, tx, config).await
+ handle_response(res, tx, config, head_req).await
}
};
@@ -206,6 +207,7 @@ async fn handle_response(
res: Response,
mut tx: SendResponse,
config: ServiceConfig,
+ head_req: bool,
) -> Result<(), DispatchError>
where
B: MessageBody,
@@ -215,14 +217,14 @@ where
// prepare response.
let mut size = body.size();
let res = prepare_response(config, res.head(), &mut size);
- let eof = size.is_eof();
+ let eof_or_head = size.is_eof() || head_req;
// send response head and return on eof.
let mut stream = tx
- .send_response(res, eof)
+ .send_response(res, eof_or_head)
.map_err(DispatchError::SendResponse)?;
- if eof {
+ if eof_or_head {
return Ok(());
}
diff --git a/actix-http/src/header/common.rs b/actix-http/src/header/common.rs
index 52909099a..67b0a9069 100644
--- a/actix-http/src/header/common.rs
+++ b/actix-http/src/header/common.rs
@@ -4,6 +4,18 @@
use http::header::HeaderName;
+/// Response header field that indicates how caches have handled that response and its corresponding
+/// request.
+///
+/// See [RFC 9211](https://www.rfc-editor.org/rfc/rfc9211) for full semantics.
+pub const CACHE_STATUS: HeaderName = HeaderName::from_static("cache-status");
+
+/// Response header field that allows origin servers to control the behavior of CDN caches
+/// interposed between them and clients separately from other caches that might handle the response.
+///
+/// See [RFC 9213](https://www.rfc-editor.org/rfc/rfc9213) for full semantics.
+pub const CDN_CACHE_CONTROL: HeaderName = HeaderName::from_static("cdn-cache-control");
+
/// Response header that prevents a document from loading any cross-origin resources that don't
/// explicitly grant the document permission (using [CORP] or [CORS]).
///
diff --git a/actix-http/src/header/mod.rs b/actix-http/src/header/mod.rs
index e2c2fe912..a63174a92 100644
--- a/actix-http/src/header/mod.rs
+++ b/actix-http/src/header/mod.rs
@@ -55,8 +55,9 @@ pub use self::{
// re-export list is explicit so that any updates to `http` do not conflict with this set
pub use self::common::{
- CROSS_ORIGIN_EMBEDDER_POLICY, CROSS_ORIGIN_OPENER_POLICY, CROSS_ORIGIN_RESOURCE_POLICY,
- PERMISSIONS_POLICY, X_FORWARDED_FOR, X_FORWARDED_HOST, X_FORWARDED_PROTO,
+ CACHE_STATUS, CDN_CACHE_CONTROL, CROSS_ORIGIN_EMBEDDER_POLICY, CROSS_ORIGIN_OPENER_POLICY,
+ CROSS_ORIGIN_RESOURCE_POLICY, PERMISSIONS_POLICY, X_FORWARDED_FOR, X_FORWARDED_HOST,
+ X_FORWARDED_PROTO,
};
/// An interface for types that already represent a valid header.
diff --git a/actix-http/src/ws/proto.rs b/actix-http/src/ws/proto.rs
index 7222168b7..0653c00b0 100644
--- a/actix-http/src/ws/proto.rs
+++ b/actix-http/src/ws/proto.rs
@@ -3,6 +3,7 @@ use std::{
fmt,
};
+use base64::prelude::*;
use tracing::error;
/// Operation codes defined in [RFC 6455 §11.8].
@@ -244,7 +245,7 @@ pub fn hash_key(key: &[u8]) -> [u8; 28] {
};
let mut hash_b64 = [0; 28];
- let n = base64::encode_config_slice(hash, base64::STANDARD, &mut hash_b64);
+ let n = BASE64_STANDARD.encode_slice(hash, &mut hash_b64).unwrap();
assert_eq!(n, 28);
hash_b64
diff --git a/actix-multipart-derive/Cargo.toml b/actix-multipart-derive/Cargo.toml
new file mode 100644
index 000000000..4a30898b4
--- /dev/null
+++ b/actix-multipart-derive/Cargo.toml
@@ -0,0 +1,26 @@
+[package]
+name = "actix-multipart-derive"
+version = "0.5.0"
+authors = ["Jacob Halsey "]
+description = "Multipart form derive macro for Actix Web"
+keywords = ["http", "web", "framework", "async", "futures"]
+homepage = "https://actix.rs"
+repository = "https://github.com/actix/actix-web.git"
+license = "MIT OR Apache-2.0"
+edition = "2018"
+
+[lib]
+proc-macro = true
+
+[dependencies]
+darling = "0.14"
+parse-size = "1"
+proc-macro2 = "1"
+quote = "1"
+syn = "1"
+
+[dev-dependencies]
+actix-multipart = "0.5"
+actix-web = "4"
+rustversion = "1"
+trybuild = "1"
diff --git a/actix-multipart-derive/LICENSE-APACHE b/actix-multipart-derive/LICENSE-APACHE
new file mode 120000
index 000000000..965b606f3
--- /dev/null
+++ b/actix-multipart-derive/LICENSE-APACHE
@@ -0,0 +1 @@
+../LICENSE-APACHE
\ No newline at end of file
diff --git a/actix-multipart-derive/LICENSE-MIT b/actix-multipart-derive/LICENSE-MIT
new file mode 120000
index 000000000..76219eb72
--- /dev/null
+++ b/actix-multipart-derive/LICENSE-MIT
@@ -0,0 +1 @@
+../LICENSE-MIT
\ No newline at end of file
diff --git a/actix-multipart-derive/README.md b/actix-multipart-derive/README.md
new file mode 100644
index 000000000..95f80bc79
--- /dev/null
+++ b/actix-multipart-derive/README.md
@@ -0,0 +1,3 @@
+# actix-multipart-derive
+
+> The derive macro implementation for actix-multipart.
diff --git a/actix-multipart-derive/src/lib.rs b/actix-multipart-derive/src/lib.rs
new file mode 100644
index 000000000..9b6ecbae6
--- /dev/null
+++ b/actix-multipart-derive/src/lib.rs
@@ -0,0 +1,315 @@
+//! Multipart form derive macro for Actix Web.
+//!
+//! See [`macro@MultipartForm`] for usage examples.
+
+#![deny(rust_2018_idioms, nonstandard_style)]
+#![warn(future_incompatible)]
+#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
+#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
+#![cfg_attr(docsrs, feature(doc_cfg))]
+
+use std::{collections::HashSet, convert::TryFrom as _};
+
+use darling::{FromDeriveInput, FromField, FromMeta};
+use parse_size::parse_size;
+use proc_macro::TokenStream;
+use proc_macro2::Ident;
+use quote::quote;
+use syn::{parse_macro_input, Type};
+
+#[derive(FromMeta)]
+enum DuplicateField {
+ Ignore,
+ Deny,
+ Replace,
+}
+
+impl Default for DuplicateField {
+ fn default() -> Self {
+ Self::Ignore
+ }
+}
+
+#[derive(FromDeriveInput, Default)]
+#[darling(attributes(multipart), default)]
+struct MultipartFormAttrs {
+ deny_unknown_fields: bool,
+ duplicate_field: DuplicateField,
+}
+
+#[derive(FromField, Default)]
+#[darling(attributes(multipart), default)]
+struct FieldAttrs {
+ rename: Option,
+ limit: Option,
+}
+
+struct ParsedField<'t> {
+ serialization_name: String,
+ rust_name: &'t Ident,
+ limit: Option,
+ ty: &'t Type,
+}
+
+/// Implements `MultipartCollect` for a struct so that it can be used with the `MultipartForm`
+/// extractor.
+///
+/// # Basic Use
+///
+/// Each field type should implement the `FieldReader` trait:
+///
+/// ```
+/// use actix_multipart::form::{tempfile::TempFile, text::Text, MultipartForm};
+///
+/// #[derive(MultipartForm)]
+/// struct ImageUpload {
+/// description: Text,
+/// timestamp: Text,
+/// image: TempFile,
+/// }
+/// ```
+///
+/// # Optional and List Fields
+///
+/// You can also use `Vec` and `Option` provided that `T: FieldReader`.
+///
+/// A [`Vec`] field corresponds to an upload with multiple parts under the [same field
+/// name](https://www.rfc-editor.org/rfc/rfc7578#section-4.3).
+///
+/// ```
+/// use actix_multipart::form::{tempfile::TempFile, text::Text, MultipartForm};
+///
+/// #[derive(MultipartForm)]
+/// struct Form {
+/// category: Option>,
+/// files: Vec,
+/// }
+/// ```
+///
+/// # Field Renaming
+///
+/// You can use the `#[multipart(rename = "foo")]` attribute to receive a field by a different name.
+///
+/// ```
+/// use actix_multipart::form::{tempfile::TempFile, MultipartForm};
+///
+/// #[derive(MultipartForm)]
+/// struct Form {
+/// #[multipart(rename = "files[]")]
+/// files: Vec,
+/// }
+/// ```
+///
+/// # Field Limits
+///
+/// You can use the `#[multipart(limit = "")]` attribute to set field level limits. The limit
+/// string is parsed using [parse_size].
+///
+/// Note: the form is also subject to the global limits configured using `MultipartFormConfig`.
+///
+/// ```
+/// use actix_multipart::form::{tempfile::TempFile, text::Text, MultipartForm};
+///
+/// #[derive(MultipartForm)]
+/// struct Form {
+/// #[multipart(limit = "2 KiB")]
+/// description: Text,
+///
+/// #[multipart(limit = "512 MiB")]
+/// files: Vec,
+/// }
+/// ```
+///
+/// # Unknown Fields
+///
+/// By default fields with an unknown name are ignored. They can be rejected using the
+/// `#[multipart(deny_unknown_fields)]` attribute:
+///
+/// ```
+/// # use actix_multipart::form::MultipartForm;
+/// #[derive(MultipartForm)]
+/// #[multipart(deny_unknown_fields)]
+/// struct Form { }
+/// ```
+///
+/// # Duplicate Fields
+///
+/// The behaviour for when multiple fields with the same name are received can be changed using the
+/// `#[multipart(duplicate_field = "")]` attribute:
+///
+/// - "ignore": (default) Extra fields are ignored. I.e., the first one is persisted.
+/// - "deny": A `MultipartError::UnsupportedField` error response is returned.
+/// - "replace": Each field is processed, but only the last one is persisted.
+///
+/// Note that `Vec` fields will ignore this option.
+///
+/// ```
+/// # use actix_multipart::form::MultipartForm;
+/// #[derive(MultipartForm)]
+/// #[multipart(duplicate_field = "deny")]
+/// struct Form { }
+/// ```
+///
+/// [parse_size]: https://docs.rs/parse-size/1/parse_size
+#[proc_macro_derive(MultipartForm, attributes(multipart))]
+pub fn impl_multipart_form(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
+ let input: syn::DeriveInput = parse_macro_input!(input);
+
+ let name = &input.ident;
+
+ let data_struct = match &input.data {
+ syn::Data::Struct(data_struct) => data_struct,
+ _ => {
+ return compile_err(syn::Error::new(
+ input.ident.span(),
+ "`MultipartForm` can only be derived for structs",
+ ))
+ }
+ };
+
+ let fields = match &data_struct.fields {
+ syn::Fields::Named(fields_named) => fields_named,
+ _ => {
+ return compile_err(syn::Error::new(
+ input.ident.span(),
+ "`MultipartForm` can only be derived for a struct with named fields",
+ ))
+ }
+ };
+
+ let attrs = match MultipartFormAttrs::from_derive_input(&input) {
+ Ok(attrs) => attrs,
+ Err(err) => return err.write_errors().into(),
+ };
+
+ // Parse the field attributes
+ let parsed = match fields
+ .named
+ .iter()
+ .map(|field| {
+ let rust_name = field.ident.as_ref().unwrap();
+ let attrs = FieldAttrs::from_field(field).map_err(|err| err.write_errors())?;
+ let serialization_name = attrs.rename.unwrap_or_else(|| rust_name.to_string());
+
+ let limit = match attrs.limit.map(|limit| match parse_size(&limit) {
+ Ok(size) => Ok(usize::try_from(size).unwrap()),
+ Err(err) => Err(syn::Error::new(
+ field.ident.as_ref().unwrap().span(),
+ format!("Could not parse size limit `{}`: {}", limit, err),
+ )),
+ }) {
+ Some(Err(err)) => return Err(compile_err(err)),
+ limit => limit.map(Result::unwrap),
+ };
+
+ Ok(ParsedField {
+ serialization_name,
+ rust_name,
+ limit,
+ ty: &field.ty,
+ })
+ })
+ .collect::, TokenStream>>()
+ {
+ Ok(attrs) => attrs,
+ Err(err) => return err,
+ };
+
+ // Check that field names are unique
+ let mut set = HashSet::new();
+ for field in &parsed {
+ if !set.insert(field.serialization_name.clone()) {
+ return compile_err(syn::Error::new(
+ field.rust_name.span(),
+ format!("Multiple fields named: `{}`", field.serialization_name),
+ ));
+ }
+ }
+
+ // Return value when a field name is not supported by the form
+ let unknown_field_result = if attrs.deny_unknown_fields {
+ quote!(::std::result::Result::Err(
+ ::actix_multipart::MultipartError::UnsupportedField(field.name().to_string())
+ ))
+ } else {
+ quote!(::std::result::Result::Ok(()))
+ };
+
+ // Value for duplicate action
+ let duplicate_field = match attrs.duplicate_field {
+ DuplicateField::Ignore => quote!(::actix_multipart::form::DuplicateField::Ignore),
+ DuplicateField::Deny => quote!(::actix_multipart::form::DuplicateField::Deny),
+ DuplicateField::Replace => quote!(::actix_multipart::form::DuplicateField::Replace),
+ };
+
+ // limit() implementation
+ let mut limit_impl = quote!();
+ for field in &parsed {
+ let name = &field.serialization_name;
+ if let Some(value) = field.limit {
+ limit_impl.extend(quote!(
+ #name => ::std::option::Option::Some(#value),
+ ));
+ }
+ }
+
+ // handle_field() implementation
+ let mut handle_field_impl = quote!();
+ for field in &parsed {
+ let name = &field.serialization_name;
+ let ty = &field.ty;
+
+ handle_field_impl.extend(quote!(
+ #name => ::std::boxed::Box::pin(
+ <#ty as ::actix_multipart::form::FieldGroupReader>::handle_field(req, field, limits, state, #duplicate_field)
+ ),
+ ));
+ }
+
+ // from_state() implementation
+ let mut from_state_impl = quote!();
+ for field in &parsed {
+ let name = &field.serialization_name;
+ let rust_name = &field.rust_name;
+ let ty = &field.ty;
+ from_state_impl.extend(quote!(
+ #rust_name: <#ty as ::actix_multipart::form::FieldGroupReader>::from_state(#name, &mut state)?,
+ ));
+ }
+
+ let gen = quote! {
+ impl ::actix_multipart::form::MultipartCollect for #name {
+ fn limit(field_name: &str) -> ::std::option::Option {
+ match field_name {
+ #limit_impl
+ _ => None,
+ }
+ }
+
+ fn handle_field<'t>(
+ req: &'t ::actix_web::HttpRequest,
+ field: ::actix_multipart::Field,
+ limits: &'t mut ::actix_multipart::form::Limits,
+ state: &'t mut ::actix_multipart::form::State,
+ ) -> ::std::pin::Pin<::std::boxed::Box> + 't>> {
+ match field.name() {
+ #handle_field_impl
+ _ => return ::std::boxed::Box::pin(::std::future::ready(#unknown_field_result)),
+ }
+ }
+
+ fn from_state(mut state: ::actix_multipart::form::State) -> ::std::result::Result {
+ Ok(Self {
+ #from_state_impl
+ })
+ }
+
+ }
+ };
+ gen.into()
+}
+
+/// Transform a syn error into a token stream for returning.
+fn compile_err(err: syn::Error) -> TokenStream {
+ TokenStream::from(err.to_compile_error())
+}
diff --git a/actix-multipart-derive/tests/trybuild.rs b/actix-multipart-derive/tests/trybuild.rs
new file mode 100644
index 000000000..7b9f14ed7
--- /dev/null
+++ b/actix-multipart-derive/tests/trybuild.rs
@@ -0,0 +1,16 @@
+#[rustversion::stable(1.59)] // MSRV
+#[test]
+fn compile_macros() {
+ let t = trybuild::TestCases::new();
+
+ t.pass("tests/trybuild/all-required.rs");
+ t.pass("tests/trybuild/optional-and-list.rs");
+ t.pass("tests/trybuild/rename.rs");
+ t.pass("tests/trybuild/deny-unknown.rs");
+
+ t.pass("tests/trybuild/deny-duplicates.rs");
+ t.compile_fail("tests/trybuild/deny-parse-fail.rs");
+
+ t.pass("tests/trybuild/size-limits.rs");
+ t.compile_fail("tests/trybuild/size-limit-parse-fail.rs");
+}
diff --git a/actix-multipart-derive/tests/trybuild/all-required.rs b/actix-multipart-derive/tests/trybuild/all-required.rs
new file mode 100644
index 000000000..1b4a824d9
--- /dev/null
+++ b/actix-multipart-derive/tests/trybuild/all-required.rs
@@ -0,0 +1,19 @@
+use actix_web::{web, App, Responder};
+
+use actix_multipart::form::{tempfile::TempFile, text::Text, MultipartForm};
+
+#[derive(Debug, MultipartForm)]
+struct ImageUpload {
+ description: Text,
+ timestamp: Text,
+ image: TempFile,
+}
+
+async fn handler(_form: MultipartForm) -> impl Responder {
+ "Hello World!"
+}
+
+#[actix_web::main]
+async fn main() {
+ App::new().default_service(web::to(handler));
+}
diff --git a/actix-multipart-derive/tests/trybuild/deny-duplicates.rs b/actix-multipart-derive/tests/trybuild/deny-duplicates.rs
new file mode 100644
index 000000000..9fcc1506c
--- /dev/null
+++ b/actix-multipart-derive/tests/trybuild/deny-duplicates.rs
@@ -0,0 +1,16 @@
+use actix_web::{web, App, Responder};
+
+use actix_multipart::form::MultipartForm;
+
+#[derive(MultipartForm)]
+#[multipart(duplicate_field = "deny")]
+struct Form {}
+
+async fn handler(_form: MultipartForm
-[](https://crates.io/crates/actix-web)
-[](https://docs.rs/actix-web/4.2.1)
-
-
-[](https://deps.rs/crate/actix-web/4.2.1)
-
-[](https://github.com/actix/actix-web/actions/workflows/ci.yml)
-[](https://codecov.io/gh/actix/actix-web)
-
-[](https://discord.gg/NWpN5mmg3x)
+[](https://crates.io/crates/actix-web) [](https://docs.rs/actix-web/4.3.1)   [](https://deps.rs/crate/actix-web/4.3.1)
[](https://github.com/actix/actix-web/actions/workflows/ci.yml) [](https://codecov.io/gh/actix/actix-web)  [](https://discord.gg/NWpN5mmg3x)
diff --git a/actix-web/examples/uds.rs b/actix-web/examples/uds.rs
index ba4b25a29..15e28ba1d 100644
--- a/actix-web/examples/uds.rs
+++ b/actix-web/examples/uds.rs
@@ -41,7 +41,7 @@ async fn main() -> std::io::Result<()> {
)
.service(web::resource("/test1.html").to(|| async { "Test\r\n" }))
})
- .bind_uds("/Users/fafhrd91/uds-test")?
+ .bind_uds("/Users/me/uds-test")?
.workers(1)
.run()
.await
diff --git a/actix-web/src/app_service.rs b/actix-web/src/app_service.rs
index 0b5ba2ab6..0fc856203 100644
--- a/actix-web/src/app_service.rs
+++ b/actix-web/src/app_service.rs
@@ -21,7 +21,7 @@ use crate::{
Error, HttpResponse,
};
-/// Service factory to convert `Request` to a `ServiceRequest`.
+/// Service factory to convert [`Request`] to a [`ServiceRequest`].
///
/// It also executes data factories.
pub struct AppInit
@@ -155,7 +155,7 @@ where
app_state: Rc,
}
-/// A collection of [`AppInitService`] state that shared across `HttpRequest`s.
+/// A collection of state for [`AppInitService`] that is shared across [`HttpRequest`]s.
pub(crate) struct AppInitServiceState {
rmap: Rc,
config: AppConfig,
@@ -163,6 +163,7 @@ pub(crate) struct AppInitServiceState {
}
impl AppInitServiceState {
+ /// Constructs state collection from resource map and app config.
pub(crate) fn new(rmap: Rc, config: AppConfig) -> Rc {
Rc::new(AppInitServiceState {
rmap,
@@ -171,16 +172,19 @@ impl AppInitServiceState {
})
}
+ /// Returns a reference to the application's resource map.
#[inline]
pub(crate) fn rmap(&self) -> &ResourceMap {
&self.rmap
}
+ /// Returns a reference to the application's configuration.
#[inline]
pub(crate) fn config(&self) -> &AppConfig {
&self.config
}
+ /// Returns a reference to the application's request pool.
#[inline]
pub(crate) fn pool(&self) -> &HttpRequestPool {
&self.pool
diff --git a/actix-web/src/config.rs b/actix-web/src/config.rs
index 68bea34ca..11eaf8720 100644
--- a/actix-web/src/config.rs
+++ b/actix-web/src/config.rs
@@ -141,7 +141,7 @@ impl AppConfig {
self.secure
}
- /// Returns the socket address of the local half of this TCP connection
+ /// Returns the socket address of the local half of this TCP connection.
pub fn local_addr(&self) -> SocketAddr {
self.addr
}
diff --git a/actix-web/src/guard/host.rs b/actix-web/src/guard/host.rs
new file mode 100644
index 000000000..f05c81183
--- /dev/null
+++ b/actix-web/src/guard/host.rs
@@ -0,0 +1,209 @@
+use actix_http::{header, uri::Uri, RequestHead};
+
+use super::{Guard, GuardContext};
+
+/// Creates a guard that matches requests targetting a specific host.
+///
+/// # Matching Host
+/// This guard will:
+/// - match against the `Host` header, if present;
+/// - fall-back to matching against the request target's host, if present;
+/// - return false if host cannot be determined;
+///
+/// # Matching Scheme
+/// Optionally, this guard can match against the host's scheme. Set the scheme for matching using
+/// `Host(host).scheme(protocol)`. If the request's scheme cannot be determined, it will not prevent
+/// the guard from matching successfully.
+///
+/// # Examples
+/// The `Host` guard can be used to set up a form of [virtual hosting] within a single app.
+/// Overlapping scope prefixes are usually discouraged, but when combined with non-overlapping guard
+/// definitions they become safe to use in this way. Without these host guards, only routes under
+/// the first-to-be-defined scope would be accessible. You can test this locally using `127.0.0.1`
+/// and `localhost` as the `Host` guards.
+/// ```
+/// use actix_web::{web, http::Method, guard, App, HttpResponse};
+///
+/// App::new()
+/// .service(
+/// web::scope("")
+/// .guard(guard::Host("www.rust-lang.org"))
+/// .default_service(web::to(|| async {
+/// HttpResponse::Ok().body("marketing site")
+/// })),
+/// )
+/// .service(
+/// web::scope("")
+/// .guard(guard::Host("play.rust-lang.org"))
+/// .default_service(web::to(|| async {
+/// HttpResponse::Ok().body("playground frontend")
+/// })),
+/// );
+/// ```
+///
+/// The example below additionally guards on the host URI's scheme. This could allow routing to
+/// different handlers for `http:` vs `https:` visitors; to redirect, for example.
+/// ```
+/// use actix_web::{web, guard::Host, HttpResponse};
+///
+/// web::scope("/admin")
+/// .guard(Host("admin.rust-lang.org").scheme("https"))
+/// .default_service(web::to(|| async {
+/// HttpResponse::Ok().body("admin connection is secure")
+/// }));
+/// ```
+///
+/// [virtual hosting]: https://en.wikipedia.org/wiki/Virtual_hosting
+#[allow(non_snake_case)]
+pub fn Host(host: impl AsRef) -> HostGuard {
+ HostGuard {
+ host: host.as_ref().to_string(),
+ scheme: None,
+ }
+}
+
+fn get_host_uri(req: &RequestHead) -> Option {
+ req.headers
+ .get(header::HOST)
+ .and_then(|host_value| host_value.to_str().ok())
+ .or_else(|| req.uri.host())
+ .and_then(|host| host.parse().ok())
+}
+
+#[doc(hidden)]
+pub struct HostGuard {
+ host: String,
+ scheme: Option,
+}
+
+impl HostGuard {
+ /// Set request scheme to match
+ pub fn scheme>(mut self, scheme: H) -> HostGuard {
+ self.scheme = Some(scheme.as_ref().to_string());
+ self
+ }
+}
+
+impl Guard for HostGuard {
+ fn check(&self, ctx: &GuardContext<'_>) -> bool {
+ // parse host URI from header or request target
+ let req_host_uri = match get_host_uri(ctx.head()) {
+ Some(uri) => uri,
+
+ // no match if host cannot be determined
+ None => return false,
+ };
+
+ match req_host_uri.host() {
+ // fall through to scheme checks
+ Some(uri_host) if self.host == uri_host => {}
+
+ // Either:
+ // - request's host does not match guard's host;
+ // - It was possible that the parsed URI from request target did not contain a host.
+ _ => return false,
+ }
+
+ if let Some(ref scheme) = self.scheme {
+ if let Some(ref req_host_uri_scheme) = req_host_uri.scheme_str() {
+ return scheme == req_host_uri_scheme;
+ }
+
+ // TODO: is this the correct behavior?
+ // falls through if scheme cannot be determined
+ }
+
+ // all conditions passed
+ true
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::test::TestRequest;
+
+ #[test]
+ fn host_from_header() {
+ let req = TestRequest::default()
+ .insert_header((
+ header::HOST,
+ header::HeaderValue::from_static("www.rust-lang.org"),
+ ))
+ .to_srv_request();
+
+ let host = Host("www.rust-lang.org");
+ assert!(host.check(&req.guard_ctx()));
+
+ let host = Host("www.rust-lang.org").scheme("https");
+ assert!(host.check(&req.guard_ctx()));
+
+ let host = Host("blog.rust-lang.org");
+ assert!(!host.check(&req.guard_ctx()));
+
+ let host = Host("blog.rust-lang.org").scheme("https");
+ assert!(!host.check(&req.guard_ctx()));
+
+ let host = Host("crates.io");
+ assert!(!host.check(&req.guard_ctx()));
+
+ let host = Host("localhost");
+ assert!(!host.check(&req.guard_ctx()));
+ }
+
+ #[test]
+ fn host_without_header() {
+ let req = TestRequest::default()
+ .uri("www.rust-lang.org")
+ .to_srv_request();
+
+ let host = Host("www.rust-lang.org");
+ assert!(host.check(&req.guard_ctx()));
+
+ let host = Host("www.rust-lang.org").scheme("https");
+ assert!(host.check(&req.guard_ctx()));
+
+ let host = Host("blog.rust-lang.org");
+ assert!(!host.check(&req.guard_ctx()));
+
+ let host = Host("blog.rust-lang.org").scheme("https");
+ assert!(!host.check(&req.guard_ctx()));
+
+ let host = Host("crates.io");
+ assert!(!host.check(&req.guard_ctx()));
+
+ let host = Host("localhost");
+ assert!(!host.check(&req.guard_ctx()));
+ }
+
+ #[test]
+ fn host_scheme() {
+ let req = TestRequest::default()
+ .insert_header((
+ header::HOST,
+ header::HeaderValue::from_static("https://www.rust-lang.org"),
+ ))
+ .to_srv_request();
+
+ let host = Host("www.rust-lang.org").scheme("https");
+ assert!(host.check(&req.guard_ctx()));
+
+ let host = Host("www.rust-lang.org");
+ assert!(host.check(&req.guard_ctx()));
+
+ let host = Host("www.rust-lang.org").scheme("http");
+ assert!(!host.check(&req.guard_ctx()));
+
+ let host = Host("blog.rust-lang.org");
+ assert!(!host.check(&req.guard_ctx()));
+
+ let host = Host("blog.rust-lang.org").scheme("https");
+ assert!(!host.check(&req.guard_ctx()));
+
+ let host = Host("crates.io").scheme("https");
+ assert!(!host.check(&req.guard_ctx()));
+
+ let host = Host("localhost");
+ assert!(!host.check(&req.guard_ctx()));
+ }
+}
diff --git a/actix-web/src/guard/mod.rs b/actix-web/src/guard/mod.rs
index e086f8648..164032bdc 100644
--- a/actix-web/src/guard/mod.rs
+++ b/actix-web/src/guard/mod.rs
@@ -52,12 +52,15 @@ use std::{
rc::Rc,
};
-use actix_http::{header, uri::Uri, Extensions, Method as HttpMethod, RequestHead};
+use actix_http::{header, Extensions, Method as HttpMethod, RequestHead};
use crate::{http::header::Header, service::ServiceRequest, HttpMessage as _};
mod acceptable;
+mod host;
+
pub use self::acceptable::Acceptable;
+pub use self::host::{Host, HostGuard};
/// Provides access to request parts that are useful during routing.
#[derive(Debug)]
@@ -371,124 +374,6 @@ impl Guard for HeaderGuard {
}
}
-/// Creates a guard that matches requests targetting a specific host.
-///
-/// # Matching Host
-/// This guard will:
-/// - match against the `Host` header, if present;
-/// - fall-back to matching against the request target's host, if present;
-/// - return false if host cannot be determined;
-///
-/// # Matching Scheme
-/// Optionally, this guard can match against the host's scheme. Set the scheme for matching using
-/// `Host(host).scheme(protocol)`. If the request's scheme cannot be determined, it will not prevent
-/// the guard from matching successfully.
-///
-/// # Examples
-/// The [module-level documentation](self) has an example of virtual hosting using `Host` guards.
-///
-/// The example below additionally guards on the host URI's scheme. This could allow routing to
-/// different handlers for `http:` vs `https:` visitors; to redirect, for example.
-/// ```
-/// use actix_web::{web, guard::Host, HttpResponse};
-///
-/// web::scope("/admin")
-/// .guard(Host("admin.rust-lang.org").scheme("https"))
-/// .default_service(web::to(|| async {
-/// HttpResponse::Ok().body("admin connection is secure")
-/// }));
-/// ```
-///
-/// The `Host` guard can be used to set up some form of [virtual hosting] within a single app.
-/// Overlapping scope prefixes are usually discouraged, but when combined with non-overlapping guard
-/// definitions they become safe to use in this way. Without these host guards, only routes under
-/// the first-to-be-defined scope would be accessible. You can test this locally using `127.0.0.1`
-/// and `localhost` as the `Host` guards.
-/// ```
-/// use actix_web::{web, http::Method, guard, App, HttpResponse};
-///
-/// App::new()
-/// .service(
-/// web::scope("")
-/// .guard(guard::Host("www.rust-lang.org"))
-/// .default_service(web::to(|| async {
-/// HttpResponse::Ok().body("marketing site")
-/// })),
-/// )
-/// .service(
-/// web::scope("")
-/// .guard(guard::Host("play.rust-lang.org"))
-/// .default_service(web::to(|| async {
-/// HttpResponse::Ok().body("playground frontend")
-/// })),
-/// );
-/// ```
-///
-/// [virtual hosting]: https://en.wikipedia.org/wiki/Virtual_hosting
-#[allow(non_snake_case)]
-pub fn Host(host: impl AsRef) -> HostGuard {
- HostGuard {
- host: host.as_ref().to_string(),
- scheme: None,
- }
-}
-
-fn get_host_uri(req: &RequestHead) -> Option {
- req.headers
- .get(header::HOST)
- .and_then(|host_value| host_value.to_str().ok())
- .or_else(|| req.uri.host())
- .and_then(|host| host.parse().ok())
-}
-
-#[doc(hidden)]
-pub struct HostGuard {
- host: String,
- scheme: Option,
-}
-
-impl HostGuard {
- /// Set request scheme to match
- pub fn scheme>(mut self, scheme: H) -> HostGuard {
- self.scheme = Some(scheme.as_ref().to_string());
- self
- }
-}
-
-impl Guard for HostGuard {
- fn check(&self, ctx: &GuardContext<'_>) -> bool {
- // parse host URI from header or request target
- let req_host_uri = match get_host_uri(ctx.head()) {
- Some(uri) => uri,
-
- // no match if host cannot be determined
- None => return false,
- };
-
- match req_host_uri.host() {
- // fall through to scheme checks
- Some(uri_host) if self.host == uri_host => {}
-
- // Either:
- // - request's host does not match guard's host;
- // - It was possible that the parsed URI from request target did not contain a host.
- _ => return false,
- }
-
- if let Some(ref scheme) = self.scheme {
- if let Some(ref req_host_uri_scheme) = req_host_uri.scheme_str() {
- return scheme == req_host_uri_scheme;
- }
-
- // TODO: is this the correct behavior?
- // falls through if scheme cannot be determined
- }
-
- // all conditions passed
- true
- }
-}
-
#[cfg(test)]
mod tests {
use actix_http::{header, Method};
@@ -515,90 +400,6 @@ mod tests {
assert!(!hdr.check(&req.guard_ctx()));
}
- #[test]
- fn host_from_header() {
- let req = TestRequest::default()
- .insert_header((
- header::HOST,
- header::HeaderValue::from_static("www.rust-lang.org"),
- ))
- .to_srv_request();
-
- let host = Host("www.rust-lang.org");
- assert!(host.check(&req.guard_ctx()));
-
- let host = Host("www.rust-lang.org").scheme("https");
- assert!(host.check(&req.guard_ctx()));
-
- let host = Host("blog.rust-lang.org");
- assert!(!host.check(&req.guard_ctx()));
-
- let host = Host("blog.rust-lang.org").scheme("https");
- assert!(!host.check(&req.guard_ctx()));
-
- let host = Host("crates.io");
- assert!(!host.check(&req.guard_ctx()));
-
- let host = Host("localhost");
- assert!(!host.check(&req.guard_ctx()));
- }
-
- #[test]
- fn host_without_header() {
- let req = TestRequest::default()
- .uri("www.rust-lang.org")
- .to_srv_request();
-
- let host = Host("www.rust-lang.org");
- assert!(host.check(&req.guard_ctx()));
-
- let host = Host("www.rust-lang.org").scheme("https");
- assert!(host.check(&req.guard_ctx()));
-
- let host = Host("blog.rust-lang.org");
- assert!(!host.check(&req.guard_ctx()));
-
- let host = Host("blog.rust-lang.org").scheme("https");
- assert!(!host.check(&req.guard_ctx()));
-
- let host = Host("crates.io");
- assert!(!host.check(&req.guard_ctx()));
-
- let host = Host("localhost");
- assert!(!host.check(&req.guard_ctx()));
- }
-
- #[test]
- fn host_scheme() {
- let req = TestRequest::default()
- .insert_header((
- header::HOST,
- header::HeaderValue::from_static("https://www.rust-lang.org"),
- ))
- .to_srv_request();
-
- let host = Host("www.rust-lang.org").scheme("https");
- assert!(host.check(&req.guard_ctx()));
-
- let host = Host("www.rust-lang.org");
- assert!(host.check(&req.guard_ctx()));
-
- let host = Host("www.rust-lang.org").scheme("http");
- assert!(!host.check(&req.guard_ctx()));
-
- let host = Host("blog.rust-lang.org");
- assert!(!host.check(&req.guard_ctx()));
-
- let host = Host("blog.rust-lang.org").scheme("https");
- assert!(!host.check(&req.guard_ctx()));
-
- let host = Host("crates.io").scheme("https");
- assert!(!host.check(&req.guard_ctx()));
-
- let host = Host("localhost");
- assert!(!host.check(&req.guard_ctx()));
- }
-
#[test]
fn method_guards() {
let get_req = TestRequest::get().to_srv_request();
diff --git a/actix-web/src/info.rs b/actix-web/src/info.rs
index 7c685406e..c5d9638f4 100644
--- a/actix-web/src/info.rs
+++ b/actix-web/src/info.rs
@@ -76,7 +76,6 @@ impl ConnectionInfo {
for (name, val) in req
.headers
.get_all(&header::FORWARDED)
- .into_iter()
.filter_map(|hdr| hdr.to_str().ok())
// "for=1.2.3.4, for=5.6.7.8; scheme=https"
.flat_map(|val| val.split(';'))
diff --git a/actix-web/src/middleware/authors-guide.md b/actix-web/src/middleware/authors-guide.md
index a8d1edea4..64bad15c2 100644
--- a/actix-web/src/middleware/authors-guide.md
+++ b/actix-web/src/middleware/authors-guide.md
@@ -13,4 +13,5 @@
## When To (Not) Use Middleware
## Author's References
+
- `EitherBody` + when is middleware appropriate: https://discord.com/channels/771444961383153695/952016890723729428
diff --git a/actix-web/src/middleware/err_handlers.rs b/actix-web/src/middleware/err_handlers.rs
index 4ddbc6318..00fb5cf59 100644
--- a/actix-web/src/middleware/err_handlers.rs
+++ b/actix-web/src/middleware/err_handlers.rs
@@ -50,6 +50,8 @@ type DefaultHandler = Option>>;
/// will pass by unchanged by this middleware.
///
/// # Examples
+/// ## Handler Response
+/// Header
/// ```
/// use actix_web::http::{header, StatusCode};
/// use actix_web::middleware::{ErrorHandlerResponse, ErrorHandlers};
@@ -67,6 +69,28 @@ type DefaultHandler = Option>>;
/// .wrap(ErrorHandlers::new().handler(StatusCode::INTERNAL_SERVER_ERROR, add_error_header))
/// .service(web::resource("/").route(web::get().to(HttpResponse::InternalServerError)));
/// ```
+///
+/// Body Content
+/// ```
+/// use actix_web::http::{header, StatusCode};
+/// use actix_web::middleware::{ErrorHandlerResponse, ErrorHandlers};
+/// use actix_web::{dev, web, App, HttpResponse, Result};
+/// fn add_error_body(res: dev::ServiceResponse) -> Result> {
+/// // Get the error message and status code
+/// let error_message = "An error occurred";
+/// // Destructures ServiceResponse into request and response components
+/// let (req, res) = res.into_parts();
+/// // Create a new response with the modified body
+/// let res = res.set_body(error_message).map_into_boxed_body();
+/// // Create a new ServiceResponse with the modified response
+/// let res = dev::ServiceResponse::new(req, res).map_into_right_body();
+/// Ok(ErrorHandlerResponse::Response(res))
+///}
+///
+/// let app = App::new()
+/// .wrap(ErrorHandlers::new().handler(StatusCode::INTERNAL_SERVER_ERROR, add_error_body))
+/// .service(web::resource("/").route(web::get().to(HttpResponse::InternalServerError)));
+/// ```
/// ## Registering default handler
/// ```
/// # use actix_web::http::{header, StatusCode};
diff --git a/actix-web/src/request.rs b/actix-web/src/request.rs
index 6a32bf838..a99dcaa3e 100644
--- a/actix-web/src/request.rs
+++ b/actix-web/src/request.rs
@@ -260,7 +260,7 @@ impl HttpRequest {
Ref::map(self.extensions(), |data| data.get().unwrap())
}
- /// App config
+ /// Returns a reference to the application's connection configuration.
#[inline]
pub fn app_config(&self) -> &AppConfig {
self.app_state().config()
diff --git a/actix-web/src/response/responder.rs b/actix-web/src/response/responder.rs
index da8091981..965163a1f 100644
--- a/actix-web/src/response/responder.rs
+++ b/actix-web/src/response/responder.rs
@@ -21,7 +21,7 @@ use crate::{Error, HttpRequest, HttpResponse};
/// - `HttpResponse` and `HttpResponseBuilder`
/// - `Option` where `R: Responder`
/// - `Result` where `R: Responder` and [`E: ResponseError`](crate::ResponseError)
-/// - `(R, StatusCode) where `R: Responder`
+/// - `(R, StatusCode)` where `R: Responder`
/// - `&'static str`, `String`, `&'_ String`, `Cow<'_, str>`, [`ByteString`](bytestring::ByteString)
/// - `&'static [u8]`, `Vec`, `Bytes`, `BytesMut`
/// - [`Json`](crate::web::Json) and [`Form`](crate::web::Form) where `T: Serialize`
diff --git a/actix-web/src/service.rs b/actix-web/src/service.rs
index ea23f09f5..f7692ce16 100644
--- a/actix-web/src/service.rs
+++ b/actix-web/src/service.rs
@@ -238,11 +238,7 @@ impl ServiceRequest {
self.req.connection_info()
}
- /// Returns reference to the Path parameters.
- ///
- /// Params is a container for URL parameters. A variable segment is specified in the form
- /// `{identifier}`, where the identifier can be used later in a request handler to access the
- /// matched value for that segment.
+ /// Counterpart to [`HttpRequest::match_info`].
#[inline]
pub fn match_info(&self) -> &Path {
self.req.match_info()
@@ -267,12 +263,13 @@ impl ServiceRequest {
}
/// Returns a reference to the application's resource map.
+ /// Counterpart to [`HttpRequest::resource_map`].
#[inline]
pub fn resource_map(&self) -> &ResourceMap {
self.req.resource_map()
}
- /// Returns a reference to the application's configuration.
+ /// Counterpart to [`HttpRequest::app_config`].
#[inline]
pub fn app_config(&self) -> &AppConfig {
self.req.app_config()
diff --git a/actix-web/src/test/mod.rs b/actix-web/src/test/mod.rs
index 9c6121151..5d9367b82 100644
--- a/actix-web/src/test/mod.rs
+++ b/actix-web/src/test/mod.rs
@@ -10,12 +10,16 @@
//! # Calling Test Service
//! - [`TestRequest`]
//! - [`call_service`]
+//! - [`try_call_service`]
//! - [`call_and_read_body`]
//! - [`call_and_read_body_json`]
+//! - [`try_call_and_read_body_json`]
//!
//! # Reading Response Payloads
//! - [`read_body`]
+//! - [`try_read_body`]
//! - [`read_body_json`]
+//! - [`try_read_body_json`]
// TODO: more docs on generally how testing works with these parts
@@ -31,7 +35,8 @@ pub use self::test_services::{default_service, ok_service, simple_service, statu
#[allow(deprecated)]
pub use self::test_utils::{
call_and_read_body, call_and_read_body_json, call_service, init_service, read_body,
- read_body_json, read_response, read_response_json,
+ read_body_json, read_response, read_response_json, try_call_and_read_body_json,
+ try_call_service, try_read_body, try_read_body_json,
};
#[cfg(test)]
diff --git a/actix-web/src/test/test_utils.rs b/actix-web/src/test/test_utils.rs
index 6f0926f35..b985c3b36 100644
--- a/actix-web/src/test/test_utils.rs
+++ b/actix-web/src/test/test_utils.rs
@@ -100,6 +100,15 @@ where
.expect("test service call returned error")
}
+/// Fallible version of [`call_service`] that allows testing response completion errors.
+pub async fn try_call_service(app: &S, req: R) -> Result
+where
+ S: Service, Error = E>,
+ E: std::fmt::Debug,
+{
+ app.call(req).await
+}
+
/// Helper function that returns a response body of a TestRequest
///
/// # Examples
@@ -185,13 +194,23 @@ pub async fn read_body(res: ServiceResponse) -> Bytes
where
B: MessageBody,
{
- let body = res.into_body();
- body::to_bytes(body)
+ try_read_body(res)
.await
.map_err(Into::>::into)
.expect("error reading test response body")
}
+/// Fallible version of [`read_body`] that allows testing MessageBody reading errors.
+pub async fn try_read_body(
+ res: ServiceResponse,
+) -> Result::Error>
+where
+ B: MessageBody,
+{
+ let body = res.into_body();
+ body::to_bytes(body).await
+}
+
/// Helper function that returns a deserialized response body of a ServiceResponse.
///
/// # Examples
@@ -240,18 +259,27 @@ where
B: MessageBody,
T: DeserializeOwned,
{
- let body = read_body(res).await;
-
- serde_json::from_slice(&body).unwrap_or_else(|err| {
+ try_read_body_json(res).await.unwrap_or_else(|err| {
panic!(
- "could not deserialize body into a {}\nerr: {}\nbody: {:?}",
+ "could not deserialize body into a {}\nerr: {}",
std::any::type_name::(),
err,
- body,
)
})
}
+/// Fallible version of [`read_body_json`] that allows testing response deserialization errors.
+pub async fn try_read_body_json(res: ServiceResponse) -> Result>
+where
+ B: MessageBody,
+ T: DeserializeOwned,
+{
+ let body = try_read_body(res)
+ .await
+ .map_err(Into::>::into)?;
+ serde_json::from_slice(&body).map_err(Into::>::into)
+}
+
/// Helper function that returns a deserialized response body of a TestRequest
///
/// # Examples
@@ -299,8 +327,23 @@ where
B: MessageBody,
T: DeserializeOwned,
{
- let res = call_service(app, req).await;
- read_body_json(res).await
+ try_call_and_read_body_json(app, req).await.unwrap()
+}
+
+/// Fallible version of [`call_and_read_body_json`] that allows testing service call errors.
+pub async fn try_call_and_read_body_json(
+ app: &S,
+ req: Request,
+) -> Result>
+where
+ S: Service, Error = Error>,
+ B: MessageBody,
+ T: DeserializeOwned,
+{
+ let res = try_call_service(app, req)
+ .await
+ .map_err(Into::>::into)?;
+ try_read_body_json(res).await
}
#[doc(hidden)]
@@ -358,7 +401,7 @@ mod tests {
assert_eq!(result, Bytes::from_static(b"delete!"));
}
- #[derive(Serialize, Deserialize)]
+ #[derive(Serialize, Deserialize, Debug)]
pub struct Person {
id: String,
name: String,
@@ -383,6 +426,26 @@ mod tests {
assert_eq!(&result.id, "12345");
}
+ #[actix_rt::test]
+ async fn test_try_response_json_error() {
+ let app = init_service(App::new().service(web::resource("/people").route(
+ web::post().to(|person: web::Json| HttpResponse::Ok().json(person)),
+ )))
+ .await;
+
+ let payload = r#"{"id":"12345","name":"User name"}"#.as_bytes();
+
+ let req = TestRequest::post()
+ .uri("/animals") // Not registered to ensure an error occurs.
+ .insert_header((header::CONTENT_TYPE, "application/json"))
+ .set_payload(payload)
+ .to_request();
+
+ let result: Result> =
+ try_call_and_read_body_json(&app, req).await;
+ assert!(result.is_err());
+ }
+
#[actix_rt::test]
async fn test_body_json() {
let app = init_service(App::new().service(web::resource("/people").route(
@@ -403,6 +466,27 @@ mod tests {
assert_eq!(&result.name, "User name");
}
+ #[actix_rt::test]
+ async fn test_try_body_json_error() {
+ let app = init_service(App::new().service(web::resource("/people").route(
+ web::post().to(|person: web::Json| HttpResponse::Ok().json(person)),
+ )))
+ .await;
+
+ // Use a number for id to cause a deserialization error.
+ let payload = r#"{"id":12345,"name":"User name"}"#.as_bytes();
+
+ let res = TestRequest::post()
+ .uri("/people")
+ .insert_header((header::CONTENT_TYPE, "application/json"))
+ .set_payload(payload)
+ .send_request(&app)
+ .await;
+
+ let result: Result> = try_read_body_json(res).await;
+ assert!(result.is_err());
+ }
+
#[actix_rt::test]
async fn test_request_response_form() {
let app = init_service(App::new().service(web::resource("/people").route(
diff --git a/awc/CHANGES.md b/awc/CHANGES.md
index 7892d9339..03cbf61d4 100644
--- a/awc/CHANGES.md
+++ b/awc/CHANGES.md
@@ -1,22 +1,35 @@
# Changes
-## Unreleased - 2022-xx-xx
+## Unreleased - 2023-xx-xx
+
+## 3.1.1 - 2023-02-26
+
### Changed
+
+- `client::Connect` is now public to allow tunneling connection with `client::Connector`.
+
+## 3.1.0 - 2023-01-21
+
+### Changed
+
- Minimum supported Rust version (MSRV) is now 1.59 due to transitive `time` dependency.
-
## 3.0.1 - 2022-08-25
+
### Changed
+
- Minimum supported Rust version (MSRV) is now 1.57 due to transitive `time` dependency.
### Fixed
+
- Fixed handling of redirection requests that begin with `//`. [#2840]
[#2840]: https://github.com/actix/actix-web/pull/2840
-
## 3.0.0 - 2022-03-07
+
### Dependencies
+
- Updated `actix-*` to Tokio v1-based versions. [#1813]
- Updated `bytes` to `1.0`. [#1813]
- Updated `cookie` to `0.16`. [#2555]
@@ -25,6 +38,7 @@
- Updated `tokio` to `1`.
### Added
+
- `trust-dns` crate feature to enable `trust-dns-resolver` as client DNS resolver; disabled by default. [#1969]
- `cookies` crate feature; enabled by default. [#2619]
- `compress-brotli` crate feature; enabled by default. [#2250]
@@ -41,6 +55,7 @@
- `ClientBuilder::add_default_header()` (and deprecate `ClientBuilder::header()`). [#2510]
### Changed
+
- `client::Connector` type now only has one generic type for `actix_service::Service`. [#2063]
- `client::error::ConnectError` Resolver variant contains `Box` type. [#1905]
- `client::ConnectorConfig` default timeout changed to 5 seconds. [#1905]
@@ -58,6 +73,7 @@
- Minimum supported Rust version (MSRV) is now 1.54.
### Fixed
+
- Send headers along with redirected requests. [#2310]
- Improve `Client` instantiation efficiency when using `openssl` by only building connectors once. [#2503]
- Remove unnecessary `Unpin` bounds on `*::send_stream`. [#2553]
@@ -66,6 +82,7 @@
- `impl Stream` for `ClientResponse` no longer requires the body type be `Unpin`. [#2546]
### Removed
+
- `compress` crate feature. [#2250]
- `ClientRequest::set`; use `ClientRequest::insert_header`. [#1869]
- `ClientRequest::set_header`; use `ClientRequest::insert_header`. [#1869]
@@ -75,10 +92,10 @@
- `ClientBuilder::default` function [#2008]
### Security
+
- `cookie` upgrade addresses [`RUSTSEC-2020-0071`].
-[`RUSTSEC-2020-0071`]: https://rustsec.org/advisories/RUSTSEC-2020-0071.html
-
+[`rustsec-2020-0071`]: https://rustsec.org/advisories/RUSTSEC-2020-0071.html
[#1813]: https://github.com/actix/actix-web/pull/1813
[#1869]: https://github.com/actix/actix-web/pull/1869
[#1905]: https://github.com/actix/actix-web/pull/1905
@@ -108,46 +125,48 @@
[#2553]: https://github.com/actix/actix-web/pull/2553
[#2555]: https://github.com/actix/actix-web/pull/2555
-
3.0.0 Pre-Releases
## 3.0.0-beta.21 - 2022-02-16
+
- No significant changes since `3.0.0-beta.20`.
-
## 3.0.0-beta.20 - 2022-01-31
+
- No significant changes since `3.0.0-beta.19`.
-
## 3.0.0-beta.19 - 2022-01-21
+
- No significant changes since `3.0.0-beta.18`.
-
## 3.0.0-beta.18 - 2022-01-04
+
- Minimum supported Rust version (MSRV) is now 1.54.
-
## 3.0.0-beta.17 - 2021-12-29
+
### Changed
+
- Update `cookie` dependency (re-exported) to `0.16`. [#2555]
### Security
+
- `cookie` upgrade addresses [`RUSTSEC-2020-0071`].
[#2555]: https://github.com/actix/actix-web/pull/2555
-[`RUSTSEC-2020-0071`]: https://rustsec.org/advisories/RUSTSEC-2020-0071.html
-
+[`rustsec-2020-0071`]: https://rustsec.org/advisories/RUSTSEC-2020-0071.html
## 3.0.0-beta.16 - 2021-12-29
+
- `*::send_json` and `*::send_form` methods now receive `impl Serialize`. [#2553]
- `FrozenClientRequest::extra_header` now uses receives an `impl TryIntoHeaderPair`. [#2553]
- Remove unnecessary `Unpin` bounds on `*::send_stream`. [#2553]
[#2553]: https://github.com/actix/actix-web/pull/2553
-
## 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]
@@ -159,89 +178,96 @@
[#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
-
## 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
+
- Update `actix-tls` to `3.0.0-rc.1`. [#2474]
[#2474]: https://github.com/actix/actix-web/pull/2474
-
## 3.0.0-beta.11 - 2021-11-22
+
- No significant changes from `3.0.0-beta.10`.
-
## 3.0.0-beta.10 - 2021-11-15
+
- No significant changes from `3.0.0-beta.9`.
-
## 3.0.0-beta.9 - 2021-10-20
+
- Updated rustls to v0.20. [#2414]
[#2414]: https://github.com/actix/actix-web/pull/2414
-
## 3.0.0-beta.8 - 2021-09-09
+
### Changed
+
- Send headers within the redirect requests. [#2310]
[#2310]: https://github.com/actix/actix-web/pull/2310
-
## 3.0.0-beta.7 - 2021-06-26
+
### Changed
+
- Change compression algorithm features flags. [#2250]
[#2250]: https://github.com/actix/actix-web/pull/2250
-
## 3.0.0-beta.6 - 2021-06-17
+
- No significant changes since 3.0.0-beta.5.
-
## 3.0.0-beta.5 - 2021-04-17
+
### Removed
+
- Deprecated methods on `ClientRequest`: `if_true`, `if_some`. [#2148]
[#2148]: https://github.com/actix/actix-web/pull/2148
-
## 3.0.0-beta.4 - 2021-04-02
+
### Added
+
- Add `Client::headers` to get default mut reference of `HeaderMap` of client object. [#2114]
### Changed
+
- `ConnectorService` type is renamed to `BoxConnectorService`. [#2081]
- Fix http/https encoding when enabling `compress` feature. [#2116]
-- Rename `TestResponse::header` to `append_header`, `set` to `insert_header`. `TestResponse` header
- methods now take `TryIntoHeaderPair` tuples. [#2094]
+- Rename `TestResponse::header` to `append_header`, `set` to `insert_header`. `TestResponse` header methods now take `TryIntoHeaderPair` tuples. [#2094]
[#2081]: https://github.com/actix/actix-web/pull/2081
[#2094]: https://github.com/actix/actix-web/pull/2094
[#2114]: https://github.com/actix/actix-web/pull/2114
[#2116]: https://github.com/actix/actix-web/pull/2116
-
## 3.0.0-beta.3 - 2021-03-08
+
### Added
+
- `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]
### Changed
+
- Feature `cookies` is now optional and enabled by default. [#1981]
- `ClientBuilder::connector` method would take `actix_http::client::Connector` type. [#2008]
- Basic auth password now takes blank passwords as an empty string instead of Option. [#2050]
### Removed
+
- `ClientBuilder::default` function [#2008]
[#1931]: https://github.com/actix/actix-web/pull/1931
@@ -250,17 +276,20 @@
[#2024]: https://github.com/actix/actix-web/pull/2024
[#2050]: https://github.com/actix/actix-web/pull/2050
-
## 3.0.0-beta.2 - 2021-02-10
+
### Added
+
- `ClientRequest::insert_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]
### Changed
+
- Relax default timeout for `Connector` to 5 seconds(original 1 second). [#1905]
### Removed
+
- `ClientRequest::set`; 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]
@@ -270,9 +299,10 @@
[#1905]: https://github.com/actix/actix-web/pull/1905
[#1969]: https://github.com/actix/actix-web/pull/1969
-
## 3.0.0-beta.1 - 2021-01-07
+
### Changed
+
- Update `rand` to `0.8`
- Update `bytes` to `1.0`. [#1813]
- Update `rust-tls` to `0.19`. [#1813]
@@ -282,53 +312,62 @@
## 2.0.3 - 2020-11-29
+
### Fixed
+
- Ensure `actix-http` dependency uses same `serde_urlencoded`.
-
## 2.0.2 - 2020-11-25
+
### Changed
+
- Upgrade `serde_urlencoded` to `0.7`. [#1773]
[#1773]: https://github.com/actix/actix-web/pull/1773
-
## 2.0.1 - 2020-10-30
+
### Changed
+
- Upgrade `base64` to `0.13`. [#1744]
- Deprecate `ClientRequest::{if_some, if_true}`. [#1760]
### Fixed
-- Use `Accept-Encoding: identity` instead of `Accept-Encoding: br` when no compression feature
- is enabled [#1737]
+
+- Use `Accept-Encoding: identity` instead of `Accept-Encoding: br` when no compression feature is enabled [#1737]
[#1737]: https://github.com/actix/actix-web/pull/1737
[#1760]: https://github.com/actix/actix-web/pull/1760
[#1744]: https://github.com/actix/actix-web/pull/1744
-
## 2.0.0 - 2020-09-11
+
### Changed
+
- `Client::build` was renamed to `Client::builder`.
-
## 2.0.0-beta.4 - 2020-09-09
+
### Changed
+
- Update actix-codec & actix-tls dependencies.
-
## 2.0.0-beta.3 - 2020-08-17
+
### Changed
+
- Update `rustls` to 0.18
-
## 2.0.0-beta.2 - 2020-07-21
+
### Changed
+
- Update `actix-http` dependency to 2.0.0-beta.2
-
## [2.0.0-beta.1] - 2020-07-14
+
### Changed
+
- Update `actix-http` dependency to 2.0.0-beta.1
## [2.0.0-alpha.2] - 2020-05-21
@@ -360,26 +399,22 @@
- Migrate to `std::future`
-
## [0.2.8] - 2019-11-06
- Add support for setting query from Serialize type for client request.
-
## [0.2.7] - 2019-09-25
### Added
- Remaining getter methods for `ClientRequest`'s private `head` field #1101
-
## [0.2.6] - 2019-09-12
### Added
- Export frozen request related types.
-
## [0.2.5] - 2019-09-11
### Added
@@ -390,7 +425,6 @@
- Ensure that the `Host` header is set when initiating a WebSocket client connection.
-
## [0.2.4] - 2019-08-13
### Changed
@@ -399,14 +433,12 @@
- Update serde_urlencoded to "0.6.1"
-
## [0.2.3] - 2019-08-01
### Added
- Add `rustls` support
-
## [0.2.2] - 2019-07-01
### Changed
@@ -415,7 +447,6 @@
- Upgrade `rand` dependency version to 0.7
-
## [0.2.1] - 2019-06-05
### Added
@@ -432,7 +463,6 @@
- Upgrade actix-http dependency.
-
## [0.1.1] - 2019-04-19
### Added
@@ -443,19 +473,16 @@
- `ClientRequest::if_true()` and `ClientRequest::if_some()` use instance instead of ref
-
## [0.1.0] - 2019-04-16
- No changes
-
## [0.1.0-alpha.6] - 2019-04-14
### Changed
- Do not set default headers for websocket request
-
## [0.1.0-alpha.5] - 2019-04-12
### Changed
@@ -466,14 +493,12 @@
- Add Debug impl for BoxedSocket
-
## [0.1.0-alpha.4] - 2019-04-08
### Changed
- Update actix-http dependency
-
## [0.1.0-alpha.3] - 2019-04-02
### Added
@@ -482,7 +507,6 @@
- `ClientResponse::json()` - Loads and parse `application/json` encoded body
-
### Changed
- `ClientRequest::json()` accepts reference instead of object.
@@ -491,7 +515,6 @@
- Renamed `ClientRequest::close_connection()` to `ClientRequest::force_close()`
-
## [0.1.0-alpha.2] - 2019-03-29
### Added
@@ -504,14 +527,12 @@
- Re-export `actix_http::client::Connector`.
-
### Changed
- Allow to override request's uri
- Export `ws` sub-module with websockets related types
-
## [0.1.0-alpha.1] - 2019-03-28
- Initial impl
diff --git a/awc/Cargo.toml b/awc/Cargo.toml
index cf64eed49..8a75e28f7 100644
--- a/awc/Cargo.toml
+++ b/awc/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "awc"
-version = "3.0.1"
+version = "3.1.1"
authors = ["Nikolay Kim "]
description = "Async HTTP and WebSocket client library"
keywords = ["actix", "http", "framework", "async", "web"]
@@ -57,13 +57,12 @@ dangerous-h2c = []
[dependencies]
actix-codec = "0.5"
actix-service = "2"
-actix-http = { version = "3", features = ["http2", "ws"] }
+actix-http = { version = "3.3", features = ["http2", "ws"] }
actix-rt = { version = "2.1", default-features = false }
actix-tls = { version = "3", features = ["connect", "uri"] }
actix-utils = "3"
-ahash = "0.7"
-base64 = "0.13"
+base64 = "0.21"
bytes = "1"
cfg-if = "1"
derive_more = "0.99.5"
@@ -80,7 +79,7 @@ rand = "0.8"
serde = "1.0"
serde_json = "1.0"
serde_urlencoded = "0.7"
-tokio = { version = "1.8.4", features = ["sync"] }
+tokio = { version = "1.24.2", features = ["sync"] }
cookie = { version = "0.16", features = ["percent-encode"], optional = true }
@@ -99,14 +98,14 @@ actix-utils = "3"
actix-web = { version = "4", features = ["openssl"] }
brotli = "3.3.3"
-const-str = "0.4"
+const-str = "0.3"
env_logger = "0.9"
flate2 = "1.0.13"
futures-util = { version = "0.3.17", default-features = false }
static_assertions = "1.1"
rcgen = "0.9"
rustls-pemfile = "1"
-tokio = { version = "1.13.1", features = ["rt-multi-thread", "macros"] }
+tokio = { version = "1.24.2", features = ["rt-multi-thread", "macros"] }
zstd = "0.12"
[[example]]
diff --git a/awc/README.md b/awc/README.md
index 9f47e663b..a9d411067 100644
--- a/awc/README.md
+++ b/awc/README.md
@@ -3,16 +3,16 @@
> Async HTTP and WebSocket client library.
[](https://crates.io/crates/awc)
-[](https://docs.rs/awc/3.0.1)
+[](https://docs.rs/awc/3.1.1)

-[](https://deps.rs/crate/awc/3.0.1)
+[](https://deps.rs/crate/awc/3.1.1)
[](https://discord.gg/NWpN5mmg3x)
## Documentation & Resources
- [API Documentation](https://docs.rs/awc)
- [Example Project](https://github.com/actix/examples/tree/master/https-tls/awc-https)
-- Minimum Supported Rust Version (MSRV): 1.54
+- Minimum Supported Rust Version (MSRV): 1.59
## Example
diff --git a/awc/src/builder.rs b/awc/src/builder.rs
index 34a5f8505..79838a3f6 100644
--- a/awc/src/builder.rs
+++ b/awc/src/builder.rs
@@ -1,5 +1,7 @@
use std::{convert::TryFrom, fmt, net::IpAddr, rc::Rc, time::Duration};
+use base64::prelude::*;
+
use actix_http::{
error::HttpError,
header::{self, HeaderMap, HeaderName, TryIntoHeaderPair},
@@ -210,7 +212,7 @@ where
};
self.add_default_header((
header::AUTHORIZATION,
- format!("Basic {}", base64::encode(auth)),
+ format!("Basic {}", BASE64_STANDARD.encode(auth)),
))
}
diff --git a/awc/src/client/pool.rs b/awc/src/client/pool.rs
index 47c1fdd67..632608c45 100644
--- a/awc/src/client/pool.rs
+++ b/awc/src/client/pool.rs
@@ -2,7 +2,7 @@
use std::{
cell::RefCell,
- collections::VecDeque,
+ collections::{HashMap, VecDeque},
future::Future,
io,
ops::Deref,
@@ -17,7 +17,6 @@ use actix_codec::{AsyncRead, AsyncWrite, ReadBuf};
use actix_http::Protocol;
use actix_rt::time::{sleep, Sleep};
use actix_service::Service;
-use ahash::AHashMap;
use futures_core::future::LocalBoxFuture;
use futures_util::FutureExt as _;
use http::uri::Authority;
@@ -62,7 +61,7 @@ where
{
fn new(config: ConnectorConfig) -> Self {
let permits = Arc::new(Semaphore::new(config.limit));
- let available = RefCell::new(AHashMap::default());
+ let available = RefCell::new(HashMap::default());
Self(Rc::new(ConnectionPoolInnerPriv {
config,
@@ -124,7 +123,7 @@ where
Io: AsyncWrite + Unpin + 'static,
{
config: ConnectorConfig,
- available: RefCell>>>,
+ available: RefCell>>>,
permits: Arc,
}
diff --git a/awc/src/lib.rs b/awc/src/lib.rs
index bb7f06c93..42f029669 100644
--- a/awc/src/lib.rs
+++ b/awc/src/lib.rs
@@ -139,7 +139,7 @@ pub mod http {
}
pub use self::builder::ClientBuilder;
-pub use self::client::{Client, Connector};
+pub use self::client::{Client, Connect, Connector};
pub use self::connect::{BoxConnectorService, BoxedSocket, ConnectRequest, ConnectResponse};
pub use self::frozen::{FrozenClientRequest, FrozenSendBuilder};
pub use self::request::ClientRequest;
diff --git a/awc/src/request.rs b/awc/src/request.rs
index 331c80af7..d3a4eda8c 100644
--- a/awc/src/request.rs
+++ b/awc/src/request.rs
@@ -1,5 +1,6 @@
use std::{convert::TryFrom, fmt, net, rc::Rc, time::Duration};
+use base64::prelude::*;
use bytes::Bytes;
use futures_core::Stream;
use serde::Serialize;
@@ -238,7 +239,7 @@ impl ClientRequest {
self.insert_header((
header::AUTHORIZATION,
- format!("Basic {}", base64::encode(auth)),
+ format!("Basic {}", BASE64_STANDARD.encode(auth)),
))
}
diff --git a/awc/src/ws.rs b/awc/src/ws.rs
index f905b8ef2..406368e62 100644
--- a/awc/src/ws.rs
+++ b/awc/src/ws.rs
@@ -28,6 +28,8 @@
use std::{convert::TryFrom, fmt, net::SocketAddr, str};
+use base64::prelude::*;
+
use actix_codec::Framed;
use actix_http::{ws, Payload, RequestHead};
use actix_rt::time::timeout;
@@ -236,7 +238,10 @@ impl WebsocketsRequest {
Some(password) => format!("{}:{}", username, password),
None => format!("{}:", username),
};
- self.header(AUTHORIZATION, format!("Basic {}", base64::encode(auth)))
+ self.header(
+ AUTHORIZATION,
+ format!("Basic {}", BASE64_STANDARD.encode(auth)),
+ )
}
/// Set HTTP bearer authentication header
@@ -321,7 +326,7 @@ impl WebsocketsRequest {
// Generate a random key for the `Sec-WebSocket-Key` header which is a base64-encoded
// (see RFC 4648 §4) value that, when decoded, is 16 bytes in length (RFC 6455 §1.3).
let sec_key: [u8; 16] = rand::random();
- let key = base64::encode(sec_key);
+ let key = BASE64_STANDARD.encode(sec_key);
self.head.headers.insert(
header::SEC_WEBSOCKET_KEY,
diff --git a/awc/tests/test_client.rs b/awc/tests/test_client.rs
index 0949595cb..9c3543ff0 100644
--- a/awc/tests/test_client.rs
+++ b/awc/tests/test_client.rs
@@ -13,6 +13,7 @@ use std::{
};
use actix_utils::future::ok;
+use base64::prelude::*;
use bytes::Bytes;
use cookie::Cookie;
use futures_util::stream;
@@ -783,7 +784,7 @@ async fn client_basic_auth() {
.unwrap()
.to_str()
.unwrap()
- == format!("Basic {}", base64::encode("username:password"))
+ == format!("Basic {}", BASE64_STANDARD.encode("username:password"))
{
HttpResponse::Ok()
} else {
diff --git a/scripts/bump b/scripts/bump
index 33ea52010..40d43d429 100755
--- a/scripts/bump
+++ b/scripts/bump
@@ -51,7 +51,6 @@ cat "$CHANGELOG_FILE" |
if [ "$(wc -w "$CHANGE_CHUNK_FILE" | awk '{ print $1 }')" = "0" ]; then
echo "- No significant changes since \`$CURRENT_VERSION\`." >"$CHANGE_CHUNK_FILE"
echo >>"$CHANGE_CHUNK_FILE"
- echo >>"$CHANGE_CHUNK_FILE"
fi
if [ -n "${2-}" ]; then
@@ -82,7 +81,6 @@ sed -i.bak -E "s/^version ?= ?\"[^\"]+\"$/version = \"$NEW_VERSION\"/" "$CARGO_M
(
sed '/Unreleased/ q' "$CHANGELOG_FILE" # up to unreleased heading
echo # blank line
- echo # blank line
echo "## $NEW_VERSION - $DATE" # new version heading
cat "$CHANGE_CHUNK_FILE" # previously unreleased changes
sed "/$CURRENT_VERSION/ q" "$CHANGELOG_FILE" | tail -n 1 # the previous version heading
@@ -90,6 +88,9 @@ sed -i.bak -E "s/^version ?= ?\"[^\"]+\"$/version = \"$NEW_VERSION\"/" "$CARGO_M
) >"$CHANGELOG_FILE.bak"
mv "$CHANGELOG_FILE.bak" "$CHANGELOG_FILE"
+# format CHANGELOG file according to prettier
+npx -y prettier --write "$CHANGELOG_FILE" || true
+
# done; remove backup files
rm -f $CARGO_MANIFEST.bak
rm -f $CHANGELOG_FILE.bak
@@ -139,12 +140,14 @@ GIT_TAG="$(echo $SHORT_PACKAGE_NAME-v$NEW_VERSION)"
RELEASE_TITLE="$(echo $PACKAGE_NAME: v$NEW_VERSION)"
if [ "$(echo $NEW_VERSION | grep beta)" ] || [ "$(echo $NEW_VERSION | grep rc)" ] || [ "$(echo $NEW_VERSION | grep alpha)" ]; then
- PRERELEASE="--prerelease"
+ FLAGS="--prerelease"
+else
+ FLAGS="--latest"
fi
echo
echo "GitHub release command:"
-GH_CMD="gh release create \"$GIT_TAG\" --draft --title \"$RELEASE_TITLE\" --notes-file \"$CHANGE_CHUNK_FILE\" ${PRERELEASE:-}"
+GH_CMD="gh release create \"$GIT_TAG\" --draft --title \"$RELEASE_TITLE\" --notes-file \"$CHANGE_CHUNK_FILE\" ${FLAGS:-}"
echo "$GH_CMD"
read -p "Submit draft GH release: (y/N) " GH_RELEASE