From e72ee2823261c4a08dfd4133963ae6161b6f7ff9 Mon Sep 17 00:00:00 2001
From: Andrew
Date: Wed, 17 Jun 2020 00:58:23 -0700
Subject: [PATCH 01/78] Enforce HW_BUFFER_SIZE inside h1::dispatcher (#1550)
---
CHANGES.md | 6 ++++++
actix-http/src/h1/dispatcher.rs | 7 +++++++
2 files changed, 13 insertions(+)
diff --git a/CHANGES.md b/CHANGES.md
index 739f1b13c..3b4b8dc0b 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,5 +1,11 @@
# Changes
+## [Unreleased]
+
+### Changed
+
+* Fix actix_http::h1::dispatcher so it returns when HW_BUFFER_SIZE is reached. Should reduce peak memory consumption during large uploads. [#1550]
+
## [3.0.0-alpha.3] - 2020-05-21
### Added
diff --git a/actix-http/src/h1/dispatcher.rs b/actix-http/src/h1/dispatcher.rs
index e16d3536f..51f107efb 100644
--- a/actix-http/src/h1/dispatcher.rs
+++ b/actix-http/src/h1/dispatcher.rs
@@ -861,7 +861,14 @@ where
T: AsyncRead + Unpin,
{
let mut read_some = false;
+
loop {
+ // If buf is full return but do not disconnect since
+ // there is more reading to be done
+ if buf.len() >= HW_BUFFER_SIZE {
+ return Ok(Some(false));
+ }
+
let remaining = buf.capacity() - buf.len();
if remaining < LW_BUFFER_SIZE {
buf.reserve(HW_BUFFER_SIZE - remaining);
From 9af07d66ae092a0efe90829951f22aa948bc761f Mon Sep 17 00:00:00 2001
From: Fabianstelmach
Date: Wed, 17 Jun 2020 11:54:20 +0200
Subject: [PATCH 02/78] Fix NormalizePath trailing slash behavior (#1548)
---
CHANGES.md | 4 ++++
src/middleware/normalize.rs | 31 ++++++++++++++++++++++++++-----
2 files changed, 30 insertions(+), 5 deletions(-)
diff --git a/CHANGES.md b/CHANGES.md
index 3b4b8dc0b..3c21c301d 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -6,6 +6,10 @@
* Fix actix_http::h1::dispatcher so it returns when HW_BUFFER_SIZE is reached. Should reduce peak memory consumption during large uploads. [#1550]
+### Fixed
+
+* `NormalizePath` improved consistency when path needs slashes added _and_ removed.
+
## [3.0.0-alpha.3] - 2020-05-21
### Added
diff --git a/src/middleware/normalize.rs b/src/middleware/normalize.rs
index d23f03445..3d5d30c80 100644
--- a/src/middleware/normalize.rs
+++ b/src/middleware/normalize.rs
@@ -16,6 +16,7 @@ use crate::Error;
/// Performs following:
///
/// - Merges multiple slashes into one.
+/// - Appends a trailing slash if one is not present.
///
/// ```rust
/// use actix_web::{web, http, middleware, App, HttpResponse};
@@ -75,14 +76,26 @@ where
fn call(&mut self, mut req: ServiceRequest) -> Self::Future {
let head = req.head_mut();
+ let original_path = head.uri.path();
+
// always add trailing slash, might be an extra one
- let path = head.uri.path().to_string() + "/";
- let original_len = path.len();
+ let path = original_path.to_string() + "/";
// normalize multiple /'s to one /
let path = self.merge_slash.replace_all(&path, "/");
- if original_len != path.len() {
+ // Check whether the path has been changed
+ //
+ // This check was previously implemented as string length comparison
+ //
+ // That approach fails when a trailing slash is added,
+ // and a duplicate slash is removed,
+ // since the length of the strings remains the same
+ //
+ // For example, the path "/v1//s" will be normalized to "/v1/s/"
+ // Both of the paths have the same length,
+ // so the change can not be deduced from the length comparison
+ if path != original_path {
let mut parts = head.uri.clone().into_parts();
let pq = parts.path_and_query.as_ref().unwrap();
@@ -131,6 +144,10 @@ mod tests {
let req3 = TestRequest::with_uri("//v1//////something").to_request();
let res3 = call_service(&mut app, req3).await;
assert!(res3.status().is_success());
+
+ let req4 = TestRequest::with_uri("/v1//something").to_request();
+ let res4 = call_service(&mut app, req4).await;
+ assert!(res4.status().is_success());
}
#[actix_rt::test]
@@ -156,6 +173,10 @@ mod tests {
let req3 = TestRequest::with_uri("//v1///something").to_srv_request();
let res3 = normalize.call(req3).await.unwrap();
assert!(res3.status().is_success());
+
+ let req4 = TestRequest::with_uri("/v1//something").to_srv_request();
+ let res4 = normalize.call(req4).await.unwrap();
+ assert!(res4.status().is_success());
}
#[actix_rt::test]
@@ -178,11 +199,11 @@ mod tests {
}
#[actix_rt::test]
- async fn should_normalize_nothing_notrail() {
+ async fn should_normalize_notrail() {
const URI: &str = "/v1/something";
let srv = |req: ServiceRequest| {
- assert_eq!(URI, req.path());
+ assert_eq!(URI.to_string() + "/", req.path());
ok(req.into_response(HttpResponse::Ok().finish()))
};
From 0ba73fc44c7771975f761c5e2704d95ac5b18b10 Mon Sep 17 00:00:00 2001
From: Rob Ede
Date: Thu, 18 Jun 2020 11:23:36 +0100
Subject: [PATCH 03/78] exclude default -web features on -actors (#1562)
---
actix-web-actors/Cargo.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/actix-web-actors/Cargo.toml b/actix-web-actors/Cargo.toml
index 8db7a35ef..746aca2d5 100644
--- a/actix-web-actors/Cargo.toml
+++ b/actix-web-actors/Cargo.toml
@@ -17,7 +17,7 @@ path = "src/lib.rs"
[dependencies]
actix = "0.10.0-alpha.2"
-actix-web = "3.0.0-alpha.3"
+actix-web = { version = "3.0.0-alpha.3", default-features = false }
actix-http = "2.0.0-alpha.4"
actix-codec = "0.2.0"
bytes = "0.5.2"
From dc74db1f2f6a0686410633531960807836bb38b6 Mon Sep 17 00:00:00 2001
From: Rob Ede
Date: Thu, 18 Jun 2020 15:45:30 +0100
Subject: [PATCH 04/78] re-export actix_rt::main macro (#1559)
---
CHANGES.md | 4 ++++
Cargo.toml | 2 +-
README.md | 3 +--
examples/basic.rs | 2 +-
examples/client.rs | 2 +-
examples/uds.rs | 2 +-
src/lib.rs | 1 +
7 files changed, 10 insertions(+), 6 deletions(-)
diff --git a/CHANGES.md b/CHANGES.md
index 3c21c301d..c2dd43973 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -2,6 +2,10 @@
## [Unreleased]
+### Added
+
+* Re-export `actix_rt::main` as `actix_web::main`.
+
### Changed
* Fix actix_http::h1::dispatcher so it returns when HW_BUFFER_SIZE is reached. Should reduce peak memory consumption during large uploads. [#1550]
diff --git a/Cargo.toml b/Cargo.toml
index 8c6d461d6..ade432c6d 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -69,7 +69,7 @@ actix-codec = "0.2.0"
actix-service = "1.0.2"
actix-utils = "1.0.6"
actix-router = "0.2.4"
-actix-rt = "1.0.0"
+actix-rt = "1.1.1"
actix-server = "1.0.0"
actix-testing = "1.0.0"
actix-macros = "0.1.0"
diff --git a/README.md b/README.md
index 97e3ceeae..6382abd4d 100644
--- a/README.md
+++ b/README.md
@@ -53,7 +53,6 @@ Dependencies:
```toml
[dependencies]
actix-web = "2"
-actix-rt = "1"
```
Code:
@@ -66,7 +65,7 @@ async fn index(info: web::Path<(u32, String)>) -> impl Responder {
format!("Hello {}! id:{}", info.1, info.0)
}
-#[actix_rt::main]
+#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| App::new().service(index))
.bind("127.0.0.1:8080")?
diff --git a/examples/basic.rs b/examples/basic.rs
index bd6f8146f..8b2bf2319 100644
--- a/examples/basic.rs
+++ b/examples/basic.rs
@@ -16,7 +16,7 @@ async fn no_params() -> &'static str {
"Hello world!\r\n"
}
-#[actix_rt::main]
+#[actix_web::main]
async fn main() -> std::io::Result<()> {
std::env::set_var("RUST_LOG", "actix_server=info,actix_web=info");
env_logger::init();
diff --git a/examples/client.rs b/examples/client.rs
index 874e08e1b..15cf24fa8 100644
--- a/examples/client.rs
+++ b/examples/client.rs
@@ -1,6 +1,6 @@
use actix_http::Error;
-#[actix_rt::main]
+#[actix_web::main]
async fn main() -> Result<(), Error> {
std::env::set_var("RUST_LOG", "actix_http=trace");
env_logger::init();
diff --git a/examples/uds.rs b/examples/uds.rs
index 06aa87f4d..e34fa5ac9 100644
--- a/examples/uds.rs
+++ b/examples/uds.rs
@@ -20,7 +20,7 @@ async fn no_params() -> &'static str {
}
#[cfg(unix)]
-#[actix_rt::main]
+#[actix_web::main]
async fn main() -> std::io::Result<()> {
std::env::set_var("RUST_LOG", "actix_server=info,actix_web=info");
env_logger::init();
diff --git a/src/lib.rs b/src/lib.rs
index cff4acf27..09642806f 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -112,6 +112,7 @@ pub use actix_web_codegen::*;
// re-export for convenience
pub use actix_http::Response as HttpResponse;
pub use actix_http::{body, cookie, http, Error, HttpMessage, ResponseError, Result};
+pub use actix_macros::{main, test as test_rt};
pub use crate::app::App;
pub use crate::extract::FromRequest;
From eb0eda69c6c986475e927bf707c8dd86bda4d70e Mon Sep 17 00:00:00 2001
From: Rob Ede
Date: Fri, 19 Jun 2020 14:34:14 +0100
Subject: [PATCH 05/78] migrate cookie handling to cookie crate (#1558)
---
CHANGES.md | 1 +
Cargo.toml | 2 +-
actix-http/CHANGES.md | 6 +
actix-http/Cargo.toml | 6 +-
actix-http/src/cookie/builder.rs | 252 ------
actix-http/src/cookie/delta.rs | 71 --
actix-http/src/cookie/draft.rs | 106 ---
actix-http/src/cookie/jar.rs | 651 --------------
actix-http/src/cookie/mod.rs | 1100 -----------------------
actix-http/src/cookie/parse.rs | 467 ----------
actix-http/src/cookie/secure/key.rs | 190 ----
actix-http/src/cookie/secure/macros.rs | 40 -
actix-http/src/cookie/secure/mod.rs | 10 -
actix-http/src/cookie/secure/private.rs | 275 ------
actix-http/src/cookie/secure/signed.rs | 184 ----
actix-http/src/lib.rs | 2 +-
actix-http/src/response.rs | 2 +-
actix-http/src/test.rs | 24 +-
awc/src/request.rs | 24 +-
awc/src/test.rs | 14 +-
awc/src/ws.rs | 23 +-
21 files changed, 49 insertions(+), 3401 deletions(-)
delete mode 100644 actix-http/src/cookie/builder.rs
delete mode 100644 actix-http/src/cookie/delta.rs
delete mode 100644 actix-http/src/cookie/draft.rs
delete mode 100644 actix-http/src/cookie/jar.rs
delete mode 100644 actix-http/src/cookie/mod.rs
delete mode 100644 actix-http/src/cookie/parse.rs
delete mode 100644 actix-http/src/cookie/secure/key.rs
delete mode 100644 actix-http/src/cookie/secure/macros.rs
delete mode 100644 actix-http/src/cookie/secure/mod.rs
delete mode 100644 actix-http/src/cookie/secure/private.rs
delete mode 100644 actix-http/src/cookie/secure/signed.rs
diff --git a/CHANGES.md b/CHANGES.md
index c2dd43973..58cddf78d 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -9,6 +9,7 @@
### Changed
* Fix actix_http::h1::dispatcher so it returns when HW_BUFFER_SIZE is reached. Should reduce peak memory consumption during large uploads. [#1550]
+* Migrate cookie handling to `cookie` crate. Actix-web no longer requires `ring` dependency.
### Fixed
diff --git a/Cargo.toml b/Cargo.toml
index ade432c6d..de7222f1f 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -43,7 +43,7 @@ default = ["compress"]
# content-encoding support
compress = ["actix-http/compress", "awc/compress"]
-# sessions feature, session require "ring" crate and c compiler
+# sessions feature
secure-cookies = ["actix-http/secure-cookies"]
# openssl
diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md
index c19e40e4c..49599b9be 100644
--- a/actix-http/CHANGES.md
+++ b/actix-http/CHANGES.md
@@ -1,5 +1,11 @@
# Changes
+## [Unreleased]
+
+### Changed
+
+* Migrate cookie handling to `cookie` crate.
+
## [2.0.0-alpha.4] - 2020-05-21
### Changed
diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml
index d2ae7698e..8b9b8c825 100644
--- a/actix-http/Cargo.toml
+++ b/actix-http/Cargo.toml
@@ -34,7 +34,7 @@ rustls = ["actix-tls/rustls", "actix-connect/rustls"]
compress = ["flate2", "brotli2"]
# support for secure cookies
-secure-cookies = ["ring"]
+secure-cookies = ["cookie/secure"]
# support for actix Actor messages
actors = ["actix"]
@@ -52,6 +52,7 @@ actix = { version = "0.10.0-alpha.1", optional = true }
base64 = "0.12"
bitflags = "1.2"
bytes = "0.5.3"
+cookie = { version = "0.14.1", features = ["percent-encode"] }
copyless = "0.1.4"
derive_more = "0.99.2"
either = "1.5.3"
@@ -80,9 +81,6 @@ slab = "0.4"
serde_urlencoded = "0.6.1"
time = { version = "0.2.7", default-features = false, features = ["std"] }
-# for secure cookie
-ring = { version = "0.16.9", optional = true }
-
# compression
brotli2 = { version="0.3.2", optional = true }
flate2 = { version = "1.0.13", optional = true }
diff --git a/actix-http/src/cookie/builder.rs b/actix-http/src/cookie/builder.rs
deleted file mode 100644
index b64352e35..000000000
--- a/actix-http/src/cookie/builder.rs
+++ /dev/null
@@ -1,252 +0,0 @@
-use std::borrow::Cow;
-
-use time::{Duration, OffsetDateTime};
-
-use super::{Cookie, SameSite};
-
-/// Structure that follows the builder pattern for building `Cookie` structs.
-///
-/// To construct a cookie:
-///
-/// 1. Call [`Cookie::build`](struct.Cookie.html#method.build) to start building.
-/// 2. Use any of the builder methods to set fields in the cookie.
-/// 3. Call [finish](#method.finish) to retrieve the built cookie.
-///
-/// # Example
-///
-/// ```rust
-/// use actix_http::cookie::Cookie;
-///
-/// let cookie: Cookie = Cookie::build("name", "value")
-/// .domain("www.rust-lang.org")
-/// .path("/")
-/// .secure(true)
-/// .http_only(true)
-/// .max_age(84600)
-/// .finish();
-/// ```
-#[derive(Debug, Clone)]
-pub struct CookieBuilder {
- /// The cookie being built.
- cookie: Cookie<'static>,
-}
-
-impl CookieBuilder {
- /// Creates a new `CookieBuilder` instance from the given name and value.
- ///
- /// This method is typically called indirectly via
- /// [Cookie::build](struct.Cookie.html#method.build).
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::Cookie;
- ///
- /// let c = Cookie::build("foo", "bar").finish();
- /// assert_eq!(c.name_value(), ("foo", "bar"));
- /// ```
- pub fn new(name: N, value: V) -> CookieBuilder
- where
- N: Into>,
- V: Into>,
- {
- CookieBuilder {
- cookie: Cookie::new(name, value),
- }
- }
-
- /// Sets the `expires` field in the cookie being built.
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::Cookie;
- ///
- /// let c = Cookie::build("foo", "bar")
- /// .expires(time::OffsetDateTime::now_utc())
- /// .finish();
- ///
- /// assert!(c.expires().is_some());
- /// ```
- #[inline]
- pub fn expires(mut self, when: OffsetDateTime) -> CookieBuilder {
- self.cookie.set_expires(when);
- self
- }
-
- /// Sets the `max_age` field in seconds in the cookie being built.
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::Cookie;
- ///
- /// let c = Cookie::build("foo", "bar")
- /// .max_age(1800)
- /// .finish();
- ///
- /// assert_eq!(c.max_age(), Some(time::Duration::seconds(30 * 60)));
- /// ```
- #[inline]
- pub fn max_age(self, seconds: i64) -> CookieBuilder {
- self.max_age_time(Duration::seconds(seconds))
- }
-
- /// Sets the `max_age` field in the cookie being built.
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::Cookie;
- ///
- /// let c = Cookie::build("foo", "bar")
- /// .max_age_time(time::Duration::minutes(30))
- /// .finish();
- ///
- /// assert_eq!(c.max_age(), Some(time::Duration::seconds(30 * 60)));
- /// ```
- #[inline]
- pub fn max_age_time(mut self, value: Duration) -> CookieBuilder {
- // Truncate any nanoseconds from the Duration, as they aren't represented within `Max-Age`
- // and would cause two otherwise identical `Cookie` instances to not be equivalent to one another.
- self.cookie
- .set_max_age(Duration::seconds(value.whole_seconds()));
- self
- }
-
- /// Sets the `domain` field in the cookie being built.
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::Cookie;
- ///
- /// let c = Cookie::build("foo", "bar")
- /// .domain("www.rust-lang.org")
- /// .finish();
- ///
- /// assert_eq!(c.domain(), Some("www.rust-lang.org"));
- /// ```
- pub fn domain>>(mut self, value: D) -> CookieBuilder {
- self.cookie.set_domain(value);
- self
- }
-
- /// Sets the `path` field in the cookie being built.
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::Cookie;
- ///
- /// let c = Cookie::build("foo", "bar")
- /// .path("/")
- /// .finish();
- ///
- /// assert_eq!(c.path(), Some("/"));
- /// ```
- pub fn path>>(mut self, path: P) -> CookieBuilder {
- self.cookie.set_path(path);
- self
- }
-
- /// Sets the `secure` field in the cookie being built.
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::Cookie;
- ///
- /// let c = Cookie::build("foo", "bar")
- /// .secure(true)
- /// .finish();
- ///
- /// assert_eq!(c.secure(), Some(true));
- /// ```
- #[inline]
- pub fn secure(mut self, value: bool) -> CookieBuilder {
- self.cookie.set_secure(value);
- self
- }
-
- /// Sets the `http_only` field in the cookie being built.
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::Cookie;
- ///
- /// let c = Cookie::build("foo", "bar")
- /// .http_only(true)
- /// .finish();
- ///
- /// assert_eq!(c.http_only(), Some(true));
- /// ```
- #[inline]
- pub fn http_only(mut self, value: bool) -> CookieBuilder {
- self.cookie.set_http_only(value);
- self
- }
-
- /// Sets the `same_site` field in the cookie being built.
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::{Cookie, SameSite};
- ///
- /// let c = Cookie::build("foo", "bar")
- /// .same_site(SameSite::Strict)
- /// .finish();
- ///
- /// assert_eq!(c.same_site(), Some(SameSite::Strict));
- /// ```
- #[inline]
- pub fn same_site(mut self, value: SameSite) -> CookieBuilder {
- self.cookie.set_same_site(value);
- self
- }
-
- /// Makes the cookie being built 'permanent' by extending its expiration and
- /// max age 20 years into the future.
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::Cookie;
- /// use time::Duration;
- ///
- /// let c = Cookie::build("foo", "bar")
- /// .permanent()
- /// .finish();
- ///
- /// assert_eq!(c.max_age(), Some(Duration::days(365 * 20)));
- /// # assert!(c.expires().is_some());
- /// ```
- #[inline]
- pub fn permanent(mut self) -> CookieBuilder {
- self.cookie.make_permanent();
- self
- }
-
- /// Finishes building and returns the built `Cookie`.
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::Cookie;
- ///
- /// let c = Cookie::build("foo", "bar")
- /// .domain("crates.io")
- /// .path("/")
- /// .finish();
- ///
- /// assert_eq!(c.name_value(), ("foo", "bar"));
- /// assert_eq!(c.domain(), Some("crates.io"));
- /// assert_eq!(c.path(), Some("/"));
- /// ```
- #[inline]
- pub fn finish(self) -> Cookie<'static> {
- self.cookie
- }
-}
diff --git a/actix-http/src/cookie/delta.rs b/actix-http/src/cookie/delta.rs
deleted file mode 100644
index a001a5bb8..000000000
--- a/actix-http/src/cookie/delta.rs
+++ /dev/null
@@ -1,71 +0,0 @@
-use std::borrow::Borrow;
-use std::hash::{Hash, Hasher};
-use std::ops::{Deref, DerefMut};
-
-use super::Cookie;
-
-/// A `DeltaCookie` is a helper structure used in a cookie jar. It wraps a
-/// `Cookie` so that it can be hashed and compared purely by name. It further
-/// records whether the wrapped cookie is a "removal" cookie, that is, a cookie
-/// that when sent to the client removes the named cookie on the client's
-/// machine.
-#[derive(Clone, Debug)]
-pub struct DeltaCookie {
- pub cookie: Cookie<'static>,
- pub removed: bool,
-}
-
-impl DeltaCookie {
- /// Create a new `DeltaCookie` that is being added to a jar.
- #[inline]
- pub fn added(cookie: Cookie<'static>) -> DeltaCookie {
- DeltaCookie {
- cookie,
- removed: false,
- }
- }
-
- /// Create a new `DeltaCookie` that is being removed from a jar. The
- /// `cookie` should be a "removal" cookie.
- #[inline]
- pub fn removed(cookie: Cookie<'static>) -> DeltaCookie {
- DeltaCookie {
- cookie,
- removed: true,
- }
- }
-}
-
-impl Deref for DeltaCookie {
- type Target = Cookie<'static>;
-
- fn deref(&self) -> &Cookie<'static> {
- &self.cookie
- }
-}
-
-impl DerefMut for DeltaCookie {
- fn deref_mut(&mut self) -> &mut Cookie<'static> {
- &mut self.cookie
- }
-}
-
-impl PartialEq for DeltaCookie {
- fn eq(&self, other: &DeltaCookie) -> bool {
- self.name() == other.name()
- }
-}
-
-impl Eq for DeltaCookie {}
-
-impl Hash for DeltaCookie {
- fn hash(&self, state: &mut H) {
- self.name().hash(state);
- }
-}
-
-impl Borrow for DeltaCookie {
- fn borrow(&self) -> &str {
- self.name()
- }
-}
diff --git a/actix-http/src/cookie/draft.rs b/actix-http/src/cookie/draft.rs
deleted file mode 100644
index a6525a605..000000000
--- a/actix-http/src/cookie/draft.rs
+++ /dev/null
@@ -1,106 +0,0 @@
-//! This module contains types that represent cookie properties that are not yet
-//! standardized. That is, _draft_ features.
-
-use std::fmt;
-
-/// The `SameSite` cookie attribute.
-///
-/// A cookie with a `SameSite` attribute is imposed restrictions on when it is
-/// sent to the origin server in a cross-site request. If the `SameSite`
-/// attribute is "Strict", then the cookie is never sent in cross-site requests.
-/// If the `SameSite` attribute is "Lax", the cookie is only sent in cross-site
-/// requests with "safe" HTTP methods, i.e, `GET`, `HEAD`, `OPTIONS`, `TRACE`.
-/// If the `SameSite` attribute is not present then the cookie will be sent as
-/// normal. In some browsers, this will implicitly handle the cookie as if "Lax"
-/// and in others, "None". It's best to explicitly set the `SameSite` attribute
-/// to avoid inconsistent behavior.
-///
-/// **Note:** Depending on browser, the `Secure` attribute may be required for
-/// `SameSite` "None" cookies to be accepted.
-///
-/// **Note:** This cookie attribute is an HTTP draft! Its meaning and definition
-/// are subject to change.
-///
-/// More info about these draft changes can be found in the draft spec:
-/// - https://tools.ietf.org/html/draft-west-cookie-incrementalism-00
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub enum SameSite {
- /// The "Strict" `SameSite` attribute.
- Strict,
- /// The "Lax" `SameSite` attribute.
- Lax,
- /// The "None" `SameSite` attribute.
- None,
-}
-
-impl SameSite {
- /// Returns `true` if `self` is `SameSite::Strict` and `false` otherwise.
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::SameSite;
- ///
- /// let strict = SameSite::Strict;
- /// assert!(strict.is_strict());
- /// assert!(!strict.is_lax());
- /// assert!(!strict.is_none());
- /// ```
- #[inline]
- pub fn is_strict(self) -> bool {
- match self {
- SameSite::Strict => true,
- SameSite::Lax | SameSite::None => false,
- }
- }
-
- /// Returns `true` if `self` is `SameSite::Lax` and `false` otherwise.
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::SameSite;
- ///
- /// let lax = SameSite::Lax;
- /// assert!(lax.is_lax());
- /// assert!(!lax.is_strict());
- /// assert!(!lax.is_none());
- /// ```
- #[inline]
- pub fn is_lax(self) -> bool {
- match self {
- SameSite::Lax => true,
- SameSite::Strict | SameSite::None => false,
- }
- }
-
- /// Returns `true` if `self` is `SameSite::None` and `false` otherwise.
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::SameSite;
- ///
- /// let none = SameSite::None;
- /// assert!(none.is_none());
- /// assert!(!none.is_lax());
- /// assert!(!none.is_strict());
- /// ```
- #[inline]
- pub fn is_none(self) -> bool {
- match self {
- SameSite::None => true,
- SameSite::Lax | SameSite::Strict => false,
- }
- }
-}
-
-impl fmt::Display for SameSite {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match *self {
- SameSite::Strict => write!(f, "Strict"),
- SameSite::Lax => write!(f, "Lax"),
- SameSite::None => write!(f, "None"),
- }
- }
-}
diff --git a/actix-http/src/cookie/jar.rs b/actix-http/src/cookie/jar.rs
deleted file mode 100644
index fbefa1bbf..000000000
--- a/actix-http/src/cookie/jar.rs
+++ /dev/null
@@ -1,651 +0,0 @@
-use std::collections::HashSet;
-use std::mem;
-
-use time::{Duration, OffsetDateTime};
-
-use super::delta::DeltaCookie;
-use super::Cookie;
-
-#[cfg(feature = "secure-cookies")]
-use super::secure::{Key, PrivateJar, SignedJar};
-
-/// A collection of cookies that tracks its modifications.
-///
-/// A `CookieJar` provides storage for any number of cookies. Any changes made
-/// to the jar are tracked; the changes can be retrieved via the
-/// [delta](#method.delta) method which returns an iterator over the changes.
-///
-/// # Usage
-///
-/// A jar's life begins via [new](#method.new) and calls to
-/// [`add_original`](#method.add_original):
-///
-/// ```rust
-/// use actix_http::cookie::{Cookie, CookieJar};
-///
-/// let mut jar = CookieJar::new();
-/// jar.add_original(Cookie::new("name", "value"));
-/// jar.add_original(Cookie::new("second", "another"));
-/// ```
-///
-/// Cookies can be added via [add](#method.add) and removed via
-/// [remove](#method.remove). Finally, cookies can be looked up via
-/// [get](#method.get):
-///
-/// ```rust
-/// # use actix_http::cookie::{Cookie, CookieJar};
-/// let mut jar = CookieJar::new();
-/// jar.add(Cookie::new("a", "one"));
-/// jar.add(Cookie::new("b", "two"));
-///
-/// assert_eq!(jar.get("a").map(|c| c.value()), Some("one"));
-/// assert_eq!(jar.get("b").map(|c| c.value()), Some("two"));
-///
-/// jar.remove(Cookie::named("b"));
-/// assert!(jar.get("b").is_none());
-/// ```
-///
-/// # Deltas
-///
-/// A jar keeps track of any modifications made to it over time. The
-/// modifications are recorded as cookies. The modifications can be retrieved
-/// via [delta](#method.delta). Any new `Cookie` added to a jar via `add`
-/// results in the same `Cookie` appearing in the `delta`; cookies added via
-/// `add_original` do not count towards the delta. Any _original_ cookie that is
-/// removed from a jar results in a "removal" cookie appearing in the delta. A
-/// "removal" cookie is a cookie that a server sends so that the cookie is
-/// removed from the client's machine.
-///
-/// Deltas are typically used to create `Set-Cookie` headers corresponding to
-/// the changes made to a cookie jar over a period of time.
-///
-/// ```rust
-/// # use actix_http::cookie::{Cookie, CookieJar};
-/// let mut jar = CookieJar::new();
-///
-/// // original cookies don't affect the delta
-/// jar.add_original(Cookie::new("original", "value"));
-/// assert_eq!(jar.delta().count(), 0);
-///
-/// // new cookies result in an equivalent `Cookie` in the delta
-/// jar.add(Cookie::new("a", "one"));
-/// jar.add(Cookie::new("b", "two"));
-/// assert_eq!(jar.delta().count(), 2);
-///
-/// // removing an original cookie adds a "removal" cookie to the delta
-/// jar.remove(Cookie::named("original"));
-/// assert_eq!(jar.delta().count(), 3);
-///
-/// // removing a new cookie that was added removes that `Cookie` from the delta
-/// jar.remove(Cookie::named("a"));
-/// assert_eq!(jar.delta().count(), 2);
-/// ```
-#[derive(Default, Debug, Clone)]
-pub struct CookieJar {
- original_cookies: HashSet,
- delta_cookies: HashSet,
-}
-
-impl CookieJar {
- /// Creates an empty cookie jar.
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::CookieJar;
- ///
- /// let jar = CookieJar::new();
- /// assert_eq!(jar.iter().count(), 0);
- /// ```
- pub fn new() -> CookieJar {
- CookieJar::default()
- }
-
- /// Returns a reference to the `Cookie` inside this jar with the name
- /// `name`. If no such cookie exists, returns `None`.
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::{CookieJar, Cookie};
- ///
- /// let mut jar = CookieJar::new();
- /// assert!(jar.get("name").is_none());
- ///
- /// jar.add(Cookie::new("name", "value"));
- /// assert_eq!(jar.get("name").map(|c| c.value()), Some("value"));
- /// ```
- pub fn get(&self, name: &str) -> Option<&Cookie<'static>> {
- self.delta_cookies
- .get(name)
- .or_else(|| self.original_cookies.get(name))
- .and_then(|c| if !c.removed { Some(&c.cookie) } else { None })
- }
-
- /// Adds an "original" `cookie` to this jar. If an original cookie with the
- /// same name already exists, it is replaced with `cookie`. Cookies added
- /// with `add` take precedence and are not replaced by this method.
- ///
- /// Adding an original cookie does not affect the [delta](#method.delta)
- /// computation. This method is intended to be used to seed the cookie jar
- /// with cookies received from a client's HTTP message.
- ///
- /// For accurate `delta` computations, this method should not be called
- /// after calling `remove`.
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::{CookieJar, Cookie};
- ///
- /// let mut jar = CookieJar::new();
- /// jar.add_original(Cookie::new("name", "value"));
- /// jar.add_original(Cookie::new("second", "two"));
- ///
- /// assert_eq!(jar.get("name").map(|c| c.value()), Some("value"));
- /// assert_eq!(jar.get("second").map(|c| c.value()), Some("two"));
- /// assert_eq!(jar.iter().count(), 2);
- /// assert_eq!(jar.delta().count(), 0);
- /// ```
- pub fn add_original(&mut self, cookie: Cookie<'static>) {
- self.original_cookies.replace(DeltaCookie::added(cookie));
- }
-
- /// Adds `cookie` to this jar. If a cookie with the same name already
- /// exists, it is replaced with `cookie`.
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::{CookieJar, Cookie};
- ///
- /// let mut jar = CookieJar::new();
- /// jar.add(Cookie::new("name", "value"));
- /// jar.add(Cookie::new("second", "two"));
- ///
- /// assert_eq!(jar.get("name").map(|c| c.value()), Some("value"));
- /// assert_eq!(jar.get("second").map(|c| c.value()), Some("two"));
- /// assert_eq!(jar.iter().count(), 2);
- /// assert_eq!(jar.delta().count(), 2);
- /// ```
- pub fn add(&mut self, cookie: Cookie<'static>) {
- self.delta_cookies.replace(DeltaCookie::added(cookie));
- }
-
- /// Removes `cookie` from this jar. If an _original_ cookie with the same
- /// name as `cookie` is present in the jar, a _removal_ cookie will be
- /// present in the `delta` computation. To properly generate the removal
- /// cookie, `cookie` must contain the same `path` and `domain` as the cookie
- /// that was initially set.
- ///
- /// A "removal" cookie is a cookie that has the same name as the original
- /// cookie but has an empty value, a max-age of 0, and an expiration date
- /// far in the past.
- ///
- /// # Example
- ///
- /// Removing an _original_ cookie results in a _removal_ cookie:
- ///
- /// ```rust
- /// use actix_http::cookie::{CookieJar, Cookie};
- /// use time::Duration;
- ///
- /// let mut jar = CookieJar::new();
- ///
- /// // Assume this cookie originally had a path of "/" and domain of "a.b".
- /// jar.add_original(Cookie::new("name", "value"));
- ///
- /// // If the path and domain were set, they must be provided to `remove`.
- /// jar.remove(Cookie::build("name", "").path("/").domain("a.b").finish());
- ///
- /// // The delta will contain the removal cookie.
- /// let delta: Vec<_> = jar.delta().collect();
- /// assert_eq!(delta.len(), 1);
- /// assert_eq!(delta[0].name(), "name");
- /// assert_eq!(delta[0].max_age(), Some(Duration::zero()));
- /// ```
- ///
- /// Removing a new cookie does not result in a _removal_ cookie:
- ///
- /// ```rust
- /// use actix_http::cookie::{CookieJar, Cookie};
- ///
- /// let mut jar = CookieJar::new();
- /// jar.add(Cookie::new("name", "value"));
- /// assert_eq!(jar.delta().count(), 1);
- ///
- /// jar.remove(Cookie::named("name"));
- /// assert_eq!(jar.delta().count(), 0);
- /// ```
- pub fn remove(&mut self, mut cookie: Cookie<'static>) {
- if self.original_cookies.contains(cookie.name()) {
- cookie.set_value("");
- cookie.set_max_age(Duration::zero());
- cookie.set_expires(OffsetDateTime::now_utc() - Duration::days(365));
- self.delta_cookies.replace(DeltaCookie::removed(cookie));
- } else {
- self.delta_cookies.remove(cookie.name());
- }
- }
-
- /// Removes `cookie` from this jar completely. This method differs from
- /// `remove` in that no delta cookie is created under any condition. Neither
- /// the `delta` nor `iter` methods will return a cookie that is removed
- /// using this method.
- ///
- /// # Example
- ///
- /// Removing an _original_ cookie; no _removal_ cookie is generated:
- ///
- /// ```rust
- /// use actix_http::cookie::{CookieJar, Cookie};
- /// use time::Duration;
- ///
- /// let mut jar = CookieJar::new();
- ///
- /// // Add an original cookie and a new cookie.
- /// jar.add_original(Cookie::new("name", "value"));
- /// jar.add(Cookie::new("key", "value"));
- /// assert_eq!(jar.delta().count(), 1);
- /// assert_eq!(jar.iter().count(), 2);
- ///
- /// // Now force remove the original cookie.
- /// jar.force_remove(Cookie::new("name", "value"));
- /// assert_eq!(jar.delta().count(), 1);
- /// assert_eq!(jar.iter().count(), 1);
- ///
- /// // Now force remove the new cookie.
- /// jar.force_remove(Cookie::new("key", "value"));
- /// assert_eq!(jar.delta().count(), 0);
- /// assert_eq!(jar.iter().count(), 0);
- /// ```
- pub fn force_remove<'a>(&mut self, cookie: Cookie<'a>) {
- self.original_cookies.remove(cookie.name());
- self.delta_cookies.remove(cookie.name());
- }
-
- /// Removes all cookies from this cookie jar.
- #[deprecated(
- since = "0.7.0",
- note = "calling this method may not remove \
- all cookies since the path and domain are not specified; use \
- `remove` instead"
- )]
- pub fn clear(&mut self) {
- self.delta_cookies.clear();
- for delta in mem::take(&mut self.original_cookies) {
- self.remove(delta.cookie);
- }
- }
-
- /// Returns an iterator over cookies that represent the changes to this jar
- /// over time. These cookies can be rendered directly as `Set-Cookie` header
- /// values to affect the changes made to this jar on the client.
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::{CookieJar, Cookie};
- ///
- /// let mut jar = CookieJar::new();
- /// jar.add_original(Cookie::new("name", "value"));
- /// jar.add_original(Cookie::new("second", "two"));
- ///
- /// // Add new cookies.
- /// jar.add(Cookie::new("new", "third"));
- /// jar.add(Cookie::new("another", "fourth"));
- /// jar.add(Cookie::new("yac", "fifth"));
- ///
- /// // Remove some cookies.
- /// jar.remove(Cookie::named("name"));
- /// jar.remove(Cookie::named("another"));
- ///
- /// // Delta contains two new cookies ("new", "yac") and a removal ("name").
- /// assert_eq!(jar.delta().count(), 3);
- /// ```
- pub fn delta(&self) -> Delta<'_> {
- Delta {
- iter: self.delta_cookies.iter(),
- }
- }
-
- /// Returns an iterator over all of the cookies present in this jar.
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::{CookieJar, Cookie};
- ///
- /// let mut jar = CookieJar::new();
- ///
- /// jar.add_original(Cookie::new("name", "value"));
- /// jar.add_original(Cookie::new("second", "two"));
- ///
- /// jar.add(Cookie::new("new", "third"));
- /// jar.add(Cookie::new("another", "fourth"));
- /// jar.add(Cookie::new("yac", "fifth"));
- ///
- /// jar.remove(Cookie::named("name"));
- /// jar.remove(Cookie::named("another"));
- ///
- /// // There are three cookies in the jar: "second", "new", and "yac".
- /// # assert_eq!(jar.iter().count(), 3);
- /// for cookie in jar.iter() {
- /// match cookie.name() {
- /// "second" => assert_eq!(cookie.value(), "two"),
- /// "new" => assert_eq!(cookie.value(), "third"),
- /// "yac" => assert_eq!(cookie.value(), "fifth"),
- /// _ => unreachable!("there are only three cookies in the jar")
- /// }
- /// }
- /// ```
- pub fn iter(&self) -> Iter<'_> {
- Iter {
- delta_cookies: self
- .delta_cookies
- .iter()
- .chain(self.original_cookies.difference(&self.delta_cookies)),
- }
- }
-
- /// Returns a `PrivateJar` with `self` as its parent jar using the key `key`
- /// to sign/encrypt and verify/decrypt cookies added/retrieved from the
- /// child jar.
- ///
- /// Any modifications to the child jar will be reflected on the parent jar,
- /// and any retrievals from the child jar will be made from the parent jar.
- ///
- /// This method is only available when the `secure` feature is enabled.
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::{Cookie, CookieJar, Key};
- ///
- /// // Generate a secure key.
- /// let key = Key::generate();
- ///
- /// // Add a private (signed + encrypted) cookie.
- /// let mut jar = CookieJar::new();
- /// jar.private(&key).add(Cookie::new("private", "text"));
- ///
- /// // The cookie's contents are encrypted.
- /// assert_ne!(jar.get("private").unwrap().value(), "text");
- ///
- /// // They can be decrypted and verified through the child jar.
- /// assert_eq!(jar.private(&key).get("private").unwrap().value(), "text");
- ///
- /// // A tampered with cookie does not validate but still exists.
- /// let mut cookie = jar.get("private").unwrap().clone();
- /// jar.add(Cookie::new("private", cookie.value().to_string() + "!"));
- /// assert!(jar.private(&key).get("private").is_none());
- /// assert!(jar.get("private").is_some());
- /// ```
- #[cfg(feature = "secure-cookies")]
- pub fn private(&mut self, key: &Key) -> PrivateJar<'_> {
- PrivateJar::new(self, key)
- }
-
- /// Returns a `SignedJar` with `self` as its parent jar using the key `key`
- /// to sign/verify cookies added/retrieved from the child jar.
- ///
- /// Any modifications to the child jar will be reflected on the parent jar,
- /// and any retrievals from the child jar will be made from the parent jar.
- ///
- /// This method is only available when the `secure` feature is enabled.
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::{Cookie, CookieJar, Key};
- ///
- /// // Generate a secure key.
- /// let key = Key::generate();
- ///
- /// // Add a signed cookie.
- /// let mut jar = CookieJar::new();
- /// jar.signed(&key).add(Cookie::new("signed", "text"));
- ///
- /// // The cookie's contents are signed but still in plaintext.
- /// assert_ne!(jar.get("signed").unwrap().value(), "text");
- /// assert!(jar.get("signed").unwrap().value().contains("text"));
- ///
- /// // They can be verified through the child jar.
- /// assert_eq!(jar.signed(&key).get("signed").unwrap().value(), "text");
- ///
- /// // A tampered with cookie does not validate but still exists.
- /// let mut cookie = jar.get("signed").unwrap().clone();
- /// jar.add(Cookie::new("signed", cookie.value().to_string() + "!"));
- /// assert!(jar.signed(&key).get("signed").is_none());
- /// assert!(jar.get("signed").is_some());
- /// ```
- #[cfg(feature = "secure-cookies")]
- pub fn signed(&mut self, key: &Key) -> SignedJar<'_> {
- SignedJar::new(self, key)
- }
-}
-
-use std::collections::hash_set::Iter as HashSetIter;
-
-/// Iterator over the changes to a cookie jar.
-pub struct Delta<'a> {
- iter: HashSetIter<'a, DeltaCookie>,
-}
-
-impl<'a> Iterator for Delta<'a> {
- type Item = &'a Cookie<'static>;
-
- fn next(&mut self) -> Option<&'a Cookie<'static>> {
- self.iter.next().map(|c| &c.cookie)
- }
-}
-
-use std::collections::hash_map::RandomState;
-use std::collections::hash_set::Difference;
-use std::iter::Chain;
-
-/// Iterator over all of the cookies in a jar.
-pub struct Iter<'a> {
- delta_cookies:
- Chain, Difference<'a, DeltaCookie, RandomState>>,
-}
-
-impl<'a> Iterator for Iter<'a> {
- type Item = &'a Cookie<'static>;
-
- fn next(&mut self) -> Option<&'a Cookie<'static>> {
- for cookie in self.delta_cookies.by_ref() {
- if !cookie.removed {
- return Some(&*cookie);
- }
- }
-
- None
- }
-}
-
-#[cfg(test)]
-mod test {
- #[cfg(feature = "secure-cookies")]
- use super::Key;
- use super::{Cookie, CookieJar};
-
- #[test]
- #[allow(deprecated)]
- fn simple() {
- let mut c = CookieJar::new();
-
- c.add(Cookie::new("test", ""));
- c.add(Cookie::new("test2", ""));
- c.remove(Cookie::named("test"));
-
- assert!(c.get("test").is_none());
- assert!(c.get("test2").is_some());
-
- c.add(Cookie::new("test3", ""));
- c.clear();
-
- assert!(c.get("test").is_none());
- assert!(c.get("test2").is_none());
- assert!(c.get("test3").is_none());
- }
-
- #[test]
- fn jar_is_send() {
- fn is_send(_: T) -> bool {
- true
- }
-
- assert!(is_send(CookieJar::new()))
- }
-
- #[test]
- #[cfg(feature = "secure-cookies")]
- fn iter() {
- let key = Key::generate();
- let mut c = CookieJar::new();
-
- c.add_original(Cookie::new("original", "original"));
-
- c.add(Cookie::new("test", "test"));
- c.add(Cookie::new("test2", "test2"));
- c.add(Cookie::new("test3", "test3"));
- assert_eq!(c.iter().count(), 4);
-
- c.signed(&key).add(Cookie::new("signed", "signed"));
- c.private(&key).add(Cookie::new("encrypted", "encrypted"));
- assert_eq!(c.iter().count(), 6);
-
- c.remove(Cookie::named("test"));
- assert_eq!(c.iter().count(), 5);
-
- c.remove(Cookie::named("signed"));
- c.remove(Cookie::named("test2"));
- assert_eq!(c.iter().count(), 3);
-
- c.add(Cookie::new("test2", "test2"));
- assert_eq!(c.iter().count(), 4);
-
- c.remove(Cookie::named("test2"));
- assert_eq!(c.iter().count(), 3);
- }
-
- #[test]
- #[cfg(feature = "secure-cookies")]
- fn delta() {
- use std::collections::HashMap;
- use time::Duration;
-
- let mut c = CookieJar::new();
-
- c.add_original(Cookie::new("original", "original"));
- c.add_original(Cookie::new("original1", "original1"));
-
- c.add(Cookie::new("test", "test"));
- c.add(Cookie::new("test2", "test2"));
- c.add(Cookie::new("test3", "test3"));
- c.add(Cookie::new("test4", "test4"));
-
- c.remove(Cookie::named("test"));
- c.remove(Cookie::named("original"));
-
- assert_eq!(c.delta().count(), 4);
-
- let names: HashMap<_, _> = c.delta().map(|c| (c.name(), c.max_age())).collect();
-
- assert!(names.get("test2").unwrap().is_none());
- assert!(names.get("test3").unwrap().is_none());
- assert!(names.get("test4").unwrap().is_none());
- assert_eq!(names.get("original").unwrap(), &Some(Duration::zero()));
- }
-
- #[test]
- fn replace_original() {
- let mut jar = CookieJar::new();
- jar.add_original(Cookie::new("original_a", "a"));
- jar.add_original(Cookie::new("original_b", "b"));
- assert_eq!(jar.get("original_a").unwrap().value(), "a");
-
- jar.add(Cookie::new("original_a", "av2"));
- assert_eq!(jar.get("original_a").unwrap().value(), "av2");
- }
-
- #[test]
- fn empty_delta() {
- let mut jar = CookieJar::new();
- jar.add(Cookie::new("name", "val"));
- assert_eq!(jar.delta().count(), 1);
-
- jar.remove(Cookie::named("name"));
- assert_eq!(jar.delta().count(), 0);
-
- jar.add_original(Cookie::new("name", "val"));
- assert_eq!(jar.delta().count(), 0);
-
- jar.remove(Cookie::named("name"));
- assert_eq!(jar.delta().count(), 1);
-
- jar.add(Cookie::new("name", "val"));
- assert_eq!(jar.delta().count(), 1);
-
- jar.remove(Cookie::named("name"));
- assert_eq!(jar.delta().count(), 1);
- }
-
- #[test]
- fn add_remove_add() {
- let mut jar = CookieJar::new();
- jar.add_original(Cookie::new("name", "val"));
- assert_eq!(jar.delta().count(), 0);
-
- jar.remove(Cookie::named("name"));
- assert_eq!(jar.delta().filter(|c| c.value().is_empty()).count(), 1);
- assert_eq!(jar.delta().count(), 1);
-
- // The cookie's been deleted. Another original doesn't change that.
- jar.add_original(Cookie::new("name", "val"));
- assert_eq!(jar.delta().filter(|c| c.value().is_empty()).count(), 1);
- assert_eq!(jar.delta().count(), 1);
-
- jar.remove(Cookie::named("name"));
- assert_eq!(jar.delta().filter(|c| c.value().is_empty()).count(), 1);
- assert_eq!(jar.delta().count(), 1);
-
- jar.add(Cookie::new("name", "val"));
- assert_eq!(jar.delta().filter(|c| !c.value().is_empty()).count(), 1);
- assert_eq!(jar.delta().count(), 1);
-
- jar.remove(Cookie::named("name"));
- assert_eq!(jar.delta().filter(|c| c.value().is_empty()).count(), 1);
- assert_eq!(jar.delta().count(), 1);
- }
-
- #[test]
- fn replace_remove() {
- let mut jar = CookieJar::new();
- jar.add_original(Cookie::new("name", "val"));
- assert_eq!(jar.delta().count(), 0);
-
- jar.add(Cookie::new("name", "val"));
- assert_eq!(jar.delta().count(), 1);
- assert_eq!(jar.delta().filter(|c| !c.value().is_empty()).count(), 1);
-
- jar.remove(Cookie::named("name"));
- assert_eq!(jar.delta().filter(|c| c.value().is_empty()).count(), 1);
- }
-
- #[test]
- fn remove_with_path() {
- let mut jar = CookieJar::new();
- jar.add_original(Cookie::build("name", "val").finish());
- assert_eq!(jar.iter().count(), 1);
- assert_eq!(jar.delta().count(), 0);
- assert_eq!(jar.iter().filter(|c| c.path().is_none()).count(), 1);
-
- jar.remove(Cookie::build("name", "").path("/").finish());
- assert_eq!(jar.iter().count(), 0);
- assert_eq!(jar.delta().count(), 1);
- assert_eq!(jar.delta().filter(|c| c.value().is_empty()).count(), 1);
- assert_eq!(jar.delta().filter(|c| c.path() == Some("/")).count(), 1);
- }
-}
diff --git a/actix-http/src/cookie/mod.rs b/actix-http/src/cookie/mod.rs
deleted file mode 100644
index b94e0fe0f..000000000
--- a/actix-http/src/cookie/mod.rs
+++ /dev/null
@@ -1,1100 +0,0 @@
-//! https://github.com/alexcrichton/cookie-rs fork
-//!
-//! HTTP cookie parsing and cookie jar management.
-//!
-//! This crates provides the [`Cookie`](struct.Cookie.html) type, which directly
-//! maps to an HTTP cookie, and the [`CookieJar`](struct.CookieJar.html) type,
-//! which allows for simple management of many cookies as well as encryption and
-//! signing of cookies for session management.
-//!
-//! # Features
-//!
-//! This crates can be configured at compile-time through the following Cargo
-//! features:
-//!
-//!
-//! * **secure** (disabled by default)
-//!
-//! Enables signed and private (signed + encrypted) cookie jars.
-//!
-//! When this feature is enabled, the
-//! [signed](struct.CookieJar.html#method.signed) and
-//! [private](struct.CookieJar.html#method.private) method of `CookieJar` and
-//! [`SignedJar`](struct.SignedJar.html) and
-//! [`PrivateJar`](struct.PrivateJar.html) structures are available. The jars
-//! act as "children jars", allowing for easy retrieval and addition of signed
-//! and/or encrypted cookies to a cookie jar. When this feature is disabled,
-//! none of the types are available.
-//!
-//! * **percent-encode** (disabled by default)
-//!
-//! Enables percent encoding and decoding of names and values in cookies.
-//!
-//! When this feature is enabled, the
-//! [encoded](struct.Cookie.html#method.encoded) and
-//! [`parse_encoded`](struct.Cookie.html#method.parse_encoded) methods of
-//! `Cookie` become available. The `encoded` method returns a wrapper around a
-//! `Cookie` whose `Display` implementation percent-encodes the name and value
-//! of the cookie. The `parse_encoded` method percent-decodes the name and
-//! value of a `Cookie` during parsing. When this feature is disabled, the
-//! `encoded` and `parse_encoded` methods are not available.
-//!
-//! You can enable features via the `Cargo.toml` file:
-//!
-//! ```ignore
-//! [dependencies.cookie]
-//! features = ["secure", "percent-encode"]
-//! ```
-
-#![doc(html_root_url = "https://docs.rs/cookie/0.11")]
-#![warn(missing_docs)]
-
-mod builder;
-mod delta;
-mod draft;
-mod jar;
-mod parse;
-
-#[cfg(feature = "secure-cookies")]
-#[macro_use]
-mod secure;
-#[cfg(feature = "secure-cookies")]
-pub use self::secure::*;
-
-use std::borrow::Cow;
-use std::fmt;
-use std::str::FromStr;
-
-use percent_encoding::{percent_encode, AsciiSet, CONTROLS};
-use time::{Duration, OffsetDateTime};
-
-pub use self::builder::CookieBuilder;
-pub use self::draft::*;
-pub use self::jar::{CookieJar, Delta, Iter};
-use self::parse::parse_cookie;
-pub use self::parse::ParseError;
-
-/// https://url.spec.whatwg.org/#fragment-percent-encode-set
-const FRAGMENT: &AsciiSet = &CONTROLS.add(b' ').add(b'"').add(b'<').add(b'>').add(b'`');
-
-/// https://url.spec.whatwg.org/#path-percent-encode-set
-const PATH: &AsciiSet = &FRAGMENT.add(b'#').add(b'?').add(b'{').add(b'}');
-
-/// https://url.spec.whatwg.org/#userinfo-percent-encode-set
-pub const USERINFO: &AsciiSet = &PATH
- .add(b'/')
- .add(b':')
- .add(b';')
- .add(b'=')
- .add(b'@')
- .add(b'[')
- .add(b'\\')
- .add(b']')
- .add(b'^')
- .add(b'|');
-
-#[derive(Debug, Clone)]
-enum CookieStr {
- /// An string derived from indexes (start, end).
- Indexed(usize, usize),
- /// A string derived from a concrete string.
- Concrete(Cow<'static, str>),
-}
-
-impl CookieStr {
- /// Retrieves the string `self` corresponds to. If `self` is derived from
- /// indexes, the corresponding sub-slice of `string` is returned. Otherwise,
- /// the concrete string is returned.
- ///
- /// # Panics
- ///
- /// Panics if `self` is an indexed string and `string` is None.
- fn to_str<'s>(&'s self, string: Option<&'s Cow<'_, str>>) -> &'s str {
- match *self {
- CookieStr::Indexed(i, j) => {
- let s = string.expect(
- "`Some` base string must exist when \
- converting indexed str to str! (This is a module invariant.)",
- );
- &s[i..j]
- }
- CookieStr::Concrete(ref cstr) => &*cstr,
- }
- }
-
- #[allow(clippy::ptr_arg)]
- fn to_raw_str<'s, 'c: 's>(&'s self, string: &'s Cow<'c, str>) -> Option<&'c str> {
- match *self {
- CookieStr::Indexed(i, j) => match *string {
- Cow::Borrowed(s) => Some(&s[i..j]),
- Cow::Owned(_) => None,
- },
- CookieStr::Concrete(_) => None,
- }
- }
-}
-
-/// Representation of an HTTP cookie.
-///
-/// # Constructing a `Cookie`
-///
-/// To construct a cookie with only a name/value, use the [new](#method.new)
-/// method:
-///
-/// ```rust
-/// use actix_http::cookie::Cookie;
-///
-/// let cookie = Cookie::new("name", "value");
-/// assert_eq!(&cookie.to_string(), "name=value");
-/// ```
-///
-/// To construct more elaborate cookies, use the [build](#method.build) method
-/// and [`CookieBuilder`](struct.CookieBuilder.html) methods:
-///
-/// ```rust
-/// use actix_http::cookie::Cookie;
-///
-/// let cookie = Cookie::build("name", "value")
-/// .domain("www.rust-lang.org")
-/// .path("/")
-/// .secure(true)
-/// .http_only(true)
-/// .finish();
-/// ```
-#[derive(Debug, Clone)]
-pub struct Cookie<'c> {
- /// Storage for the cookie string. Only used if this structure was derived
- /// from a string that was subsequently parsed.
- cookie_string: Option>,
- /// The cookie's name.
- name: CookieStr,
- /// The cookie's value.
- value: CookieStr,
- /// The cookie's expiration, if any.
- expires: Option,
- /// The cookie's maximum age, if any.
- max_age: Option,
- /// The cookie's domain, if any.
- domain: Option,
- /// The cookie's path domain, if any.
- path: Option,
- /// Whether this cookie was marked Secure.
- secure: Option,
- /// Whether this cookie was marked HttpOnly.
- http_only: Option,
- /// The draft `SameSite` attribute.
- same_site: Option,
-}
-
-impl Cookie<'static> {
- /// Creates a new `Cookie` with the given name and value.
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::Cookie;
- ///
- /// let cookie = Cookie::new("name", "value");
- /// assert_eq!(cookie.name_value(), ("name", "value"));
- /// ```
- pub fn new(name: N, value: V) -> Cookie<'static>
- where
- N: Into>,
- V: Into>,
- {
- Cookie {
- cookie_string: None,
- name: CookieStr::Concrete(name.into()),
- value: CookieStr::Concrete(value.into()),
- expires: None,
- max_age: None,
- domain: None,
- path: None,
- secure: None,
- http_only: None,
- same_site: None,
- }
- }
-
- /// Creates a new `Cookie` with the given name and an empty value.
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::Cookie;
- ///
- /// let cookie = Cookie::named("name");
- /// assert_eq!(cookie.name(), "name");
- /// assert!(cookie.value().is_empty());
- /// ```
- pub fn named(name: N) -> Cookie<'static>
- where
- N: Into>,
- {
- Cookie::new(name, "")
- }
-
- /// Creates a new `CookieBuilder` instance from the given key and value
- /// strings.
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::Cookie;
- ///
- /// let c = Cookie::build("foo", "bar").finish();
- /// assert_eq!(c.name_value(), ("foo", "bar"));
- /// ```
- pub fn build(name: N, value: V) -> CookieBuilder
- where
- N: Into>,
- V: Into>,
- {
- CookieBuilder::new(name, value)
- }
-}
-
-impl<'c> Cookie<'c> {
- /// Parses a `Cookie` from the given HTTP cookie header value string. Does
- /// not perform any percent-decoding.
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::Cookie;
- ///
- /// let c = Cookie::parse("foo=bar%20baz; HttpOnly").unwrap();
- /// assert_eq!(c.name_value(), ("foo", "bar%20baz"));
- /// assert_eq!(c.http_only(), Some(true));
- /// ```
- pub fn parse(s: S) -> Result, ParseError>
- where
- S: Into>,
- {
- parse_cookie(s, false)
- }
-
- /// Parses a `Cookie` from the given HTTP cookie header value string where
- /// the name and value fields are percent-encoded. Percent-decodes the
- /// name/value fields.
- ///
- /// This API requires the `percent-encode` feature to be enabled on this
- /// crate.
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::Cookie;
- ///
- /// let c = Cookie::parse_encoded("foo=bar%20baz; HttpOnly").unwrap();
- /// assert_eq!(c.name_value(), ("foo", "bar baz"));
- /// assert_eq!(c.http_only(), Some(true));
- /// ```
- pub fn parse_encoded(s: S) -> Result, ParseError>
- where
- S: Into>,
- {
- parse_cookie(s, true)
- }
-
- /// Wraps `self` in an `EncodedCookie`: a cost-free wrapper around `Cookie`
- /// whose `Display` implementation percent-encodes the name and value of the
- /// wrapped `Cookie`.
- ///
- /// This method is only available when the `percent-encode` feature is
- /// enabled.
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::Cookie;
- ///
- /// let mut c = Cookie::new("my name", "this; value?");
- /// assert_eq!(&c.encoded().to_string(), "my%20name=this%3B%20value%3F");
- /// ```
- pub fn encoded<'a>(&'a self) -> EncodedCookie<'a, 'c> {
- EncodedCookie(self)
- }
-
- /// Converts `self` into a `Cookie` with a static lifetime. This method
- /// results in at most one allocation.
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::Cookie;
- ///
- /// let c = Cookie::new("a", "b");
- /// let owned_cookie = c.into_owned();
- /// assert_eq!(owned_cookie.name_value(), ("a", "b"));
- /// ```
- pub fn into_owned(self) -> Cookie<'static> {
- Cookie {
- cookie_string: self.cookie_string.map(|s| s.into_owned().into()),
- name: self.name,
- value: self.value,
- expires: self.expires,
- max_age: self.max_age,
- domain: self.domain,
- path: self.path,
- secure: self.secure,
- http_only: self.http_only,
- same_site: self.same_site,
- }
- }
-
- /// Returns the name of `self`.
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::Cookie;
- ///
- /// let c = Cookie::new("name", "value");
- /// assert_eq!(c.name(), "name");
- /// ```
- #[inline]
- pub fn name(&self) -> &str {
- self.name.to_str(self.cookie_string.as_ref())
- }
-
- /// Returns the value of `self`.
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::Cookie;
- ///
- /// let c = Cookie::new("name", "value");
- /// assert_eq!(c.value(), "value");
- /// ```
- #[inline]
- pub fn value(&self) -> &str {
- self.value.to_str(self.cookie_string.as_ref())
- }
-
- /// Returns the name and value of `self` as a tuple of `(name, value)`.
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::Cookie;
- ///
- /// let c = Cookie::new("name", "value");
- /// assert_eq!(c.name_value(), ("name", "value"));
- /// ```
- #[inline]
- pub fn name_value(&self) -> (&str, &str) {
- (self.name(), self.value())
- }
-
- /// Returns whether this cookie was marked `HttpOnly` or not. Returns
- /// `Some(true)` when the cookie was explicitly set (manually or parsed) as
- /// `HttpOnly`, `Some(false)` when `http_only` was manually set to `false`,
- /// and `None` otherwise.
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::Cookie;
- ///
- /// let c = Cookie::parse("name=value; httponly").unwrap();
- /// assert_eq!(c.http_only(), Some(true));
- ///
- /// let mut c = Cookie::new("name", "value");
- /// assert_eq!(c.http_only(), None);
- ///
- /// let mut c = Cookie::new("name", "value");
- /// assert_eq!(c.http_only(), None);
- ///
- /// // An explicitly set "false" value.
- /// c.set_http_only(false);
- /// assert_eq!(c.http_only(), Some(false));
- ///
- /// // An explicitly set "true" value.
- /// c.set_http_only(true);
- /// assert_eq!(c.http_only(), Some(true));
- /// ```
- #[inline]
- pub fn http_only(&self) -> Option {
- self.http_only
- }
-
- /// Returns whether this cookie was marked `Secure` or not. Returns
- /// `Some(true)` when the cookie was explicitly set (manually or parsed) as
- /// `Secure`, `Some(false)` when `secure` was manually set to `false`, and
- /// `None` otherwise.
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::Cookie;
- ///
- /// let c = Cookie::parse("name=value; Secure").unwrap();
- /// assert_eq!(c.secure(), Some(true));
- ///
- /// let mut c = Cookie::parse("name=value").unwrap();
- /// assert_eq!(c.secure(), None);
- ///
- /// let mut c = Cookie::new("name", "value");
- /// assert_eq!(c.secure(), None);
- ///
- /// // An explicitly set "false" value.
- /// c.set_secure(false);
- /// assert_eq!(c.secure(), Some(false));
- ///
- /// // An explicitly set "true" value.
- /// c.set_secure(true);
- /// assert_eq!(c.secure(), Some(true));
- /// ```
- #[inline]
- pub fn secure(&self) -> Option {
- self.secure
- }
-
- /// Returns the `SameSite` attribute of this cookie if one was specified.
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::{Cookie, SameSite};
- ///
- /// let c = Cookie::parse("name=value; SameSite=Lax").unwrap();
- /// assert_eq!(c.same_site(), Some(SameSite::Lax));
- /// ```
- #[inline]
- pub fn same_site(&self) -> Option {
- self.same_site
- }
-
- /// Returns the specified max-age of the cookie if one was specified.
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::Cookie;
- ///
- /// let c = Cookie::parse("name=value").unwrap();
- /// assert_eq!(c.max_age(), None);
- ///
- /// let c = Cookie::parse("name=value; Max-Age=3600").unwrap();
- /// assert_eq!(c.max_age().map(|age| age.whole_hours()), Some(1));
- /// ```
- #[inline]
- pub fn max_age(&self) -> Option {
- self.max_age
- }
-
- /// Returns the `Path` of the cookie if one was specified.
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::Cookie;
- ///
- /// let c = Cookie::parse("name=value").unwrap();
- /// assert_eq!(c.path(), None);
- ///
- /// let c = Cookie::parse("name=value; Path=/").unwrap();
- /// assert_eq!(c.path(), Some("/"));
- ///
- /// let c = Cookie::parse("name=value; path=/sub").unwrap();
- /// assert_eq!(c.path(), Some("/sub"));
- /// ```
- #[inline]
- pub fn path(&self) -> Option<&str> {
- match self.path {
- Some(ref c) => Some(c.to_str(self.cookie_string.as_ref())),
- None => None,
- }
- }
-
- /// Returns the `Domain` of the cookie if one was specified.
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::Cookie;
- ///
- /// let c = Cookie::parse("name=value").unwrap();
- /// assert_eq!(c.domain(), None);
- ///
- /// let c = Cookie::parse("name=value; Domain=crates.io").unwrap();
- /// assert_eq!(c.domain(), Some("crates.io"));
- /// ```
- #[inline]
- pub fn domain(&self) -> Option<&str> {
- match self.domain {
- Some(ref c) => Some(c.to_str(self.cookie_string.as_ref())),
- None => None,
- }
- }
-
- /// Returns the `Expires` time of the cookie if one was specified.
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::Cookie;
- ///
- /// let c = Cookie::parse("name=value").unwrap();
- /// assert_eq!(c.expires(), None);
- ///
- /// let expire_time = "Wed, 21 Oct 2017 07:28:00 GMT";
- /// let cookie_str = format!("name=value; Expires={}", expire_time);
- /// let c = Cookie::parse(cookie_str).unwrap();
- /// assert_eq!(c.expires().map(|t| t.year()), Some(2017));
- /// ```
- #[inline]
- pub fn expires(&self) -> Option {
- self.expires
- }
-
- /// Sets the name of `self` to `name`.
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::Cookie;
- ///
- /// let mut c = Cookie::new("name", "value");
- /// assert_eq!(c.name(), "name");
- ///
- /// c.set_name("foo");
- /// assert_eq!(c.name(), "foo");
- /// ```
- pub fn set_name>>(&mut self, name: N) {
- self.name = CookieStr::Concrete(name.into())
- }
-
- /// Sets the value of `self` to `value`.
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::Cookie;
- ///
- /// let mut c = Cookie::new("name", "value");
- /// assert_eq!(c.value(), "value");
- ///
- /// c.set_value("bar");
- /// assert_eq!(c.value(), "bar");
- /// ```
- pub fn set_value>>(&mut self, value: V) {
- self.value = CookieStr::Concrete(value.into())
- }
-
- /// Sets the value of `http_only` in `self` to `value`.
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::Cookie;
- ///
- /// let mut c = Cookie::new("name", "value");
- /// assert_eq!(c.http_only(), None);
- ///
- /// c.set_http_only(true);
- /// assert_eq!(c.http_only(), Some(true));
- /// ```
- #[inline]
- pub fn set_http_only(&mut self, value: bool) {
- self.http_only = Some(value);
- }
-
- /// Sets the value of `secure` in `self` to `value`.
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::Cookie;
- ///
- /// let mut c = Cookie::new("name", "value");
- /// assert_eq!(c.secure(), None);
- ///
- /// c.set_secure(true);
- /// assert_eq!(c.secure(), Some(true));
- /// ```
- #[inline]
- pub fn set_secure(&mut self, value: bool) {
- self.secure = Some(value);
- }
-
- /// Sets the value of `same_site` in `self` to `value`.
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::{Cookie, SameSite};
- ///
- /// let mut c = Cookie::new("name", "value");
- /// assert!(c.same_site().is_none());
- ///
- /// c.set_same_site(SameSite::Strict);
- /// assert_eq!(c.same_site(), Some(SameSite::Strict));
- /// ```
- #[inline]
- pub fn set_same_site(&mut self, value: SameSite) {
- self.same_site = Some(value);
- }
-
- /// Sets the value of `max_age` in `self` to `value`.
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::Cookie;
- /// use time::Duration;
- ///
- /// let mut c = Cookie::new("name", "value");
- /// assert_eq!(c.max_age(), None);
- ///
- /// c.set_max_age(Duration::hours(10));
- /// assert_eq!(c.max_age(), Some(Duration::hours(10)));
- /// ```
- #[inline]
- pub fn set_max_age(&mut self, value: Duration) {
- self.max_age = Some(value);
- }
-
- /// Sets the `path` of `self` to `path`.
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::Cookie;
- ///
- /// let mut c = Cookie::new("name", "value");
- /// assert_eq!(c.path(), None);
- ///
- /// c.set_path("/");
- /// assert_eq!(c.path(), Some("/"));
- /// ```
- pub fn set_path>>(&mut self, path: P) {
- self.path = Some(CookieStr::Concrete(path.into()));
- }
-
- /// Sets the `domain` of `self` to `domain`.
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::Cookie;
- ///
- /// let mut c = Cookie::new("name", "value");
- /// assert_eq!(c.domain(), None);
- ///
- /// c.set_domain("rust-lang.org");
- /// assert_eq!(c.domain(), Some("rust-lang.org"));
- /// ```
- pub fn set_domain>>(&mut self, domain: D) {
- self.domain = Some(CookieStr::Concrete(domain.into()));
- }
-
- /// Sets the expires field of `self` to `time`.
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::Cookie;
- /// use time::{Duration, OffsetDateTime};
- ///
- /// let mut c = Cookie::new("name", "value");
- /// assert_eq!(c.expires(), None);
- ///
- /// let mut now = OffsetDateTime::now();
- /// now += Duration::week();
- ///
- /// c.set_expires(now);
- /// assert!(c.expires().is_some())
- /// ```
- #[inline]
- pub fn set_expires(&mut self, time: OffsetDateTime) {
- self.expires = Some(time);
- }
-
- /// Makes `self` a "permanent" cookie by extending its expiration and max
- /// age 20 years into the future.
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::Cookie;
- /// use time::Duration;
- ///
- /// let mut c = Cookie::new("foo", "bar");
- /// assert!(c.expires().is_none());
- /// assert!(c.max_age().is_none());
- ///
- /// c.make_permanent();
- /// assert!(c.expires().is_some());
- /// assert_eq!(c.max_age(), Some(Duration::days(365 * 20)));
- /// ```
- pub fn make_permanent(&mut self) {
- let twenty_years = Duration::days(365 * 20);
- self.set_max_age(twenty_years);
- self.set_expires(OffsetDateTime::now_utc() + twenty_years);
- }
-
- fn fmt_parameters(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- if let Some(true) = self.http_only() {
- write!(f, "; HttpOnly")?;
- }
-
- if let Some(true) = self.secure() {
- write!(f, "; Secure")?;
- }
-
- if let Some(same_site) = self.same_site() {
- write!(f, "; SameSite={}", same_site)?;
- }
-
- if let Some(path) = self.path() {
- write!(f, "; Path={}", path)?;
- }
-
- if let Some(domain) = self.domain() {
- write!(f, "; Domain={}", domain)?;
- }
-
- if let Some(max_age) = self.max_age() {
- write!(f, "; Max-Age={}", max_age.whole_seconds())?;
- }
-
- if let Some(time) = self.expires() {
- write!(f, "; Expires={}", time.format("%a, %d %b %Y %H:%M:%S GMT"))?;
- }
-
- Ok(())
- }
-
- /// Returns the name of `self` as a string slice of the raw string `self`
- /// was originally parsed from. If `self` was not originally parsed from a
- /// raw string, returns `None`.
- ///
- /// This method differs from [name](#method.name) in that it returns a
- /// string with the same lifetime as the originally parsed string. This
- /// lifetime may outlive `self`. If a longer lifetime is not required, or
- /// you're unsure if you need a longer lifetime, use [name](#method.name).
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::Cookie;
- ///
- /// let cookie_string = format!("{}={}", "foo", "bar");
- ///
- /// // `c` will be dropped at the end of the scope, but `name` will live on
- /// let name = {
- /// let c = Cookie::parse(cookie_string.as_str()).unwrap();
- /// c.name_raw()
- /// };
- ///
- /// assert_eq!(name, Some("foo"));
- /// ```
- #[inline]
- pub fn name_raw(&self) -> Option<&'c str> {
- self.cookie_string
- .as_ref()
- .and_then(|s| self.name.to_raw_str(s))
- }
-
- /// Returns the value of `self` as a string slice of the raw string `self`
- /// was originally parsed from. If `self` was not originally parsed from a
- /// raw string, returns `None`.
- ///
- /// This method differs from [value](#method.value) in that it returns a
- /// string with the same lifetime as the originally parsed string. This
- /// lifetime may outlive `self`. If a longer lifetime is not required, or
- /// you're unsure if you need a longer lifetime, use [value](#method.value).
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::Cookie;
- ///
- /// let cookie_string = format!("{}={}", "foo", "bar");
- ///
- /// // `c` will be dropped at the end of the scope, but `value` will live on
- /// let value = {
- /// let c = Cookie::parse(cookie_string.as_str()).unwrap();
- /// c.value_raw()
- /// };
- ///
- /// assert_eq!(value, Some("bar"));
- /// ```
- #[inline]
- pub fn value_raw(&self) -> Option<&'c str> {
- self.cookie_string
- .as_ref()
- .and_then(|s| self.value.to_raw_str(s))
- }
-
- /// Returns the `Path` of `self` as a string slice of the raw string `self`
- /// was originally parsed from. If `self` was not originally parsed from a
- /// raw string, or if `self` doesn't contain a `Path`, or if the `Path` has
- /// changed since parsing, returns `None`.
- ///
- /// This method differs from [path](#method.path) in that it returns a
- /// string with the same lifetime as the originally parsed string. This
- /// lifetime may outlive `self`. If a longer lifetime is not required, or
- /// you're unsure if you need a longer lifetime, use [path](#method.path).
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::Cookie;
- ///
- /// let cookie_string = format!("{}={}; Path=/", "foo", "bar");
- ///
- /// // `c` will be dropped at the end of the scope, but `path` will live on
- /// let path = {
- /// let c = Cookie::parse(cookie_string.as_str()).unwrap();
- /// c.path_raw()
- /// };
- ///
- /// assert_eq!(path, Some("/"));
- /// ```
- #[inline]
- pub fn path_raw(&self) -> Option<&'c str> {
- match (self.path.as_ref(), self.cookie_string.as_ref()) {
- (Some(path), Some(string)) => path.to_raw_str(string),
- _ => None,
- }
- }
-
- /// Returns the `Domain` of `self` as a string slice of the raw string
- /// `self` was originally parsed from. If `self` was not originally parsed
- /// from a raw string, or if `self` doesn't contain a `Domain`, or if the
- /// `Domain` has changed since parsing, returns `None`.
- ///
- /// This method differs from [domain](#method.domain) in that it returns a
- /// string with the same lifetime as the originally parsed string. This
- /// lifetime may outlive `self` struct. If a longer lifetime is not
- /// required, or you're unsure if you need a longer lifetime, use
- /// [domain](#method.domain).
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::Cookie;
- ///
- /// let cookie_string = format!("{}={}; Domain=crates.io", "foo", "bar");
- ///
- /// //`c` will be dropped at the end of the scope, but `domain` will live on
- /// let domain = {
- /// let c = Cookie::parse(cookie_string.as_str()).unwrap();
- /// c.domain_raw()
- /// };
- ///
- /// assert_eq!(domain, Some("crates.io"));
- /// ```
- #[inline]
- pub fn domain_raw(&self) -> Option<&'c str> {
- match (self.domain.as_ref(), self.cookie_string.as_ref()) {
- (Some(domain), Some(string)) => domain.to_raw_str(string),
- _ => None,
- }
- }
-}
-
-/// Wrapper around `Cookie` whose `Display` implementation percent-encodes the
-/// cookie's name and value.
-///
-/// A value of this type can be obtained via the
-/// [encoded](struct.Cookie.html#method.encoded) method on
-/// [Cookie](struct.Cookie.html). This type should only be used for its
-/// `Display` implementation.
-///
-/// This type is only available when the `percent-encode` feature is enabled.
-///
-/// # Example
-///
-/// ```rust
-/// use actix_http::cookie::Cookie;
-///
-/// let mut c = Cookie::new("my name", "this; value?");
-/// assert_eq!(&c.encoded().to_string(), "my%20name=this%3B%20value%3F");
-/// ```
-pub struct EncodedCookie<'a, 'c>(&'a Cookie<'c>);
-
-impl<'a, 'c: 'a> fmt::Display for EncodedCookie<'a, 'c> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- // Percent-encode the name and value.
- let name = percent_encode(self.0.name().as_bytes(), USERINFO);
- let value = percent_encode(self.0.value().as_bytes(), USERINFO);
-
- // Write out the name/value pair and the cookie's parameters.
- write!(f, "{}={}", name, value)?;
- self.0.fmt_parameters(f)
- }
-}
-
-impl<'c> fmt::Display for Cookie<'c> {
- /// Formats the cookie `self` as a `Set-Cookie` header value.
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::Cookie;
- ///
- /// let mut cookie = Cookie::build("foo", "bar")
- /// .path("/")
- /// .finish();
- ///
- /// assert_eq!(&cookie.to_string(), "foo=bar; Path=/");
- /// ```
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "{}={}", self.name(), self.value())?;
- self.fmt_parameters(f)
- }
-}
-
-impl FromStr for Cookie<'static> {
- type Err = ParseError;
-
- fn from_str(s: &str) -> Result, ParseError> {
- Cookie::parse(s).map(|c| c.into_owned())
- }
-}
-
-impl<'a, 'b> PartialEq> for Cookie<'a> {
- fn eq(&self, other: &Cookie<'b>) -> bool {
- let so_far_so_good = self.name() == other.name()
- && self.value() == other.value()
- && self.http_only() == other.http_only()
- && self.secure() == other.secure()
- && self.max_age() == other.max_age()
- && self.expires() == other.expires();
-
- if !so_far_so_good {
- return false;
- }
-
- match (self.path(), other.path()) {
- (Some(a), Some(b)) if a.eq_ignore_ascii_case(b) => {}
- (None, None) => {}
- _ => return false,
- };
-
- match (self.domain(), other.domain()) {
- (Some(a), Some(b)) if a.eq_ignore_ascii_case(b) => {}
- (None, None) => {}
- _ => return false,
- };
-
- true
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::{Cookie, SameSite};
- use time::PrimitiveDateTime;
-
- #[test]
- fn format() {
- let cookie = Cookie::new("foo", "bar");
- assert_eq!(&cookie.to_string(), "foo=bar");
-
- let cookie = Cookie::build("foo", "bar").http_only(true).finish();
- assert_eq!(&cookie.to_string(), "foo=bar; HttpOnly");
-
- let cookie = Cookie::build("foo", "bar").max_age(10).finish();
- assert_eq!(&cookie.to_string(), "foo=bar; Max-Age=10");
-
- let cookie = Cookie::build("foo", "bar").secure(true).finish();
- assert_eq!(&cookie.to_string(), "foo=bar; Secure");
-
- let cookie = Cookie::build("foo", "bar").path("/").finish();
- assert_eq!(&cookie.to_string(), "foo=bar; Path=/");
-
- let cookie = Cookie::build("foo", "bar")
- .domain("www.rust-lang.org")
- .finish();
- assert_eq!(&cookie.to_string(), "foo=bar; Domain=www.rust-lang.org");
-
- let time_str = "Wed, 21 Oct 2015 07:28:00 GMT";
- let expires = PrimitiveDateTime::parse(time_str, "%a, %d %b %Y %H:%M:%S")
- .unwrap()
- .assume_utc();
- let cookie = Cookie::build("foo", "bar").expires(expires).finish();
- assert_eq!(
- &cookie.to_string(),
- "foo=bar; Expires=Wed, 21 Oct 2015 07:28:00 GMT"
- );
-
- let cookie = Cookie::build("foo", "bar")
- .same_site(SameSite::Strict)
- .finish();
- assert_eq!(&cookie.to_string(), "foo=bar; SameSite=Strict");
-
- let cookie = Cookie::build("foo", "bar")
- .same_site(SameSite::Lax)
- .finish();
- assert_eq!(&cookie.to_string(), "foo=bar; SameSite=Lax");
-
- let cookie = Cookie::build("foo", "bar")
- .same_site(SameSite::None)
- .finish();
- assert_eq!(&cookie.to_string(), "foo=bar; SameSite=None");
- }
-
- #[test]
- fn cookie_string_long_lifetimes() {
- let cookie_string =
- "bar=baz; Path=/subdir; HttpOnly; Domain=crates.io".to_owned();
- let (name, value, path, domain) = {
- // Create a cookie passing a slice
- let c = Cookie::parse(cookie_string.as_str()).unwrap();
- (c.name_raw(), c.value_raw(), c.path_raw(), c.domain_raw())
- };
-
- assert_eq!(name, Some("bar"));
- assert_eq!(value, Some("baz"));
- assert_eq!(path, Some("/subdir"));
- assert_eq!(domain, Some("crates.io"));
- }
-
- #[test]
- fn owned_cookie_string() {
- let cookie_string =
- "bar=baz; Path=/subdir; HttpOnly; Domain=crates.io".to_owned();
- let (name, value, path, domain) = {
- // Create a cookie passing an owned string
- let c = Cookie::parse(cookie_string).unwrap();
- (c.name_raw(), c.value_raw(), c.path_raw(), c.domain_raw())
- };
-
- assert_eq!(name, None);
- assert_eq!(value, None);
- assert_eq!(path, None);
- assert_eq!(domain, None);
- }
-
- #[test]
- fn owned_cookie_struct() {
- let cookie_string = "bar=baz; Path=/subdir; HttpOnly; Domain=crates.io";
- let (name, value, path, domain) = {
- // Create an owned cookie
- let c = Cookie::parse(cookie_string).unwrap().into_owned();
-
- (c.name_raw(), c.value_raw(), c.path_raw(), c.domain_raw())
- };
-
- assert_eq!(name, None);
- assert_eq!(value, None);
- assert_eq!(path, None);
- assert_eq!(domain, None);
- }
-
- #[test]
- fn format_encoded() {
- let cookie = Cookie::build("foo !?=", "bar;; a").finish();
- let cookie_str = cookie.encoded().to_string();
- assert_eq!(&cookie_str, "foo%20!%3F%3D=bar%3B%3B%20a");
-
- let cookie = Cookie::parse_encoded(cookie_str).unwrap();
- assert_eq!(cookie.name_value(), ("foo !?=", "bar;; a"));
- }
-}
diff --git a/actix-http/src/cookie/parse.rs b/actix-http/src/cookie/parse.rs
deleted file mode 100644
index d472b32b6..000000000
--- a/actix-http/src/cookie/parse.rs
+++ /dev/null
@@ -1,467 +0,0 @@
-use std::borrow::Cow;
-use std::cmp;
-use std::convert::From;
-use std::error::Error;
-use std::fmt;
-use std::str::Utf8Error;
-
-use percent_encoding::percent_decode;
-use time::Duration;
-
-use super::{Cookie, CookieStr, SameSite};
-
-use crate::time_parser;
-
-/// Enum corresponding to a parsing error.
-#[derive(Debug, PartialEq, Eq, Clone, Copy)]
-pub enum ParseError {
- /// The cookie did not contain a name/value pair.
- MissingPair,
- /// The cookie's name was empty.
- EmptyName,
- /// Decoding the cookie's name or value resulted in invalid UTF-8.
- Utf8Error(Utf8Error),
- /// It is discouraged to exhaustively match on this enum as its variants may
- /// grow without a breaking-change bump in version numbers.
- #[doc(hidden)]
- __Nonexhasutive,
-}
-
-impl ParseError {
- /// Returns a description of this error as a string
- pub fn as_str(&self) -> &'static str {
- match *self {
- ParseError::MissingPair => "the cookie is missing a name/value pair",
- ParseError::EmptyName => "the cookie's name is empty",
- ParseError::Utf8Error(_) => {
- "decoding the cookie's name or value resulted in invalid UTF-8"
- }
- ParseError::__Nonexhasutive => unreachable!("__Nonexhasutive ParseError"),
- }
- }
-}
-
-impl fmt::Display for ParseError {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "{}", self.as_str())
- }
-}
-
-impl From for ParseError {
- fn from(error: Utf8Error) -> ParseError {
- ParseError::Utf8Error(error)
- }
-}
-
-impl Error for ParseError {}
-
-fn indexes_of(needle: &str, haystack: &str) -> Option<(usize, usize)> {
- let haystack_start = haystack.as_ptr() as usize;
- let needle_start = needle.as_ptr() as usize;
-
- if needle_start < haystack_start {
- return None;
- }
-
- if (needle_start + needle.len()) > (haystack_start + haystack.len()) {
- return None;
- }
-
- let start = needle_start - haystack_start;
- let end = start + needle.len();
- Some((start, end))
-}
-
-fn name_val_decoded(
- name: &str,
- val: &str,
-) -> Result<(CookieStr, CookieStr), ParseError> {
- let decoded_name = percent_decode(name.as_bytes()).decode_utf8()?;
- let decoded_value = percent_decode(val.as_bytes()).decode_utf8()?;
- let name = CookieStr::Concrete(Cow::Owned(decoded_name.into_owned()));
- let val = CookieStr::Concrete(Cow::Owned(decoded_value.into_owned()));
-
- Ok((name, val))
-}
-
-// This function does the real parsing but _does not_ set the `cookie_string` in
-// the returned cookie object. This only exists so that the borrow to `s` is
-// returned at the end of the call, allowing the `cookie_string` field to be
-// set in the outer `parse` function.
-fn parse_inner<'c>(s: &str, decode: bool) -> Result, ParseError> {
- let mut attributes = s.split(';');
- let key_value = match attributes.next() {
- Some(s) => s,
- _ => panic!(),
- };
-
- // Determine the name = val.
- let (name, value) = match key_value.find('=') {
- Some(i) => (key_value[..i].trim(), key_value[(i + 1)..].trim()),
- None => return Err(ParseError::MissingPair),
- };
-
- if name.is_empty() {
- return Err(ParseError::EmptyName);
- }
-
- // Create a cookie with all of the defaults. We'll fill things in while we
- // iterate through the parameters below.
- let (name, value) = if decode {
- name_val_decoded(name, value)?
- } else {
- let name_indexes = indexes_of(name, s).expect("name sub");
- let value_indexes = indexes_of(value, s).expect("value sub");
- let name = CookieStr::Indexed(name_indexes.0, name_indexes.1);
- let value = CookieStr::Indexed(value_indexes.0, value_indexes.1);
-
- (name, value)
- };
-
- let mut cookie = Cookie {
- name,
- value,
- cookie_string: None,
- expires: None,
- max_age: None,
- domain: None,
- path: None,
- secure: None,
- http_only: None,
- same_site: None,
- };
-
- for attr in attributes {
- let (key, value) = match attr.find('=') {
- Some(i) => (attr[..i].trim(), Some(attr[(i + 1)..].trim())),
- None => (attr.trim(), None),
- };
-
- match (&*key.to_ascii_lowercase(), value) {
- ("secure", _) => cookie.secure = Some(true),
- ("httponly", _) => cookie.http_only = Some(true),
- ("max-age", Some(v)) => {
- // See RFC 6265 Section 5.2.2, negative values indicate that the
- // earliest possible expiration time should be used, so set the
- // max age as 0 seconds.
- cookie.max_age = match v.parse() {
- Ok(val) if val <= 0 => Some(Duration::zero()),
- Ok(val) => {
- // Don't panic if the max age seconds is greater than what's supported by
- // `Duration`.
- let val = cmp::min(val, Duration::max_value().whole_seconds());
- Some(Duration::seconds(val))
- }
- Err(_) => continue,
- };
- }
- ("domain", Some(mut domain)) if !domain.is_empty() => {
- if domain.starts_with('.') {
- domain = &domain[1..];
- }
-
- let (i, j) = indexes_of(domain, s).expect("domain sub");
- cookie.domain = Some(CookieStr::Indexed(i, j));
- }
- ("path", Some(v)) => {
- let (i, j) = indexes_of(v, s).expect("path sub");
- cookie.path = Some(CookieStr::Indexed(i, j));
- }
- ("samesite", Some(v)) => {
- if v.eq_ignore_ascii_case("strict") {
- cookie.same_site = Some(SameSite::Strict);
- } else if v.eq_ignore_ascii_case("lax") {
- cookie.same_site = Some(SameSite::Lax);
- } else if v.eq_ignore_ascii_case("none") {
- cookie.same_site = Some(SameSite::None);
- } else {
- // We do nothing here, for now. When/if the `SameSite`
- // attribute becomes standard, the spec says that we should
- // ignore this cookie, i.e, fail to parse it, when an
- // invalid value is passed in. The draft is at
- // http://httpwg.org/http-extensions/draft-ietf-httpbis-cookie-same-site.html.
- }
- }
- ("expires", Some(v)) => {
- // Try parsing with three date formats according to
- // http://tools.ietf.org/html/rfc2616#section-3.3.1. Try
- // additional ones as encountered in the real world.
- let tm = time_parser::parse_http_date(v)
- .or_else(|| time::parse(v, "%a, %d-%b-%Y %H:%M:%S").ok());
-
- if let Some(time) = tm {
- cookie.expires = Some(time.assume_utc())
- }
- }
- _ => {
- // We're going to be permissive here. If we have no idea what
- // this is, then it's something nonstandard. We're not going to
- // store it (because it's not compliant), but we're also not
- // going to emit an error.
- }
- }
- }
-
- Ok(cookie)
-}
-
-pub fn parse_cookie<'c, S>(cow: S, decode: bool) -> Result, ParseError>
-where
- S: Into>,
-{
- let s = cow.into();
- let mut cookie = parse_inner(&s, decode)?;
- cookie.cookie_string = Some(s);
- Ok(cookie)
-}
-
-#[cfg(test)]
-mod tests {
- use super::{Cookie, SameSite};
- use time::{Duration, PrimitiveDateTime};
-
- macro_rules! assert_eq_parse {
- ($string:expr, $expected:expr) => {
- let cookie = match Cookie::parse($string) {
- Ok(cookie) => cookie,
- Err(e) => panic!("Failed to parse {:?}: {:?}", $string, e),
- };
-
- assert_eq!(cookie, $expected);
- };
- }
-
- macro_rules! assert_ne_parse {
- ($string:expr, $expected:expr) => {
- let cookie = match Cookie::parse($string) {
- Ok(cookie) => cookie,
- Err(e) => panic!("Failed to parse {:?}: {:?}", $string, e),
- };
-
- assert_ne!(cookie, $expected);
- };
- }
-
- #[test]
- fn parse_same_site() {
- let expected = Cookie::build("foo", "bar")
- .same_site(SameSite::Lax)
- .finish();
-
- assert_eq_parse!("foo=bar; SameSite=Lax", expected);
- assert_eq_parse!("foo=bar; SameSite=lax", expected);
- assert_eq_parse!("foo=bar; SameSite=LAX", expected);
- assert_eq_parse!("foo=bar; samesite=Lax", expected);
- assert_eq_parse!("foo=bar; SAMESITE=Lax", expected);
-
- let expected = Cookie::build("foo", "bar")
- .same_site(SameSite::Strict)
- .finish();
-
- assert_eq_parse!("foo=bar; SameSite=Strict", expected);
- assert_eq_parse!("foo=bar; SameSITE=Strict", expected);
- assert_eq_parse!("foo=bar; SameSite=strict", expected);
- assert_eq_parse!("foo=bar; SameSite=STrICT", expected);
- assert_eq_parse!("foo=bar; SameSite=STRICT", expected);
-
- let expected = Cookie::build("foo", "bar")
- .same_site(SameSite::None)
- .finish();
-
- assert_eq_parse!("foo=bar; SameSite=None", expected);
- assert_eq_parse!("foo=bar; SameSITE=None", expected);
- assert_eq_parse!("foo=bar; SameSite=nOne", expected);
- assert_eq_parse!("foo=bar; SameSite=NoNE", expected);
- assert_eq_parse!("foo=bar; SameSite=NONE", expected);
- }
-
- #[test]
- fn parse() {
- assert!(Cookie::parse("bar").is_err());
- assert!(Cookie::parse("=bar").is_err());
- assert!(Cookie::parse(" =bar").is_err());
- assert!(Cookie::parse("foo=").is_ok());
-
- let expected = Cookie::build("foo", "bar=baz").finish();
- assert_eq_parse!("foo=bar=baz", expected);
-
- let mut expected = Cookie::build("foo", "bar").finish();
- assert_eq_parse!("foo=bar", expected);
- assert_eq_parse!("foo = bar", expected);
- assert_eq_parse!(" foo=bar ", expected);
- assert_eq_parse!(" foo=bar ;Domain=", expected);
- assert_eq_parse!(" foo=bar ;Domain= ", expected);
- assert_eq_parse!(" foo=bar ;Ignored", expected);
-
- let mut unexpected = Cookie::build("foo", "bar").http_only(false).finish();
- assert_ne_parse!(" foo=bar ;HttpOnly", unexpected);
- assert_ne_parse!(" foo=bar; httponly", unexpected);
-
- expected.set_http_only(true);
- assert_eq_parse!(" foo=bar ;HttpOnly", expected);
- assert_eq_parse!(" foo=bar ;httponly", expected);
- assert_eq_parse!(" foo=bar ;HTTPONLY=whatever", expected);
- assert_eq_parse!(" foo=bar ; sekure; HTTPONLY", expected);
-
- expected.set_secure(true);
- assert_eq_parse!(" foo=bar ;HttpOnly; Secure", expected);
- assert_eq_parse!(" foo=bar ;HttpOnly; Secure=aaaa", expected);
-
- unexpected.set_http_only(true);
- unexpected.set_secure(true);
- assert_ne_parse!(" foo=bar ;HttpOnly; skeure", unexpected);
- assert_ne_parse!(" foo=bar ;HttpOnly; =secure", unexpected);
- assert_ne_parse!(" foo=bar ;HttpOnly;", unexpected);
-
- unexpected.set_secure(false);
- assert_ne_parse!(" foo=bar ;HttpOnly; secure", unexpected);
- assert_ne_parse!(" foo=bar ;HttpOnly; secure", unexpected);
- assert_ne_parse!(" foo=bar ;HttpOnly; secure", unexpected);
-
- expected.set_max_age(Duration::zero());
- assert_eq_parse!(" foo=bar ;HttpOnly; Secure; Max-Age=0", expected);
- assert_eq_parse!(" foo=bar ;HttpOnly; Secure; Max-Age = 0 ", expected);
- assert_eq_parse!(" foo=bar ;HttpOnly; Secure; Max-Age=-1", expected);
- assert_eq_parse!(" foo=bar ;HttpOnly; Secure; Max-Age = -1 ", expected);
-
- expected.set_max_age(Duration::minutes(1));
- assert_eq_parse!(" foo=bar ;HttpOnly; Secure; Max-Age=60", expected);
- assert_eq_parse!(" foo=bar ;HttpOnly; Secure; Max-Age = 60 ", expected);
-
- expected.set_max_age(Duration::seconds(4));
- assert_eq_parse!(" foo=bar ;HttpOnly; Secure; Max-Age=4", expected);
- assert_eq_parse!(" foo=bar ;HttpOnly; Secure; Max-Age = 4 ", expected);
-
- unexpected.set_secure(true);
- unexpected.set_max_age(Duration::minutes(1));
- assert_ne_parse!(" foo=bar ;HttpOnly; Secure; Max-Age=122", unexpected);
- assert_ne_parse!(" foo=bar ;HttpOnly; Secure; Max-Age = 38 ", unexpected);
- assert_ne_parse!(" foo=bar ;HttpOnly; Secure; Max-Age=51", unexpected);
- assert_ne_parse!(" foo=bar ;HttpOnly; Secure; Max-Age = -1 ", unexpected);
- assert_ne_parse!(" foo=bar ;HttpOnly; Secure; Max-Age = 0", unexpected);
-
- expected.set_path("/");
- assert_eq_parse!("foo=bar;HttpOnly; Secure; Max-Age=4; Path=/", expected);
- assert_eq_parse!("foo=bar;HttpOnly; Secure; Max-Age=4;Path=/", expected);
-
- expected.set_path("/foo");
- assert_eq_parse!("foo=bar;HttpOnly; Secure; Max-Age=4; Path=/foo", expected);
- assert_eq_parse!("foo=bar;HttpOnly; Secure; Max-Age=4;Path=/foo", expected);
- assert_eq_parse!("foo=bar;HttpOnly; Secure; Max-Age=4;path=/foo", expected);
- assert_eq_parse!("foo=bar;HttpOnly; Secure; Max-Age=4;path = /foo", expected);
-
- unexpected.set_max_age(Duration::seconds(4));
- unexpected.set_path("/bar");
- assert_ne_parse!("foo=bar;HttpOnly; Secure; Max-Age=4; Path=/foo", unexpected);
- assert_ne_parse!("foo=bar;HttpOnly; Secure; Max-Age=4;Path=/baz", unexpected);
-
- expected.set_domain("www.foo.com");
- assert_eq_parse!(
- " foo=bar ;HttpOnly; Secure; Max-Age=4; Path=/foo; \
- Domain=www.foo.com",
- expected
- );
-
- expected.set_domain("foo.com");
- assert_eq_parse!(
- " foo=bar ;HttpOnly; Secure; Max-Age=4; Path=/foo; \
- Domain=foo.com",
- expected
- );
- assert_eq_parse!(
- " foo=bar ;HttpOnly; Secure; Max-Age=4; Path=/foo; \
- Domain=FOO.COM",
- expected
- );
-
- unexpected.set_path("/foo");
- unexpected.set_domain("bar.com");
- assert_ne_parse!(
- " foo=bar ;HttpOnly; Secure; Max-Age=4; Path=/foo; \
- Domain=foo.com",
- unexpected
- );
- assert_ne_parse!(
- " foo=bar ;HttpOnly; Secure; Max-Age=4; Path=/foo; \
- Domain=FOO.COM",
- unexpected
- );
-
- let time_str = "Wed, 21 Oct 2015 07:28:00 GMT";
- let expires = PrimitiveDateTime::parse(time_str, "%a, %d %b %Y %H:%M:%S")
- .unwrap()
- .assume_utc();
- expected.set_expires(expires);
- assert_eq_parse!(
- " foo=bar ;HttpOnly; Secure; Max-Age=4; Path=/foo; \
- Domain=foo.com; Expires=Wed, 21 Oct 2015 07:28:00 GMT",
- expected
- );
-
- unexpected.set_domain("foo.com");
- let bad_expires = PrimitiveDateTime::parse(time_str, "%a, %d %b %Y %H:%S:%M")
- .unwrap()
- .assume_utc();
- expected.set_expires(bad_expires);
- assert_ne_parse!(
- " foo=bar ;HttpOnly; Secure; Max-Age=4; Path=/foo; \
- Domain=foo.com; Expires=Wed, 21 Oct 2015 07:28:00 GMT",
- unexpected
- );
-
- expected.set_expires(expires);
- expected.set_same_site(SameSite::Lax);
- assert_eq_parse!(
- " foo=bar ;HttpOnly; Secure; Max-Age=4; Path=/foo; \
- Domain=foo.com; Expires=Wed, 21 Oct 2015 07:28:00 GMT; \
- SameSite=Lax",
- expected
- );
- expected.set_same_site(SameSite::Strict);
- assert_eq_parse!(
- " foo=bar ;HttpOnly; Secure; Max-Age=4; Path=/foo; \
- Domain=foo.com; Expires=Wed, 21 Oct 2015 07:28:00 GMT; \
- SameSite=Strict",
- expected
- );
- expected.set_same_site(SameSite::None);
- assert_eq_parse!(
- " foo=bar ;HttpOnly; Secure; Max-Age=4; Path=/foo; \
- Domain=foo.com; Expires=Wed, 21 Oct 2015 07:28:00 GMT; \
- SameSite=None",
- expected
- );
- }
-
- #[test]
- fn odd_characters() {
- let expected = Cookie::new("foo", "b%2Fr");
- assert_eq_parse!("foo=b%2Fr", expected);
- }
-
- #[test]
- fn odd_characters_encoded() {
- let expected = Cookie::new("foo", "b/r");
- let cookie = match Cookie::parse_encoded("foo=b%2Fr") {
- Ok(cookie) => cookie,
- Err(e) => panic!("Failed to parse: {:?}", e),
- };
-
- assert_eq!(cookie, expected);
- }
-
- #[test]
- fn do_not_panic_on_large_max_ages() {
- let max_duration = Duration::max_value();
- let expected = Cookie::build("foo", "bar")
- .max_age_time(max_duration)
- .finish();
- let overflow_duration = max_duration
- .checked_add(Duration::nanoseconds(1))
- .unwrap_or(max_duration);
- assert_eq_parse!(
- format!(" foo=bar; Max-Age={:?}", overflow_duration.whole_seconds()),
- expected
- );
- }
-}
diff --git a/actix-http/src/cookie/secure/key.rs b/actix-http/src/cookie/secure/key.rs
deleted file mode 100644
index 41413921f..000000000
--- a/actix-http/src/cookie/secure/key.rs
+++ /dev/null
@@ -1,190 +0,0 @@
-use ring::hkdf::{Algorithm, KeyType, Prk, HKDF_SHA256};
-use ring::rand::{SecureRandom, SystemRandom};
-
-use super::private::KEY_LEN as PRIVATE_KEY_LEN;
-use super::signed::KEY_LEN as SIGNED_KEY_LEN;
-
-static HKDF_DIGEST: Algorithm = HKDF_SHA256;
-const KEYS_INFO: &[&[u8]] = &[b"COOKIE;SIGNED:HMAC-SHA256;PRIVATE:AEAD-AES-256-GCM"];
-
-/// A cryptographic master key for use with `Signed` and/or `Private` jars.
-///
-/// This structure encapsulates secure, cryptographic keys for use with both
-/// [PrivateJar](struct.PrivateJar.html) and [SignedJar](struct.SignedJar.html).
-/// It can be derived from a single master key via
-/// [from_master](#method.from_master) or generated from a secure random source
-/// via [generate](#method.generate). A single instance of `Key` can be used for
-/// both a `PrivateJar` and a `SignedJar`.
-///
-/// This type is only available when the `secure` feature is enabled.
-#[derive(Clone)]
-pub struct Key {
- signing_key: [u8; SIGNED_KEY_LEN],
- encryption_key: [u8; PRIVATE_KEY_LEN],
-}
-
-impl KeyType for &Key {
- #[inline]
- fn len(&self) -> usize {
- SIGNED_KEY_LEN + PRIVATE_KEY_LEN
- }
-}
-
-impl Key {
- /// Derives new signing/encryption keys from a master key.
- ///
- /// The master key must be at least 256-bits (32 bytes). For security, the
- /// master key _must_ be cryptographically random. The keys are derived
- /// deterministically from the master key.
- ///
- /// # Panics
- ///
- /// Panics if `key` is less than 32 bytes in length.
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::Key;
- ///
- /// # /*
- /// let master_key = { /* a cryptographically random key >= 32 bytes */ };
- /// # */
- /// # let master_key: &Vec = &(0..32).collect();
- ///
- /// let key = Key::from_master(master_key);
- /// ```
- pub fn from_master(key: &[u8]) -> Key {
- if key.len() < 32 {
- panic!(
- "bad master key length: expected at least 32 bytes, found {}",
- key.len()
- );
- }
-
- // An empty `Key` structure; will be filled in with HKDF derived keys.
- let mut output_key = Key {
- signing_key: [0; SIGNED_KEY_LEN],
- encryption_key: [0; PRIVATE_KEY_LEN],
- };
-
- // Expand the master key into two HKDF generated keys.
- let mut both_keys = [0; SIGNED_KEY_LEN + PRIVATE_KEY_LEN];
- let prk = Prk::new_less_safe(HKDF_DIGEST, key);
- let okm = prk.expand(KEYS_INFO, &output_key).expect("okm expand");
- okm.fill(&mut both_keys).expect("fill keys");
-
- // Copy the key parts into their respective fields.
- output_key
- .signing_key
- .copy_from_slice(&both_keys[..SIGNED_KEY_LEN]);
- output_key
- .encryption_key
- .copy_from_slice(&both_keys[SIGNED_KEY_LEN..]);
- output_key
- }
-
- /// Generates signing/encryption keys from a secure, random source. Keys are
- /// generated non-deterministically.
- ///
- /// # Panics
- ///
- /// Panics if randomness cannot be retrieved from the operating system. See
- /// [try_generate](#method.try_generate) for a non-panicking version.
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::Key;
- ///
- /// let key = Key::generate();
- /// ```
- pub fn generate() -> Key {
- Self::try_generate().expect("failed to generate `Key` from randomness")
- }
-
- /// Attempts to generate signing/encryption keys from a secure, random
- /// source. Keys are generated non-deterministically. If randomness cannot be
- /// retrieved from the underlying operating system, returns `None`.
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::Key;
- ///
- /// let key = Key::try_generate();
- /// ```
- pub fn try_generate() -> Option {
- let mut sign_key = [0; SIGNED_KEY_LEN];
- let mut enc_key = [0; PRIVATE_KEY_LEN];
-
- let rng = SystemRandom::new();
- if rng.fill(&mut sign_key).is_err() || rng.fill(&mut enc_key).is_err() {
- return None;
- }
-
- Some(Key {
- signing_key: sign_key,
- encryption_key: enc_key,
- })
- }
-
- /// Returns the raw bytes of a key suitable for signing cookies.
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::Key;
- ///
- /// let key = Key::generate();
- /// let signing_key = key.signing();
- /// ```
- pub fn signing(&self) -> &[u8] {
- &self.signing_key[..]
- }
-
- /// Returns the raw bytes of a key suitable for encrypting cookies.
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::Key;
- ///
- /// let key = Key::generate();
- /// let encryption_key = key.encryption();
- /// ```
- pub fn encryption(&self) -> &[u8] {
- &self.encryption_key[..]
- }
-}
-
-#[cfg(test)]
-mod test {
- use super::Key;
-
- #[test]
- fn deterministic_from_master() {
- let master_key: Vec = (0..32).collect();
-
- let key_a = Key::from_master(&master_key);
- let key_b = Key::from_master(&master_key);
-
- assert_eq!(key_a.signing(), key_b.signing());
- assert_eq!(key_a.encryption(), key_b.encryption());
- assert_ne!(key_a.encryption(), key_a.signing());
-
- let master_key_2: Vec = (32..64).collect();
- let key_2 = Key::from_master(&master_key_2);
-
- assert_ne!(key_2.signing(), key_a.signing());
- assert_ne!(key_2.encryption(), key_a.encryption());
- }
-
- #[test]
- fn non_deterministic_generate() {
- let key_a = Key::generate();
- let key_b = Key::generate();
-
- assert_ne!(key_a.signing(), key_b.signing());
- assert_ne!(key_a.encryption(), key_b.encryption());
- }
-}
diff --git a/actix-http/src/cookie/secure/macros.rs b/actix-http/src/cookie/secure/macros.rs
deleted file mode 100644
index 089047c4e..000000000
--- a/actix-http/src/cookie/secure/macros.rs
+++ /dev/null
@@ -1,40 +0,0 @@
-#[cfg(test)]
-macro_rules! assert_simple_behaviour {
- ($clear:expr, $secure:expr) => {{
- assert_eq!($clear.iter().count(), 0);
-
- $secure.add(Cookie::new("name", "val"));
- assert_eq!($clear.iter().count(), 1);
- assert_eq!($secure.get("name").unwrap().value(), "val");
- assert_ne!($clear.get("name").unwrap().value(), "val");
-
- $secure.add(Cookie::new("another", "two"));
- assert_eq!($clear.iter().count(), 2);
-
- $clear.remove(Cookie::named("another"));
- assert_eq!($clear.iter().count(), 1);
-
- $secure.remove(Cookie::named("name"));
- assert_eq!($clear.iter().count(), 0);
- }};
-}
-
-#[cfg(test)]
-macro_rules! assert_secure_behaviour {
- ($clear:expr, $secure:expr) => {{
- $secure.add(Cookie::new("secure", "secure"));
- assert!($clear.get("secure").unwrap().value() != "secure");
- assert!($secure.get("secure").unwrap().value() == "secure");
-
- let mut cookie = $clear.get("secure").unwrap().clone();
- let new_val = format!("{}l", cookie.value());
- cookie.set_value(new_val);
- $clear.add(cookie);
- assert!($secure.get("secure").is_none());
-
- let mut cookie = $clear.get("secure").unwrap().clone();
- cookie.set_value("foobar");
- $clear.add(cookie);
- assert!($secure.get("secure").is_none());
- }};
-}
diff --git a/actix-http/src/cookie/secure/mod.rs b/actix-http/src/cookie/secure/mod.rs
deleted file mode 100644
index e0fba9733..000000000
--- a/actix-http/src/cookie/secure/mod.rs
+++ /dev/null
@@ -1,10 +0,0 @@
-//! Fork of https://github.com/alexcrichton/cookie-rs
-#[macro_use]
-mod macros;
-mod key;
-mod private;
-mod signed;
-
-pub use self::key::*;
-pub use self::private::*;
-pub use self::signed::*;
diff --git a/actix-http/src/cookie/secure/private.rs b/actix-http/src/cookie/secure/private.rs
deleted file mode 100644
index f05e23800..000000000
--- a/actix-http/src/cookie/secure/private.rs
+++ /dev/null
@@ -1,275 +0,0 @@
-use std::str;
-
-use log::warn;
-use ring::aead::{Aad, Algorithm, Nonce, AES_256_GCM};
-use ring::aead::{LessSafeKey, UnboundKey};
-use ring::rand::{SecureRandom, SystemRandom};
-
-use super::Key;
-use crate::cookie::{Cookie, CookieJar};
-
-// Keep these in sync, and keep the key len synced with the `private` docs as
-// well as the `KEYS_INFO` const in secure::Key.
-static ALGO: &Algorithm = &AES_256_GCM;
-const NONCE_LEN: usize = 12;
-pub const KEY_LEN: usize = 32;
-
-/// A child cookie jar that provides authenticated encryption for its cookies.
-///
-/// A _private_ child jar signs and encrypts all the cookies added to it and
-/// verifies and decrypts cookies retrieved from it. Any cookies stored in a
-/// `PrivateJar` are simultaneously assured confidentiality, integrity, and
-/// authenticity. In other words, clients cannot discover nor tamper with the
-/// contents of a cookie, nor can they fabricate cookie data.
-///
-/// This type is only available when the `secure` feature is enabled.
-pub struct PrivateJar<'a> {
- parent: &'a mut CookieJar,
- key: [u8; KEY_LEN],
-}
-
-impl<'a> PrivateJar<'a> {
- /// Creates a new child `PrivateJar` with parent `parent` and key `key`.
- /// This method is typically called indirectly via the `signed` method of
- /// `CookieJar`.
- #[doc(hidden)]
- pub fn new(parent: &'a mut CookieJar, key: &Key) -> PrivateJar<'a> {
- let mut key_array = [0u8; KEY_LEN];
- key_array.copy_from_slice(key.encryption());
- PrivateJar {
- parent,
- key: key_array,
- }
- }
-
- /// Given a sealed value `str` and a key name `name`, where the nonce is
- /// prepended to the original value and then both are Base64 encoded,
- /// verifies and decrypts the sealed value and returns it. If there's a
- /// problem, returns an `Err` with a string describing the issue.
- fn unseal(&self, name: &str, value: &str) -> Result {
- let mut data = base64::decode(value).map_err(|_| "bad base64 value")?;
- if data.len() <= NONCE_LEN {
- return Err("length of decoded data is <= NONCE_LEN");
- }
-
- let ad = Aad::from(name.as_bytes());
- let key = LessSafeKey::new(
- UnboundKey::new(&ALGO, &self.key).expect("matching key length"),
- );
- let (nonce, mut sealed) = data.split_at_mut(NONCE_LEN);
- let nonce =
- Nonce::try_assume_unique_for_key(nonce).expect("invalid length of `nonce`");
- let unsealed = key
- .open_in_place(nonce, ad, &mut sealed)
- .map_err(|_| "invalid key/nonce/value: bad seal")?;
-
- if let Ok(unsealed_utf8) = str::from_utf8(unsealed) {
- Ok(unsealed_utf8.to_string())
- } else {
- warn!(
- "Private cookie does not have utf8 content!
-It is likely the secret key used to encrypt them has been leaked.
-Please change it as soon as possible."
- );
- Err("bad unsealed utf8")
- }
- }
-
- /// Returns a reference to the `Cookie` inside this jar with the name `name`
- /// and authenticates and decrypts the cookie's value, returning a `Cookie`
- /// with the decrypted value. If the cookie cannot be found, or the cookie
- /// fails to authenticate or decrypt, `None` is returned.
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::{CookieJar, Cookie, Key};
- ///
- /// let key = Key::generate();
- /// let mut jar = CookieJar::new();
- /// let mut private_jar = jar.private(&key);
- /// assert!(private_jar.get("name").is_none());
- ///
- /// private_jar.add(Cookie::new("name", "value"));
- /// assert_eq!(private_jar.get("name").unwrap().value(), "value");
- /// ```
- pub fn get(&self, name: &str) -> Option> {
- if let Some(cookie_ref) = self.parent.get(name) {
- let mut cookie = cookie_ref.clone();
- if let Ok(value) = self.unseal(name, cookie.value()) {
- cookie.set_value(value);
- return Some(cookie);
- }
- }
-
- None
- }
-
- /// Adds `cookie` to the parent jar. The cookie's value is encrypted with
- /// authenticated encryption assuring confidentiality, integrity, and
- /// authenticity.
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::{CookieJar, Cookie, Key};
- ///
- /// let key = Key::generate();
- /// let mut jar = CookieJar::new();
- /// jar.private(&key).add(Cookie::new("name", "value"));
- ///
- /// assert_ne!(jar.get("name").unwrap().value(), "value");
- /// assert_eq!(jar.private(&key).get("name").unwrap().value(), "value");
- /// ```
- pub fn add(&mut self, mut cookie: Cookie<'static>) {
- self.encrypt_cookie(&mut cookie);
-
- // Add the sealed cookie to the parent.
- self.parent.add(cookie);
- }
-
- /// Adds an "original" `cookie` to parent jar. The cookie's value is
- /// encrypted with authenticated encryption assuring confidentiality,
- /// integrity, and authenticity. Adding an original cookie does not affect
- /// the [`CookieJar::delta()`](struct.CookieJar.html#method.delta)
- /// computation. This method is intended to be used to seed the cookie jar
- /// with cookies received from a client's HTTP message.
- ///
- /// For accurate `delta` computations, this method should not be called
- /// after calling `remove`.
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::{CookieJar, Cookie, Key};
- ///
- /// let key = Key::generate();
- /// let mut jar = CookieJar::new();
- /// jar.private(&key).add_original(Cookie::new("name", "value"));
- ///
- /// assert_eq!(jar.iter().count(), 1);
- /// assert_eq!(jar.delta().count(), 0);
- /// ```
- pub fn add_original(&mut self, mut cookie: Cookie<'static>) {
- self.encrypt_cookie(&mut cookie);
-
- // Add the sealed cookie to the parent.
- self.parent.add_original(cookie);
- }
-
- /// Encrypts the cookie's value with
- /// authenticated encryption assuring confidentiality, integrity, and authenticity.
- fn encrypt_cookie(&self, cookie: &mut Cookie<'_>) {
- let name = cookie.name().as_bytes();
- let value = cookie.value().as_bytes();
- let data = encrypt_name_value(name, value, &self.key);
-
- // Base64 encode the nonce and encrypted value.
- let sealed_value = base64::encode(&data);
- cookie.set_value(sealed_value);
- }
-
- /// Removes `cookie` from the parent jar.
- ///
- /// For correct removal, the passed in `cookie` must contain the same `path`
- /// and `domain` as the cookie that was initially set.
- ///
- /// See [CookieJar::remove](struct.CookieJar.html#method.remove) for more
- /// details.
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::{CookieJar, Cookie, Key};
- ///
- /// let key = Key::generate();
- /// let mut jar = CookieJar::new();
- /// let mut private_jar = jar.private(&key);
- ///
- /// private_jar.add(Cookie::new("name", "value"));
- /// assert!(private_jar.get("name").is_some());
- ///
- /// private_jar.remove(Cookie::named("name"));
- /// assert!(private_jar.get("name").is_none());
- /// ```
- pub fn remove(&mut self, cookie: Cookie<'static>) {
- self.parent.remove(cookie);
- }
-}
-
-fn encrypt_name_value(name: &[u8], value: &[u8], key: &[u8]) -> Vec {
- // Create the `SealingKey` structure.
- let unbound = UnboundKey::new(&ALGO, key).expect("matching key length");
- let key = LessSafeKey::new(unbound);
-
- // Create a vec to hold the [nonce | cookie value | overhead].
- let mut data = vec![0; NONCE_LEN + value.len() + ALGO.tag_len()];
-
- // Randomly generate the nonce, then copy the cookie value as input.
- let (nonce, in_out) = data.split_at_mut(NONCE_LEN);
- let (in_out, tag) = in_out.split_at_mut(value.len());
- in_out.copy_from_slice(value);
-
- // Randomly generate the nonce into the nonce piece.
- SystemRandom::new()
- .fill(nonce)
- .expect("couldn't random fill nonce");
- let nonce = Nonce::try_assume_unique_for_key(nonce).expect("invalid `nonce` length");
-
- // Use cookie's name as associated data to prevent value swapping.
- let ad = Aad::from(name);
- let ad_tag = key
- .seal_in_place_separate_tag(nonce, ad, in_out)
- .expect("in-place seal");
-
- // Copy the tag into the tag piece.
- tag.copy_from_slice(ad_tag.as_ref());
-
- // Remove the overhead and return the sealed content.
- data
-}
-
-#[cfg(test)]
-mod test {
- use super::{encrypt_name_value, Cookie, CookieJar, Key};
-
- #[test]
- fn simple() {
- let key = Key::generate();
- let mut jar = CookieJar::new();
- assert_simple_behaviour!(jar, jar.private(&key));
- }
-
- #[test]
- fn private() {
- let key = Key::generate();
- let mut jar = CookieJar::new();
- assert_secure_behaviour!(jar, jar.private(&key));
- }
-
- #[test]
- fn non_utf8() {
- let key = Key::generate();
- let mut jar = CookieJar::new();
-
- let name = "malicious";
- let mut assert_non_utf8 = |value: &[u8]| {
- let sealed = encrypt_name_value(name.as_bytes(), value, &key.encryption());
- let encoded = base64::encode(&sealed);
- assert_eq!(
- jar.private(&key).unseal(name, &encoded),
- Err("bad unsealed utf8")
- );
- jar.add(Cookie::new(name, encoded));
- assert_eq!(jar.private(&key).get(name), None);
- };
-
- assert_non_utf8(&[0x72, 0xfb, 0xdf, 0x74]); // rûst in ISO/IEC 8859-1
-
- let mut malicious =
- String::from(r#"{"id":"abc123??%X","admin":true}"#).into_bytes();
- malicious[8] |= 0b1100_0000;
- malicious[9] |= 0b1100_0000;
- assert_non_utf8(&malicious);
- }
-}
diff --git a/actix-http/src/cookie/secure/signed.rs b/actix-http/src/cookie/secure/signed.rs
deleted file mode 100644
index 64e8d5dda..000000000
--- a/actix-http/src/cookie/secure/signed.rs
+++ /dev/null
@@ -1,184 +0,0 @@
-use ring::hmac::{self, sign, verify};
-
-use super::Key;
-use crate::cookie::{Cookie, CookieJar};
-
-// Keep these in sync, and keep the key len synced with the `signed` docs as
-// well as the `KEYS_INFO` const in secure::Key.
-static HMAC_DIGEST: hmac::Algorithm = hmac::HMAC_SHA256;
-const BASE64_DIGEST_LEN: usize = 44;
-pub const KEY_LEN: usize = 32;
-
-/// A child cookie jar that authenticates its cookies.
-///
-/// A _signed_ child jar signs all the cookies added to it and verifies cookies
-/// retrieved from it. Any cookies stored in a `SignedJar` are assured integrity
-/// and authenticity. In other words, clients cannot tamper with the contents of
-/// a cookie nor can they fabricate cookie values, but the data is visible in
-/// plaintext.
-///
-/// This type is only available when the `secure` feature is enabled.
-pub struct SignedJar<'a> {
- parent: &'a mut CookieJar,
- key: hmac::Key,
-}
-
-impl<'a> SignedJar<'a> {
- /// Creates a new child `SignedJar` with parent `parent` and key `key`. This
- /// method is typically called indirectly via the `signed` method of
- /// `CookieJar`.
- #[doc(hidden)]
- pub fn new(parent: &'a mut CookieJar, key: &Key) -> SignedJar<'a> {
- SignedJar {
- parent,
- key: hmac::Key::new(HMAC_DIGEST, key.signing()),
- }
- }
-
- /// Given a signed value `str` where the signature is prepended to `value`,
- /// verifies the signed value and returns it. If there's a problem, returns
- /// an `Err` with a string describing the issue.
- fn verify(&self, cookie_value: &str) -> Result {
- if cookie_value.len() < BASE64_DIGEST_LEN {
- return Err("length of value is <= BASE64_DIGEST_LEN");
- }
-
- let (digest_str, value) = cookie_value.split_at(BASE64_DIGEST_LEN);
- let sig = base64::decode(digest_str).map_err(|_| "bad base64 digest")?;
-
- verify(&self.key, value.as_bytes(), &sig)
- .map(|_| value.to_string())
- .map_err(|_| "value did not verify")
- }
-
- /// Returns a reference to the `Cookie` inside this jar with the name `name`
- /// and verifies the authenticity and integrity of the cookie's value,
- /// returning a `Cookie` with the authenticated value. If the cookie cannot
- /// be found, or the cookie fails to verify, `None` is returned.
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::{CookieJar, Cookie, Key};
- ///
- /// let key = Key::generate();
- /// let mut jar = CookieJar::new();
- /// let mut signed_jar = jar.signed(&key);
- /// assert!(signed_jar.get("name").is_none());
- ///
- /// signed_jar.add(Cookie::new("name", "value"));
- /// assert_eq!(signed_jar.get("name").unwrap().value(), "value");
- /// ```
- pub fn get(&self, name: &str) -> Option> {
- if let Some(cookie_ref) = self.parent.get(name) {
- let mut cookie = cookie_ref.clone();
- if let Ok(value) = self.verify(cookie.value()) {
- cookie.set_value(value);
- return Some(cookie);
- }
- }
-
- None
- }
-
- /// Adds `cookie` to the parent jar. The cookie's value is signed assuring
- /// integrity and authenticity.
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::{CookieJar, Cookie, Key};
- ///
- /// let key = Key::generate();
- /// let mut jar = CookieJar::new();
- /// jar.signed(&key).add(Cookie::new("name", "value"));
- ///
- /// assert_ne!(jar.get("name").unwrap().value(), "value");
- /// assert!(jar.get("name").unwrap().value().contains("value"));
- /// assert_eq!(jar.signed(&key).get("name").unwrap().value(), "value");
- /// ```
- pub fn add(&mut self, mut cookie: Cookie<'static>) {
- self.sign_cookie(&mut cookie);
- self.parent.add(cookie);
- }
-
- /// Adds an "original" `cookie` to this jar. The cookie's value is signed
- /// assuring integrity and authenticity. Adding an original cookie does not
- /// affect the [`CookieJar::delta()`](struct.CookieJar.html#method.delta)
- /// computation. This method is intended to be used to seed the cookie jar
- /// with cookies received from a client's HTTP message.
- ///
- /// For accurate `delta` computations, this method should not be called
- /// after calling `remove`.
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::{CookieJar, Cookie, Key};
- ///
- /// let key = Key::generate();
- /// let mut jar = CookieJar::new();
- /// jar.signed(&key).add_original(Cookie::new("name", "value"));
- ///
- /// assert_eq!(jar.iter().count(), 1);
- /// assert_eq!(jar.delta().count(), 0);
- /// ```
- pub fn add_original(&mut self, mut cookie: Cookie<'static>) {
- self.sign_cookie(&mut cookie);
- self.parent.add_original(cookie);
- }
-
- /// Signs the cookie's value assuring integrity and authenticity.
- fn sign_cookie(&self, cookie: &mut Cookie<'_>) {
- let digest = sign(&self.key, cookie.value().as_bytes());
- let mut new_value = base64::encode(digest.as_ref());
- new_value.push_str(cookie.value());
- cookie.set_value(new_value);
- }
-
- /// Removes `cookie` from the parent jar.
- ///
- /// For correct removal, the passed in `cookie` must contain the same `path`
- /// and `domain` as the cookie that was initially set.
- ///
- /// See [CookieJar::remove](struct.CookieJar.html#method.remove) for more
- /// details.
- ///
- /// # Example
- ///
- /// ```rust
- /// use actix_http::cookie::{CookieJar, Cookie, Key};
- ///
- /// let key = Key::generate();
- /// let mut jar = CookieJar::new();
- /// let mut signed_jar = jar.signed(&key);
- ///
- /// signed_jar.add(Cookie::new("name", "value"));
- /// assert!(signed_jar.get("name").is_some());
- ///
- /// signed_jar.remove(Cookie::named("name"));
- /// assert!(signed_jar.get("name").is_none());
- /// ```
- pub fn remove(&mut self, cookie: Cookie<'static>) {
- self.parent.remove(cookie);
- }
-}
-
-#[cfg(test)]
-mod test {
- use super::{Cookie, CookieJar, Key};
-
- #[test]
- fn simple() {
- let key = Key::generate();
- let mut jar = CookieJar::new();
- assert_simple_behaviour!(jar, jar.signed(&key));
- }
-
- #[test]
- fn private() {
- let key = Key::generate();
- let mut jar = CookieJar::new();
- assert_secure_behaviour!(jar, jar.signed(&key));
- }
-}
diff --git a/actix-http/src/lib.rs b/actix-http/src/lib.rs
index 9f615a129..dd8f5ee12 100644
--- a/actix-http/src/lib.rs
+++ b/actix-http/src/lib.rs
@@ -32,7 +32,7 @@ mod response;
mod service;
mod time_parser;
-pub mod cookie;
+pub use cookie;
pub mod error;
pub mod h1;
pub mod h2;
diff --git a/actix-http/src/response.rs b/actix-http/src/response.rs
index 9086212f1..2def67168 100644
--- a/actix-http/src/response.rs
+++ b/actix-http/src/response.rs
@@ -877,7 +877,7 @@ mod tests {
.domain("www.rust-lang.org")
.path("/test")
.http_only(true)
- .max_age_time(time::Duration::days(1))
+ .max_age(time::Duration::days(1))
.finish(),
)
.del_cookie(&cookies[1])
diff --git a/actix-http/src/test.rs b/actix-http/src/test.rs
index 061ba610f..b79f5a73c 100644
--- a/actix-http/src/test.rs
+++ b/actix-http/src/test.rs
@@ -1,6 +1,5 @@
//! Test Various helpers for Actix applications to use during testing.
use std::convert::TryFrom;
-use std::fmt::Write as FmtWrite;
use std::io::{self, Read, Write};
use std::pin::Pin;
use std::str::FromStr;
@@ -10,9 +9,8 @@ use actix_codec::{AsyncRead, AsyncWrite};
use bytes::{Bytes, BytesMut};
use http::header::{self, HeaderName, HeaderValue};
use http::{Error as HttpError, Method, Uri, Version};
-use percent_encoding::percent_encode;
-use crate::cookie::{Cookie, CookieJar, USERINFO};
+use crate::cookie::{Cookie, CookieJar};
use crate::header::HeaderMap;
use crate::header::{Header, IntoHeaderValue};
use crate::payload::Payload;
@@ -163,17 +161,17 @@ impl TestRequest {
head.version = inner.version;
head.headers = inner.headers;
- let mut cookie = String::new();
- for c in inner.cookies.delta() {
- let name = percent_encode(c.name().as_bytes(), USERINFO);
- let value = percent_encode(c.value().as_bytes(), USERINFO);
- let _ = write!(&mut cookie, "; {}={}", name, value);
- }
+ let cookie: String = inner
+ .cookies
+ .delta()
+ // ensure only name=value is written to cookie header
+ .map(|c| Cookie::new(c.name(), c.value()).encoded().to_string())
+ .collect::>()
+ .join("; ");
+
if !cookie.is_empty() {
- head.headers.insert(
- header::COOKIE,
- HeaderValue::from_str(&cookie.as_str()[2..]).unwrap(),
- );
+ head.headers
+ .insert(header::COOKIE, HeaderValue::from_str(&cookie).unwrap());
}
req
diff --git a/awc/src/request.rs b/awc/src/request.rs
index 21a7cd911..3dd8cb2ce 100644
--- a/awc/src/request.rs
+++ b/awc/src/request.rs
@@ -1,16 +1,14 @@
use std::convert::TryFrom;
-use std::fmt::Write as FmtWrite;
use std::rc::Rc;
use std::time::Duration;
use std::{fmt, net};
use bytes::Bytes;
use futures_core::Stream;
-use percent_encoding::percent_encode;
use serde::Serialize;
use actix_http::body::Body;
-use actix_http::cookie::{Cookie, CookieJar, USERINFO};
+use actix_http::cookie::{Cookie, CookieJar};
use actix_http::http::header::{self, Header, IntoHeaderValue};
use actix_http::http::{
uri, ConnectionType, Error as HttpError, HeaderMap, HeaderName, HeaderValue, Method,
@@ -527,16 +525,18 @@ impl ClientRequest {
// set cookies
if let Some(ref mut jar) = self.cookies {
- let mut cookie = String::new();
- for c in jar.delta() {
- let name = percent_encode(c.name().as_bytes(), USERINFO);
- let value = percent_encode(c.value().as_bytes(), USERINFO);
- let _ = write!(&mut cookie, "; {}={}", name, value);
+ let cookie: String = jar
+ .delta()
+ // ensure only name=value is written to cookie header
+ .map(|c| Cookie::new(c.name(), c.value()).encoded().to_string())
+ .collect::>()
+ .join("; ");
+
+ if !cookie.is_empty() {
+ self.head
+ .headers
+ .insert(header::COOKIE, HeaderValue::from_str(&cookie).unwrap());
}
- self.head.headers.insert(
- header::COOKIE,
- HeaderValue::from_str(&cookie.as_str()[2..]).unwrap(),
- );
}
let mut slf = self;
diff --git a/awc/src/test.rs b/awc/src/test.rs
index a6cbd03e6..68e5c9dc5 100644
--- a/awc/src/test.rs
+++ b/awc/src/test.rs
@@ -1,13 +1,11 @@
//! Test helpers for actix http client to use during testing.
use std::convert::TryFrom;
-use std::fmt::Write as FmtWrite;
-use actix_http::cookie::{Cookie, CookieJar, USERINFO};
+use actix_http::cookie::{Cookie, CookieJar};
use actix_http::http::header::{self, Header, HeaderValue, IntoHeaderValue};
use actix_http::http::{Error as HttpError, HeaderName, StatusCode, Version};
use actix_http::{h1, Payload, ResponseHead};
use bytes::Bytes;
-use percent_encoding::percent_encode;
use crate::ClientResponse;
@@ -88,16 +86,10 @@ impl TestResponse {
pub fn finish(self) -> ClientResponse {
let mut head = self.head;
- let mut cookie = String::new();
- for c in self.cookies.delta() {
- let name = percent_encode(c.name().as_bytes(), USERINFO);
- let value = percent_encode(c.value().as_bytes(), USERINFO);
- let _ = write!(&mut cookie, "; {}={}", name, value);
- }
- if !cookie.is_empty() {
+ for cookie in self.cookies.delta() {
head.headers.insert(
header::SET_COOKIE,
- HeaderValue::from_str(&cookie.as_str()[2..]).unwrap(),
+ HeaderValue::from_str(&cookie.encoded().to_string()).unwrap(),
);
}
diff --git a/awc/src/ws.rs b/awc/src/ws.rs
index 89ca50b59..6ad660c41 100644
--- a/awc/src/ws.rs
+++ b/awc/src/ws.rs
@@ -1,6 +1,5 @@
//! Websockets client
use std::convert::TryFrom;
-use std::fmt::Write as FmtWrite;
use std::net::SocketAddr;
use std::rc::Rc;
use std::{fmt, str};
@@ -9,9 +8,7 @@ use actix_codec::Framed;
use actix_http::cookie::{Cookie, CookieJar};
use actix_http::{ws, Payload, RequestHead};
use actix_rt::time::timeout;
-use percent_encoding::percent_encode;
-use actix_http::cookie::USERINFO;
pub use actix_http::ws::{CloseCode, CloseReason, Codec, Frame, Message};
use crate::connect::BoxedSocket;
@@ -246,16 +243,18 @@ impl WebsocketsRequest {
// set cookies
if let Some(ref mut jar) = self.cookies {
- let mut cookie = String::new();
- for c in jar.delta() {
- let name = percent_encode(c.name().as_bytes(), USERINFO);
- let value = percent_encode(c.value().as_bytes(), USERINFO);
- let _ = write!(&mut cookie, "; {}={}", name, value);
+ let cookie: String = jar
+ .delta()
+ // ensure only name=value is written to cookie header
+ .map(|c| Cookie::new(c.name(), c.value()).encoded().to_string())
+ .collect::>()
+ .join("; ");
+
+ if !cookie.is_empty() {
+ self.head
+ .headers
+ .insert(header::COOKIE, HeaderValue::from_str(&cookie).unwrap());
}
- self.head.headers.insert(
- header::COOKIE,
- HeaderValue::from_str(&cookie.as_str()[2..]).unwrap(),
- );
}
// origin
From a7c853329193d7eb4f05c62d1a57b900ce62b0e9 Mon Sep 17 00:00:00 2001
From: Abhishek Yadav <55960554+b4skyx@users.noreply.github.com>
Date: Mon, 22 Jun 2020 04:14:48 +0000
Subject: [PATCH 06/78] Update benchmark url in README.md
Updated benchmark url from r18 to the latest r19.
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 6382abd4d..ade632877 100644
--- a/README.md
+++ b/README.md
@@ -94,7 +94,7 @@ You may consider checking out
## Benchmarks
-* [TechEmpower Framework Benchmark](https://www.techempower.com/benchmarks/#section=data-r18)
+* [TechEmpower Framework Benchmark](https://www.techempower.com/benchmarks/#section=data-r19)
## License
From c11052f826517d4b169238ccb49cf996834daa81 Mon Sep 17 00:00:00 2001
From: Rob Ede
Date: Mon, 22 Jun 2020 10:28:06 +0100
Subject: [PATCH 07/78] add PR template for bugs and features (#1570)
Co-authored-by: Yuki Okushi
---
.github/ISSUE_TEMPLATE/bug_report.md | 4 +--
.../PULL_REQUEST_TEMPLATE/bug_or_feature.md | 33 +++++++++++++++++++
2 files changed, 35 insertions(+), 2 deletions(-)
create mode 100644 .github/PULL_REQUEST_TEMPLATE/bug_or_feature.md
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index 128f51ffd..2df863ae8 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -1,6 +1,6 @@
---
-name: bug report
-about: create a bug report
+name: Bug Report
+about: Create a bug report.
---
Your issue may already be reported!
diff --git a/.github/PULL_REQUEST_TEMPLATE/bug_or_feature.md b/.github/PULL_REQUEST_TEMPLATE/bug_or_feature.md
new file mode 100644
index 000000000..a1fa98ea8
--- /dev/null
+++ b/.github/PULL_REQUEST_TEMPLATE/bug_or_feature.md
@@ -0,0 +1,33 @@
+---
+name: Bug / Feature
+about: Submit a bug fix or feature.
+---
+
+## PR Type
+What kind of change does this PR make?
+
+
+
+- [ ] Bug fix
+- [ ] Feature
+- [ ] Refactor / code style change (no functional or public API changes)
+- [ ] Other
+
+
+## PR Checklist
+Check your PR fulfills the following:
+
+
+
+- [ ] Tests for the changes have been added / updated.
+- [ ] Documentation comments have been added / updated.
+- [ ] A changelog entry has been made for the appropriate packages.
+
+
+## Overview
+
+
+
+
+
+
From a70e599ff5b4e0a24aaf4565da075bfac1bfdec3 Mon Sep 17 00:00:00 2001
From: Rob Ede
Date: Mon, 22 Jun 2020 20:09:48 +0100
Subject: [PATCH 08/78] re-export rt in web and add main macro (#1575)
---
Cargo.toml | 4 +--
actix-files/Cargo.toml | 1 -
actix-web-codegen/CHANGES.md | 5 +++
actix-web-codegen/Cargo.toml | 1 -
actix-web-codegen/README.md | 2 +-
actix-web-codegen/src/lib.rs | 53 ++++++++++++++++++++++++++++----
rust-toolchain | 1 +
src/app.rs | 1 +
src/info.rs | 2 +-
src/lib.rs | 21 ++++---------
src/middleware/compress.rs | 1 +
src/middleware/defaultheaders.rs | 1 +
src/middleware/logger.rs | 2 +-
src/route.rs | 1 +
src/types/form.rs | 1 +
src/types/json.rs | 1 +
src/types/payload.rs | 1 +
test-server/Cargo.toml | 1 -
18 files changed, 71 insertions(+), 29 deletions(-)
create mode 100644 rust-toolchain
diff --git a/Cargo.toml b/Cargo.toml
index de7222f1f..8c4719b92 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -97,8 +97,8 @@ serde_json = "1.0"
serde_urlencoded = "0.6.1"
time = { version = "0.2.7", default-features = false, features = ["std"] }
url = "2.1"
-open-ssl = { version="0.10", package = "openssl", optional = true }
-rust-tls = { version = "0.17.0", package = "rustls", optional = true }
+open-ssl = { package = "openssl", version = "0.10", optional = true }
+rust-tls = { package = "rustls", version = "0.17.0", optional = true }
tinyvec = { version = "0.3", features = ["alloc"] }
[dev-dependencies]
diff --git a/actix-files/Cargo.toml b/actix-files/Cargo.toml
index 356c7a413..76e528b12 100644
--- a/actix-files/Cargo.toml
+++ b/actix-files/Cargo.toml
@@ -11,7 +11,6 @@ documentation = "https://docs.rs/actix-files/"
categories = ["asynchronous", "web-programming::http-server"]
license = "MIT/Apache-2.0"
edition = "2018"
-workspace = ".."
[lib]
name = "actix_files"
diff --git a/actix-web-codegen/CHANGES.md b/actix-web-codegen/CHANGES.md
index b2e80591f..8259aaac2 100644
--- a/actix-web-codegen/CHANGES.md
+++ b/actix-web-codegen/CHANGES.md
@@ -1,5 +1,10 @@
# Changes
+## [Unreleased] - XXXX-XX-XX
+
+* Add main entry-point macro that uses re-exported runtime.
+
+
## [0.2.2] - 2020-05-23
* Add resource middleware on actix-web-codegen [#1467]
diff --git a/actix-web-codegen/Cargo.toml b/actix-web-codegen/Cargo.toml
index 60480a7a1..178eeeb7e 100644
--- a/actix-web-codegen/Cargo.toml
+++ b/actix-web-codegen/Cargo.toml
@@ -9,7 +9,6 @@ documentation = "https://docs.rs/actix-web-codegen"
authors = ["Nikolay Kim "]
license = "MIT/Apache-2.0"
edition = "2018"
-workspace = ".."
[lib]
proc-macro = true
diff --git a/actix-web-codegen/README.md b/actix-web-codegen/README.md
index c482a6b36..45eb82c2c 100644
--- a/actix-web-codegen/README.md
+++ b/actix-web-codegen/README.md
@@ -1,4 +1,4 @@
-# Macros for actix-web framework [](https://travis-ci.org/actix/actix-web) [](https://codecov.io/gh/actix/actix-web) [](https://crates.io/crates/actix-web-codegen) [](https://gitter.im/actix/actix?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
+# Helper and convenience macros for Actix-web. [](https://travis-ci.org/actix/actix-web) [](https://codecov.io/gh/actix/actix-web) [](https://crates.io/crates/actix-web-codegen) [](https://gitter.im/actix/actix?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
## Documentation & Resources
diff --git a/actix-web-codegen/src/lib.rs b/actix-web-codegen/src/lib.rs
index 2a49b4714..b6df3f0dd 100644
--- a/actix-web-codegen/src/lib.rs
+++ b/actix-web-codegen/src/lib.rs
@@ -1,11 +1,12 @@
#![recursion_limit = "512"]
-//! Actix-web codegen module
+
+//! Helper and convenience macros for Actix-web.
//!
-//! Generators for routes and scopes
+//! ## Runtime Setup
//!
-//! ## Route
+//! - [main](attr.main.html)
//!
-//! Macros:
+//! ## Resource Macros:
//!
//! - [get](attr.get.html)
//! - [post](attr.post.html)
@@ -23,12 +24,12 @@
//! - `guard="function_name"` - Registers function as guard using `actix_web::guard::fn_guard`
//! - `wrap="Middleware"` - Registers a resource middleware.
//!
-//! ## Notes
+//! ### Notes
//!
//! Function name can be specified as any expression that is going to be accessible to the generate
//! code (e.g `my_guard` or `my_module::my_guard`)
//!
-//! ## Example:
+//! ### Example:
//!
//! ```rust
//! use actix_web::HttpResponse;
@@ -139,3 +140,43 @@ pub fn trace(args: TokenStream, input: TokenStream) -> TokenStream {
pub fn patch(args: TokenStream, input: TokenStream) -> TokenStream {
route::generate(args, input, route::GuardType::Patch)
}
+
+/// Marks async main function as the actix system entry-point.
+///
+/// ## Usage
+///
+/// ```rust
+/// #[actix_web::main]
+/// async fn main() {
+/// async { println!("Hello world"); }.await
+/// }
+/// ```
+#[proc_macro_attribute]
+#[cfg(not(test))] // Work around for rust-lang/rust#62127
+pub fn main(_: TokenStream, item: TokenStream) -> TokenStream {
+ use quote::quote;
+
+ let mut input = syn::parse_macro_input!(item as syn::ItemFn);
+ let attrs = &input.attrs;
+ let vis = &input.vis;
+ let sig = &mut input.sig;
+ let body = &input.block;
+ let name = &sig.ident;
+
+ if sig.asyncness.is_none() {
+ return syn::Error::new_spanned(sig.fn_token, "only async fn is supported")
+ .to_compile_error()
+ .into();
+ }
+
+ sig.asyncness = None;
+
+ (quote! {
+ #(#attrs)*
+ #vis #sig {
+ actix_web::rt::System::new(stringify!(#name))
+ .block_on(async move { #body })
+ }
+ })
+ .into()
+}
diff --git a/rust-toolchain b/rust-toolchain
new file mode 100644
index 000000000..32b7211cb
--- /dev/null
+++ b/rust-toolchain
@@ -0,0 +1 @@
+1.40.0
diff --git a/src/app.rs b/src/app.rs
index 8178d57fe..ae3d9fdf0 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -42,6 +42,7 @@ pub struct App {
impl App {
/// Create application builder. Application can be configured with a builder-like pattern.
+ #[allow(clippy::new_without_default)]
pub fn new() -> Self {
let fref = Rc::new(RefCell::new(None));
App {
diff --git a/src/info.rs b/src/info.rs
index 5b506d85a..1d9b402a7 100644
--- a/src/info.rs
+++ b/src/info.rs
@@ -25,7 +25,7 @@ impl ConnectionInfo {
Ref::map(req.extensions(), |e| e.get().unwrap())
}
- #[allow(clippy::cognitive_complexity)]
+ #[allow(clippy::cognitive_complexity, clippy::borrow_interior_mutable_const)]
fn new(req: &RequestHead, cfg: &AppConfig) -> ConnectionInfo {
let mut host = None;
let mut scheme = None;
diff --git a/src/lib.rs b/src/lib.rs
index 09642806f..844f952cc 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,18 +1,11 @@
#![warn(rust_2018_idioms, warnings)]
-#![allow(
- clippy::needless_doctest_main,
- clippy::type_complexity,
- clippy::borrow_interior_mutable_const
-)]
+#![allow(clippy::needless_doctest_main, clippy::type_complexity)]
+
//! Actix web is a small, pragmatic, and extremely fast web framework
//! for Rust.
//!
//! ## Example
//!
-//! The `#[actix_rt::main]` macro in the example below is provided by the Actix runtime
-//! crate, [`actix-rt`](https://crates.io/crates/actix-rt). You will need to include
-//! `actix-rt` in your dependencies for it to run.
-//!
//! ```rust,no_run
//! use actix_web::{web, App, Responder, HttpServer};
//!
@@ -20,7 +13,7 @@
//! format!("Hello {}! id:{}", info.0, info.1)
//! }
//!
-//! #[actix_rt::main]
+//! #[actix_web::main]
//! async fn main() -> std::io::Result<()> {
//! HttpServer::new(|| App::new().service(
//! web::resource("/{name}/{id}/index.html").to(index))
@@ -80,9 +73,7 @@
//! * `compress` - enables content encoding compression support (default enabled)
//! * `openssl` - enables ssl support via `openssl` crate, supports `http/2`
//! * `rustls` - enables ssl support via `rustls` crate, supports `http/2`
-//! * `secure-cookies` - enables secure cookies support, includes `ring` crate as
-//! dependency
-#![allow(clippy::type_complexity, clippy::new_without_default)]
+//! * `secure-cookies` - enables secure cookies support
mod app;
mod app_service;
@@ -106,13 +97,12 @@ pub mod test;
mod types;
pub mod web;
-#[doc(hidden)]
pub use actix_web_codegen::*;
+pub use actix_rt as rt;
// re-export for convenience
pub use actix_http::Response as HttpResponse;
pub use actix_http::{body, cookie, http, Error, HttpMessage, ResponseError, Result};
-pub use actix_macros::{main, test as test_rt};
pub use crate::app::App;
pub use crate::extract::FromRequest;
@@ -230,6 +220,7 @@ pub mod client {
//! println!("Response: {:?}", response);
//! }
//! ```
+
pub use awc::error::{
ConnectError, InvalidUrl, PayloadError, SendRequestError, WsClientError,
};
diff --git a/src/middleware/compress.rs b/src/middleware/compress.rs
index 6de451c84..fe3ba841c 100644
--- a/src/middleware/compress.rs
+++ b/src/middleware/compress.rs
@@ -90,6 +90,7 @@ where
self.service.poll_ready(cx)
}
+ #[allow(clippy::borrow_interior_mutable_const)]
fn call(&mut self, req: ServiceRequest) -> Self::Future {
// negotiate content-encoding
let encoding = if let Some(val) = req.headers().get(&ACCEPT_ENCODING) {
diff --git a/src/middleware/defaultheaders.rs b/src/middleware/defaultheaders.rs
index ef2e56e69..6d43aba95 100644
--- a/src/middleware/defaultheaders.rs
+++ b/src/middleware/defaultheaders.rs
@@ -128,6 +128,7 @@ where
self.service.poll_ready(cx)
}
+ #[allow(clippy::borrow_interior_mutable_const)]
fn call(&mut self, req: ServiceRequest) -> Self::Future {
let inner = self.inner.clone();
let fut = self.service.call(req);
diff --git a/src/middleware/logger.rs b/src/middleware/logger.rs
index 8b881c0a4..57b640bdd 100644
--- a/src/middleware/logger.rs
+++ b/src/middleware/logger.rs
@@ -478,7 +478,7 @@ impl FormatText {
}
FormatText::RemoteAddr => {
let s = if let Some(ref peer) = req.connection_info().remote_addr() {
- FormatText::Str(peer.to_string())
+ FormatText::Str((*peer).to_string())
} else {
FormatText::Str("-".to_string())
};
diff --git a/src/route.rs b/src/route.rs
index 2763f3b1a..b17fa9b06 100644
--- a/src/route.rs
+++ b/src/route.rs
@@ -46,6 +46,7 @@ pub struct Route {
impl Route {
/// Create new route which matches any request.
+ #[allow(clippy::new_without_default)]
pub fn new() -> Route {
Route {
service: Box::new(RouteNewService::new(Extract::new(Handler::new(|| {
diff --git a/src/types/form.rs b/src/types/form.rs
index ca1a4b103..c10ed4649 100644
--- a/src/types/form.rs
+++ b/src/types/form.rs
@@ -252,6 +252,7 @@ pub struct UrlEncoded {
fut: Option>>,
}
+#[allow(clippy::borrow_interior_mutable_const)]
impl UrlEncoded {
/// Create a new future to URL encode a request
pub fn new(req: &HttpRequest, payload: &mut Payload) -> UrlEncoded {
diff --git a/src/types/json.rs b/src/types/json.rs
index f746fd432..6de9e0d86 100644
--- a/src/types/json.rs
+++ b/src/types/json.rs
@@ -319,6 +319,7 @@ where
U: DeserializeOwned + 'static,
{
/// Create `JsonBody` for request.
+ #[allow(clippy::borrow_interior_mutable_const)]
pub fn new(
req: &HttpRequest,
payload: &mut Payload,
diff --git a/src/types/payload.rs b/src/types/payload.rs
index bad33bfc6..0efdc2c09 100644
--- a/src/types/payload.rs
+++ b/src/types/payload.rs
@@ -315,6 +315,7 @@ pub struct HttpMessageBody {
impl HttpMessageBody {
/// Create `MessageBody` for request.
+ #[allow(clippy::borrow_interior_mutable_const)]
pub fn new(req: &HttpRequest, payload: &mut dev::Payload) -> HttpMessageBody {
let mut len = None;
if let Some(l) = req.headers().get(&header::CONTENT_LENGTH) {
diff --git a/test-server/Cargo.toml b/test-server/Cargo.toml
index f90cef0dd..6265cd415 100644
--- a/test-server/Cargo.toml
+++ b/test-server/Cargo.toml
@@ -14,7 +14,6 @@ categories = ["network-programming", "asynchronous",
license = "MIT/Apache-2.0"
exclude = [".gitignore", ".cargo/config"]
edition = "2018"
-workspace = ".."
[package.metadata.docs.rs]
features = []
From fa28175a740dc589eeb52d730d18aae396e7b5bf Mon Sep 17 00:00:00 2001
From: Rob Ede
Date: Tue, 23 Jun 2020 00:58:20 +0100
Subject: [PATCH 09/78] add method to extract matched resource pattern (#1566)
---
CHANGES.md | 2 +
src/request.rs | 44 +++++++++++++++++-
src/rmap.rs | 119 ++++++++++++++++++++++++++++++++++++++++++++++++-
src/service.rs | 6 +++
4 files changed, 168 insertions(+), 3 deletions(-)
diff --git a/CHANGES.md b/CHANGES.md
index 58cddf78d..22a389d10 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -5,6 +5,8 @@
### Added
* Re-export `actix_rt::main` as `actix_web::main`.
+* `HttpRequest::match_pattern` and `ServiceRequest::match_pattern` for extracting the matched
+ resource pattern.
### Changed
diff --git a/src/request.rs b/src/request.rs
index f8abeb1bb..8ca897442 100644
--- a/src/request.rs
+++ b/src/request.rs
@@ -126,6 +126,17 @@ impl HttpRequest {
&mut Rc::get_mut(&mut self.0).unwrap().path
}
+ /// The resource definition pattern that matched the path. Useful for logging and metrics.
+ ///
+ /// For example, when a resource with pattern `/user/{id}/profile` is defined and a call is made
+ /// to `/user/123/profile` this function would return `Some("/user/{id}/profile")`.
+ ///
+ /// Returns a None when no resource is fully matched, including default services.
+ #[inline]
+ pub fn match_pattern(&self) -> Option {
+ self.0.rmap.match_pattern(self.path())
+ }
+
/// Request extensions
#[inline]
pub fn extensions(&self) -> Ref<'_, Extensions> {
@@ -141,7 +152,6 @@ impl HttpRequest {
/// Generate url for named resource
///
/// ```rust
- /// # extern crate actix_web;
/// # use actix_web::{web, App, HttpRequest, HttpResponse};
/// #
/// fn index(req: HttpRequest) -> HttpResponse {
@@ -599,4 +609,36 @@ mod tests {
assert!(tracker.borrow().dropped);
}
+
+ #[actix_rt::test]
+ async fn extract_path_pattern() {
+ let mut srv = init_service(
+ App::new().service(
+ web::scope("/user/{id}")
+ .service(web::resource("/profile").route(web::get().to(
+ move |req: HttpRequest| {
+ assert_eq!(
+ req.match_pattern(),
+ Some("/user/{id}/profile".to_owned())
+ );
+
+ HttpResponse::Ok().finish()
+ },
+ )))
+ .default_service(web::to(move |req: HttpRequest| {
+ assert!(req.match_pattern().is_none());
+ HttpResponse::Ok().finish()
+ })),
+ ),
+ )
+ .await;
+
+ let req = TestRequest::get().uri("/user/22/profile").to_request();
+ let res = call_service(&mut srv, req).await;
+ assert_eq!(res.status(), StatusCode::OK);
+
+ let req = TestRequest::get().uri("/user/22/not-exist").to_request();
+ let res = call_service(&mut srv, req).await;
+ assert_eq!(res.status(), StatusCode::OK);
+ }
}
diff --git a/src/rmap.rs b/src/rmap.rs
index 47092608c..0a0c96777 100644
--- a/src/rmap.rs
+++ b/src/rmap.rs
@@ -43,9 +43,7 @@ impl ResourceMap {
}
}
}
-}
-impl ResourceMap {
/// Generate url for named resource
///
/// Check [`HttpRequest::url_for()`](../struct.HttpRequest.html#method.
@@ -95,6 +93,45 @@ impl ResourceMap {
false
}
+ /// Returns the full resource pattern matched against a path or None if no full match
+ /// is possible.
+ pub fn match_pattern(&self, path: &str) -> Option {
+ let path = if path.is_empty() { "/" } else { path };
+
+ // ensure a full match exists
+ if !self.has_resource(path) {
+ return None;
+ }
+
+ Some(self.traverse_resource_pattern(path))
+ }
+
+ /// Takes remaining path and tries to match it up against a resource definition within the
+ /// current resource map recursively, returning a concatenation of all resource prefixes and
+ /// patterns matched in the tree.
+ ///
+ /// Should only be used after checking the resource exists in the map so that partial match
+ /// patterns are not returned.
+ fn traverse_resource_pattern(&self, remaining: &str) -> String {
+ for (pattern, rmap) in &self.patterns {
+ if let Some(ref rmap) = rmap {
+ if let Some(prefix_len) = pattern.is_prefix_match(remaining) {
+ let prefix = pattern.pattern().to_owned();
+
+ return [
+ prefix,
+ rmap.traverse_resource_pattern(&remaining[prefix_len..]),
+ ]
+ .concat();
+ }
+ } else if pattern.is_match(remaining) {
+ return pattern.pattern().to_owned();
+ }
+ }
+
+ String::new()
+ }
+
fn patterns_for(
&self,
name: &str,
@@ -188,3 +225,81 @@ impl ResourceMap {
}
}
}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn extract_matched_pattern() {
+ let mut root = ResourceMap::new(ResourceDef::root_prefix(""));
+
+ let mut user_map = ResourceMap::new(ResourceDef::root_prefix(""));
+ user_map.add(&mut ResourceDef::new("/"), None);
+ user_map.add(&mut ResourceDef::new("/profile"), None);
+ user_map.add(&mut ResourceDef::new("/article/{id}"), None);
+ user_map.add(&mut ResourceDef::new("/post/{post_id}"), None);
+ user_map.add(
+ &mut ResourceDef::new("/post/{post_id}/comment/{comment_id}"),
+ None,
+ );
+
+ root.add(&mut ResourceDef::new("/info"), None);
+ root.add(&mut ResourceDef::new("/v{version:[[:digit:]]{1}}"), None);
+ root.add(
+ &mut ResourceDef::root_prefix("/user/{id}"),
+ Some(Rc::new(user_map)),
+ );
+
+ let root = Rc::new(root);
+ root.finish(Rc::clone(&root));
+
+ // sanity check resource map setup
+
+ assert!(root.has_resource("/info"));
+ assert!(!root.has_resource("/bar"));
+
+ assert!(root.has_resource("/v1"));
+ assert!(root.has_resource("/v2"));
+ assert!(!root.has_resource("/v33"));
+
+ assert!(root.has_resource("/user/22"));
+ assert!(root.has_resource("/user/22/"));
+ assert!(root.has_resource("/user/22/profile"));
+
+ // extract patterns from paths
+
+ assert!(root.match_pattern("/bar").is_none());
+ assert!(root.match_pattern("/v44").is_none());
+
+ assert_eq!(root.match_pattern("/info"), Some("/info".to_owned()));
+ assert_eq!(
+ root.match_pattern("/v1"),
+ Some("/v{version:[[:digit:]]{1}}".to_owned())
+ );
+ assert_eq!(
+ root.match_pattern("/v2"),
+ Some("/v{version:[[:digit:]]{1}}".to_owned())
+ );
+ assert_eq!(
+ root.match_pattern("/user/22/profile"),
+ Some("/user/{id}/profile".to_owned())
+ );
+ assert_eq!(
+ root.match_pattern("/user/602CFB82-7709-4B17-ADCF-4C347B6F2203/profile"),
+ Some("/user/{id}/profile".to_owned())
+ );
+ assert_eq!(
+ root.match_pattern("/user/22/article/44"),
+ Some("/user/{id}/article/{id}".to_owned())
+ );
+ assert_eq!(
+ root.match_pattern("/user/22/post/my-post"),
+ Some("/user/{id}/post/{post_id}".to_owned())
+ );
+ assert_eq!(
+ root.match_pattern("/user/22/post/other-post/comment/42"),
+ Some("/user/{id}/post/{post_id}/comment/{comment_id}".to_owned())
+ );
+ }
+}
diff --git a/src/service.rs b/src/service.rs
index 232a2f132..f7e201779 100644
--- a/src/service.rs
+++ b/src/service.rs
@@ -195,6 +195,12 @@ impl ServiceRequest {
pub fn match_info(&self) -> &Path {
self.0.match_info()
}
+
+ /// Counterpart to [`HttpRequest::match_pattern`](../struct.HttpRequest.html#method.match_pattern).
+ #[inline]
+ pub fn match_pattern(&self) -> Option {
+ self.0.match_pattern()
+ }
#[inline]
/// Get a mutable reference to the Path parameters.
From 487f90be5bc8f520a726c6e4bf4e18dc034dd2eb Mon Sep 17 00:00:00 2001
From: Rob Ede
Date: Tue, 23 Jun 2020 06:26:07 +0100
Subject: [PATCH 10/78] move pull request template (#1578)
---
.../bug_or_feature.md => PULL_REQUEST_TEMPLATE.md} | 5 -----
1 file changed, 5 deletions(-)
rename .github/{PULL_REQUEST_TEMPLATE/bug_or_feature.md => PULL_REQUEST_TEMPLATE.md} (91%)
diff --git a/.github/PULL_REQUEST_TEMPLATE/bug_or_feature.md b/.github/PULL_REQUEST_TEMPLATE.md
similarity index 91%
rename from .github/PULL_REQUEST_TEMPLATE/bug_or_feature.md
rename to .github/PULL_REQUEST_TEMPLATE.md
index a1fa98ea8..6426eab65 100644
--- a/.github/PULL_REQUEST_TEMPLATE/bug_or_feature.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -1,8 +1,3 @@
----
-name: Bug / Feature
-about: Submit a bug fix or feature.
----
-
## PR Type
What kind of change does this PR make?
From 23c8191cca7264076d58816923b6f25eb89e6107 Mon Sep 17 00:00:00 2001
From: Takashi Idobe
Date: Sat, 27 Jun 2020 11:22:16 -0400
Subject: [PATCH 11/78] add method to extract matched resource name (#1577)
Co-authored-by: Rob Ede
---
CHANGES.md | 1 +
src/request.rs | 26 ++++++++++++++++++++
src/rmap.rs | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++
src/service.rs | 8 ++++++-
4 files changed, 99 insertions(+), 1 deletion(-)
diff --git a/CHANGES.md b/CHANGES.md
index 22a389d10..f4c04cd05 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -7,6 +7,7 @@
* Re-export `actix_rt::main` as `actix_web::main`.
* `HttpRequest::match_pattern` and `ServiceRequest::match_pattern` for extracting the matched
resource pattern.
+* `HttpRequest::match_name` and `ServiceRequest::match_name` for extracting matched resource name.
### Changed
diff --git a/src/request.rs b/src/request.rs
index 8ca897442..85f409016 100644
--- a/src/request.rs
+++ b/src/request.rs
@@ -137,6 +137,14 @@ impl HttpRequest {
self.0.rmap.match_pattern(self.path())
}
+ /// The resource name that matched the path. Useful for logging and metrics.
+ ///
+ /// Returns a None when no resource is fully matched, including default services.
+ #[inline]
+ pub fn match_name(&self) -> Option<&str> {
+ self.0.rmap.match_name(self.path())
+ }
+
/// Request extensions
#[inline]
pub fn extensions(&self) -> Ref<'_, Extensions> {
@@ -462,6 +470,24 @@ mod tests {
);
}
+ #[test]
+ fn test_match_name() {
+ let mut rdef = ResourceDef::new("/index.html");
+ *rdef.name_mut() = "index".to_string();
+
+ let mut rmap = ResourceMap::new(ResourceDef::new(""));
+ rmap.add(&mut rdef, None);
+
+ assert!(rmap.has_resource("/index.html"));
+
+ let req = TestRequest::default()
+ .uri("/index.html")
+ .rmap(rmap)
+ .to_http_request();
+
+ assert_eq!(req.match_name(), Some("index"));
+ }
+
#[test]
fn test_url_for_external() {
let mut rdef = ResourceDef::new("https://youtube.com/watch/{video_id}");
diff --git a/src/rmap.rs b/src/rmap.rs
index 0a0c96777..5e79830ec 100644
--- a/src/rmap.rs
+++ b/src/rmap.rs
@@ -93,6 +93,27 @@ impl ResourceMap {
false
}
+ /// Returns the name of the route that matches the given path or None if no full match
+ /// is possible.
+ pub fn match_name(&self, path: &str) -> Option<&str> {
+ let path = if path.is_empty() { "/" } else { path };
+
+ for (pattern, rmap) in &self.patterns {
+ if let Some(ref rmap) = rmap {
+ if let Some(plen) = pattern.is_prefix_match(path) {
+ return rmap.match_name(&path[plen..]);
+ }
+ } else if pattern.is_match(path) {
+ return match pattern.name() {
+ "" => None,
+ s => Some(s),
+ };
+ }
+ }
+
+ None
+ }
+
/// Returns the full resource pattern matched against a path or None if no full match
/// is possible.
pub fn match_pattern(&self, path: &str) -> Option {
@@ -302,4 +323,48 @@ mod tests {
Some("/user/{id}/post/{post_id}/comment/{comment_id}".to_owned())
);
}
+
+ #[test]
+ fn extract_matched_name() {
+ let mut root = ResourceMap::new(ResourceDef::root_prefix(""));
+
+ let mut rdef = ResourceDef::new("/info");
+ *rdef.name_mut() = "root_info".to_owned();
+ root.add(&mut rdef, None);
+
+ let mut user_map = ResourceMap::new(ResourceDef::root_prefix(""));
+ let mut rdef = ResourceDef::new("/");
+ user_map.add(&mut rdef, None);
+
+ let mut rdef = ResourceDef::new("/post/{post_id}");
+ *rdef.name_mut() = "user_post".to_owned();
+ user_map.add(&mut rdef, None);
+
+ root.add(
+ &mut ResourceDef::root_prefix("/user/{id}"),
+ Some(Rc::new(user_map)),
+ );
+
+ let root = Rc::new(root);
+ root.finish(Rc::clone(&root));
+
+ // sanity check resource map setup
+
+ assert!(root.has_resource("/info"));
+ assert!(!root.has_resource("/bar"));
+
+ assert!(root.has_resource("/user/22"));
+ assert!(root.has_resource("/user/22/"));
+ assert!(root.has_resource("/user/22/post/55"));
+
+ // extract patterns from paths
+
+ assert!(root.match_name("/bar").is_none());
+ assert!(root.match_name("/v44").is_none());
+
+ assert_eq!(root.match_name("/info"), Some("root_info"));
+ assert_eq!(root.match_name("/user/22"), None);
+ assert_eq!(root.match_name("/user/22/"), None);
+ assert_eq!(root.match_name("/user/22/post/55"), Some("user_post"));
+ }
}
diff --git a/src/service.rs b/src/service.rs
index f7e201779..cba852a9c 100644
--- a/src/service.rs
+++ b/src/service.rs
@@ -195,7 +195,13 @@ impl ServiceRequest {
pub fn match_info(&self) -> &Path {
self.0.match_info()
}
-
+
+ /// Counterpart to [`HttpRequest::match_name`](../struct.HttpRequest.html#method.match_name).
+ #[inline]
+ pub fn match_name(&self) -> Option<&str> {
+ self.0.match_name()
+ }
+
/// Counterpart to [`HttpRequest::match_pattern`](../struct.HttpRequest.html#method.match_pattern).
#[inline]
pub fn match_pattern(&self) -> Option {
From f2d641b772cc0d61384f9dcbf91ec132065a0f24 Mon Sep 17 00:00:00 2001
From: Yuki Okushi
Date: Thu, 2 Jul 2020 17:52:42 +0900
Subject: [PATCH 12/78] Update `v_htmlescape` to 0.10
---
actix-files/CHANGES.md | 4 ++++
actix-files/Cargo.toml | 2 +-
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/actix-files/CHANGES.md b/actix-files/CHANGES.md
index abf143997..5d1845e37 100644
--- a/actix-files/CHANGES.md
+++ b/actix-files/CHANGES.md
@@ -1,5 +1,9 @@
# Changes
+## [unreleased] - xxx
+
+* Update `v_htmlescape` to 0.10
+
## [0.3.0-alpha.1] - 2020-05-23
* Update `actix-web` and `actix-http` dependencies to alpha
diff --git a/actix-files/Cargo.toml b/actix-files/Cargo.toml
index 76e528b12..12fd03ce1 100644
--- a/actix-files/Cargo.toml
+++ b/actix-files/Cargo.toml
@@ -29,7 +29,7 @@ log = "0.4"
mime = "0.3"
mime_guess = "2.0.1"
percent-encoding = "2.1"
-v_htmlescape = "0.4"
+v_htmlescape = "0.10"
[dev-dependencies]
actix-rt = "1.0.0"
From deab634247356b79e10ce6ef537377214fb0cd75 Mon Sep 17 00:00:00 2001
From: Yuki Okushi
Date: Fri, 3 Jul 2020 09:08:24 +0900
Subject: [PATCH 13/78] actix-http: Update `sha-1` to 0.9 (#1586)
---
.github/workflows/linux.yml | 2 +-
CHANGES.md | 1 +
README.md | 2 +-
actix-http/CHANGES.md | 2 ++
actix-http/Cargo.toml | 2 +-
actix-http/src/ws/proto.rs | 6 +++---
rust-toolchain | 2 +-
test-server/Cargo.toml | 2 --
8 files changed, 10 insertions(+), 9 deletions(-)
diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml
index ae804cc53..a2dfc5da6 100644
--- a/.github/workflows/linux.yml
+++ b/.github/workflows/linux.yml
@@ -8,7 +8,7 @@ jobs:
fail-fast: false
matrix:
version:
- - 1.40.0 # MSRV
+ - 1.41.1 # MSRV
- stable
- nightly
diff --git a/CHANGES.md b/CHANGES.md
index f4c04cd05..8f15eea15 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -13,6 +13,7 @@
* Fix actix_http::h1::dispatcher so it returns when HW_BUFFER_SIZE is reached. Should reduce peak memory consumption during large uploads. [#1550]
* Migrate cookie handling to `cookie` crate. Actix-web no longer requires `ring` dependency.
+* MSRV is now 1.41.1
### Fixed
diff --git a/README.md b/README.md
index ade632877..a9b5e0116 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@
[](https://gitter.im/actix/actix?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[](https://docs.rs/actix-web)
[](https://crates.io/crates/actix-web)
-[](https://blog.rust-lang.org/2019/12/19/Rust-1.40.0.html)
+[](https://blog.rust-lang.org/2020/02/27/Rust-1.41.1.html)

diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md
index 49599b9be..d18af162d 100644
--- a/actix-http/CHANGES.md
+++ b/actix-http/CHANGES.md
@@ -5,6 +5,8 @@
### Changed
* Migrate cookie handling to `cookie` crate.
+* Update `sha-1` to 0.9
+* MSRV is now 1.41.1
## [2.0.0-alpha.4] - 2020-05-21
diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml
index 8b9b8c825..6095f89ea 100644
--- a/actix-http/Cargo.toml
+++ b/actix-http/Cargo.toml
@@ -76,7 +76,7 @@ rand = "0.7"
regex = "1.3"
serde = "1.0"
serde_json = "1.0"
-sha-1 = "0.8"
+sha-1 = "0.9"
slab = "0.4"
serde_urlencoded = "0.6.1"
time = { version = "0.2.7", default-features = false, features = ["std"] }
diff --git a/actix-http/src/ws/proto.rs b/actix-http/src/ws/proto.rs
index dd3078a6c..fc271a8f5 100644
--- a/actix-http/src/ws/proto.rs
+++ b/actix-http/src/ws/proto.rs
@@ -208,10 +208,10 @@ pub fn hash_key(key: &[u8]) -> String {
use sha1::Digest;
let mut hasher = sha1::Sha1::new();
- hasher.input(key);
- hasher.input(WS_GUID.as_bytes());
+ hasher.update(key);
+ hasher.update(WS_GUID.as_bytes());
- base64::encode(hasher.result().as_ref())
+ base64::encode(&hasher.finalize())
}
#[cfg(test)]
diff --git a/rust-toolchain b/rust-toolchain
index 32b7211cb..f86fb9cbc 100644
--- a/rust-toolchain
+++ b/rust-toolchain
@@ -1 +1 @@
-1.40.0
+1.41.1
diff --git a/test-server/Cargo.toml b/test-server/Cargo.toml
index 6265cd415..2c526e672 100644
--- a/test-server/Cargo.toml
+++ b/test-server/Cargo.toml
@@ -43,11 +43,9 @@ bytes = "0.5.3"
futures-core = { version = "0.3.5", default-features = false }
http = "0.2.0"
log = "0.4"
-env_logger = "0.7"
socket2 = "0.3"
serde = "1.0"
serde_json = "1.0"
-sha1 = "0.6"
slab = "0.4"
serde_urlencoded = "0.6.1"
time = { version = "0.2.7", default-features = false, features = ["std"] }
From 056803d534234b15698a5a34280d340f9446bfe9 Mon Sep 17 00:00:00 2001
From: Rob Ede
Date: Sun, 5 Jul 2020 01:16:53 +0100
Subject: [PATCH 14/78] revamp readme and root doc page (#1590)
---
README.md | 90 ++++++++++++++++++++++++++++--------------------------
src/lib.rs | 90 +++++++++++++++++++++++++-----------------------------
2 files changed, 87 insertions(+), 93 deletions(-)
diff --git a/README.md b/README.md
index a9b5e0116..4d6bac29c 100644
--- a/README.md
+++ b/README.md
@@ -1,58 +1,58 @@
-
Actix web
-
Actix web is a small, pragmatic, and extremely fast rust web framework
+
Actix web
+
+ Actix web is a powerful, pragmatic, and extremely fast web framework for Rust
+
-[](https://travis-ci.org/actix/actix-web)
-[](https://codecov.io/gh/actix/actix-web)
[](https://crates.io/crates/actix-web)
-[](https://gitter.im/actix/actix?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[](https://docs.rs/actix-web)
-[](https://crates.io/crates/actix-web)
[](https://blog.rust-lang.org/2020/02/27/Rust-1.41.1.html)

+
+[](https://travis-ci.org/actix/actix-web)
+[](https://codecov.io/gh/actix/actix-web)
+[](https://crates.io/crates/actix-web)
+[](https://gitter.im/actix/actix?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
-
-
-
-Actix web is a simple, pragmatic and extremely fast web framework for Rust.
+## Features
-* Supported *HTTP/1.x* and *HTTP/2.0* protocols
+* Supports *HTTP/1.x* and *HTTP/2*
* Streaming and pipelining
* Keep-alive and slow requests handling
* Client/server [WebSockets](https://actix.rs/docs/websockets/) support
* Transparent content compression/decompression (br, gzip, deflate)
-* Configurable [request routing](https://actix.rs/docs/url-dispatch/)
+* Powerful [request routing](https://actix.rs/docs/url-dispatch/)
* Multipart streams
* Static assets
-* SSL support with OpenSSL or Rustls
+* SSL support using OpenSSL or Rustls
* Middlewares ([Logger, Session, CORS, etc](https://actix.rs/docs/middleware/))
-* Includes an asynchronous [HTTP client](https://actix.rs/actix-web/actix_web/client/index.html)
+* Includes an async [HTTP client](https://actix.rs/actix-web/actix_web/client/index.html)
* Supports [Actix actor framework](https://github.com/actix/actix)
-* Supports Rust 1.40+
+* Runs on stable Rust 1.41+
-## Docs
+## Documentation
-* [API documentation (master)](https://actix.rs/actix-web/actix_web)
-* [API documentation (docs.rs)](https://docs.rs/actix-web)
-* [User guide](https://actix.rs)
+* [Website & User Guide](https://actix.rs)
+* [Examples Repository](https://actix.rs/actix-web/actix_web)
+* [API Documentation](https://docs.rs/actix-web)
+* [API Documentation (master branch)](https://actix.rs/actix-web/actix_web)
## Example
+
+ WARNING: This example is for the master branch which is currently in beta stages for v3. For
+ Actix web v2 see the getting started guide.
+
+
Dependencies:
```toml
[dependencies]
-actix-web = "2"
+actix-web = "3"
```
Code:
@@ -76,37 +76,39 @@ async fn main() -> std::io::Result<()> {
### More examples
-* [Basics](https://github.com/actix/examples/tree/master/basics/)
-* [Stateful](https://github.com/actix/examples/tree/master/state/)
-* [Multipart streams](https://github.com/actix/examples/tree/master/multipart/)
-* [Simple websocket](https://github.com/actix/examples/tree/master/websocket/)
-* [Tera](https://github.com/actix/examples/tree/master/template_tera/)
-* [Askama](https://github.com/actix/examples/tree/master/template_askama/) templates
-* [Diesel integration](https://github.com/actix/examples/tree/master/diesel/)
-* [r2d2](https://github.com/actix/examples/tree/master/r2d2/)
-* [OpenSSL](https://github.com/actix/examples/tree/master/openssl/)
-* [Rustls](https://github.com/actix/examples/tree/master/rustls/)
-* [Tcp/Websocket chat](https://github.com/actix/examples/tree/master/websocket-chat/)
-* [Json](https://github.com/actix/examples/tree/master/json/)
+* [Basic Setup](https://github.com/actix/examples/tree/master/basics/)
+* [Application State](https://github.com/actix/examples/tree/master/state/)
+* [JSON Handling](https://github.com/actix/examples/tree/master/json/)
+* [Multipart Streams](https://github.com/actix/examples/tree/master/multipart/)
+* [Diesel Integration](https://github.com/actix/examples/tree/master/diesel/)
+* [r2d2 Integration](https://github.com/actix/examples/tree/master/r2d2/)
+* [Simple WebSocket](https://github.com/actix/examples/tree/master/websocket/)
+* [Tera Templates](https://github.com/actix/examples/tree/master/template_tera/)
+* [Askama Templates](https://github.com/actix/examples/tree/master/template_askama/)
+* [HTTPS using Rustls](https://github.com/actix/examples/tree/master/rustls/)
+* [HTTPS using OpenSSL](https://github.com/actix/examples/tree/master/openssl/)
+* [WebSocket Chat](https://github.com/actix/examples/tree/master/websocket-chat/)
You may consider checking out
[this directory](https://github.com/actix/examples/tree/master/) for more examples.
## Benchmarks
-* [TechEmpower Framework Benchmark](https://www.techempower.com/benchmarks/#section=data-r19)
+One of the fastest web frameworks available according to the
+[TechEmpower Framework Benchmark](https://www.techempower.com/benchmarks/#section=data-r19).
## 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))
+* 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-web crate is organized under the terms of the
-Contributor Covenant, the maintainer of actix-web, @fafhrd91, promises to
-intervene to uphold that code of conduct.
+Contribution to the actix-web crate is organized under the terms of the Contributor Covenant, the
+maintainers of Actix web, promises to intervene to uphold that code of conduct.
diff --git a/src/lib.rs b/src/lib.rs
index 844f952cc..eb46af664 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,79 +1,72 @@
#![warn(rust_2018_idioms, warnings)]
#![allow(clippy::needless_doctest_main, clippy::type_complexity)]
-//! Actix web is a small, pragmatic, and extremely fast web framework
-//! for Rust.
+//! Actix web is a powerful, pragmatic, and extremely fast web framework for Rust.
//!
//! ## Example
//!
//! ```rust,no_run
-//! use actix_web::{web, App, Responder, HttpServer};
+//! use actix_web::{get, web, App, HttpServer, Responder};
//!
-//! async fn index(info: web::Path<(String, u32)>) -> impl Responder {
-//! format!("Hello {}! id:{}", info.0, info.1)
+//! #[get("/{id}/{name}/index.html")]
+//! async fn index(info: web::Path<(u32, String)>) -> impl Responder {
+//! format!("Hello {}! id:{}", info.1, info.0)
//! }
//!
//! #[actix_web::main]
//! async fn main() -> std::io::Result<()> {
-//! HttpServer::new(|| App::new().service(
-//! web::resource("/{name}/{id}/index.html").to(index))
-//! )
+//! HttpServer::new(|| App::new().service(index))
//! .bind("127.0.0.1:8080")?
//! .run()
//! .await
//! }
//! ```
//!
-//! ## Documentation & community resources
+//! ## Documentation & Community Resources
//!
-//! Besides the API documentation (which you are currently looking
-//! at!), several other resources are available:
+//! In addition to this API documentation, several other resources are available:
//!
-//! * [User Guide](https://actix.rs/docs/)
-//! * [Chat on gitter](https://gitter.im/actix/actix)
-//! * [GitHub repository](https://github.com/actix/actix-web)
-//! * [Cargo package](https://crates.io/crates/actix-web)
+//! * [Website & User Guide](https://actix.rs/)
+//! * [Examples Repository](https://github.com/actix/examples)
+//! * [Community Chat on Gitter](https://gitter.im/actix/actix-web)
//!
-//! To get started navigating the API documentation you may want to
-//! consider looking at the following pages:
+//! To get started navigating the API docs, you may consider looking at the following pages first:
//!
-//! * [App](struct.App.html): This struct represents an actix-web
-//! application and is used to configure routes and other common
-//! settings.
+//! * [App](struct.App.html): This struct represents an Actix web application and is used to
+//! configure routes and other common application settings.
//!
-//! * [HttpServer](struct.HttpServer.html): This struct
-//! represents an HTTP server instance and is used to instantiate and
-//! configure servers.
+//! * [HttpServer](struct.HttpServer.html): This struct represents an HTTP server instance and is
+//! used to instantiate and configure servers.
//!
-//! * [web](web/index.html): This module
-//! provides essential helper functions and types for application registration.
+//! * [web](web/index.html): This module provides essential types for route registration as well as
+//! common utilities for request handlers.
//!
-//! * [HttpRequest](struct.HttpRequest.html) and
-//! [HttpResponse](struct.HttpResponse.html): These structs
-//! represent HTTP requests and responses and expose various methods
-//! for inspecting, creating and otherwise utilizing them.
+//! * [HttpRequest](struct.HttpRequest.html) and [HttpResponse](struct.HttpResponse.html): These
+//! structs represent HTTP requests and responses and expose methods for creating, inspecting,
+//! and otherwise utilizing them.
//!
//! ## Features
//!
-//! * Supported *HTTP/1.x* and *HTTP/2.0* protocols
+//! * Supports *HTTP/1.x* and *HTTP/2*
//! * Streaming and pipelining
//! * Keep-alive and slow requests handling
-//! * `WebSockets` server/client
+//! * Client/server [WebSockets](https://actix.rs/docs/websockets/) support
//! * Transparent content compression/decompression (br, gzip, deflate)
-//! * Configurable request routing
+//! * Powerful [request routing](https://actix.rs/docs/url-dispatch/)
//! * Multipart streams
-//! * SSL support with OpenSSL or `native-tls`
-//! * Middlewares (`Logger`, `Session`, `CORS`, `DefaultHeaders`)
+//! * Static assets
+//! * SSL support using OpenSSL or Rustls
+//! * Middlewares ([Logger, Session, CORS, etc](https://actix.rs/docs/middleware/))
+//! * Includes an async [HTTP client](https://actix.rs/actix-web/actix_web/client/index.html)
//! * Supports [Actix actor framework](https://github.com/actix/actix)
-//! * Supported Rust version: 1.40 or later
+//! * Runs on stable Rust 1.41+
//!
-//! ## Package feature
+//! ## Crate Features
//!
-//! * `client` - enables http client (default enabled)
-//! * `compress` - enables content encoding compression support (default enabled)
-//! * `openssl` - enables ssl support via `openssl` crate, supports `http/2`
-//! * `rustls` - enables ssl support via `rustls` crate, supports `http/2`
-//! * `secure-cookies` - enables secure cookies support
+//! * `compress` - content encoding compression support (enabled by default)
+//! * `openssl` - HTTPS support via `openssl` crate, supports `HTTP/2`
+//! * `rustls` - HTTPS support via `rustls` crate, supports `HTTP/2`
+//! * `secure-cookies` - secure cookies support
mod app;
mod app_service;
@@ -97,12 +90,10 @@ pub mod test;
mod types;
pub mod web;
-pub use actix_web_codegen::*;
-pub use actix_rt as rt;
-
-// re-export for convenience
pub use actix_http::Response as HttpResponse;
pub use actix_http::{body, cookie, http, Error, HttpMessage, ResponseError, Result};
+pub use actix_rt as rt;
+pub use actix_web_codegen::*;
pub use crate::app::App;
pub use crate::extract::FromRequest;
@@ -203,19 +194,20 @@ pub mod dev {
}
pub mod client {
- //! An HTTP Client
+ //! Actix web async HTTP client.
//!
//! ```rust
//! use actix_web::client::Client;
//!
- //! #[actix_rt::main]
+ //! #[actix_web::main]
//! async fn main() {
//! let mut client = Client::default();
//!
//! // Create request builder and send request
//! let response = client.get("http://www.rust-lang.org")
- //! .header("User-Agent", "Actix-web")
- //! .send().await; // <- Send http request
+ //! .header("User-Agent", "actix-web/3.0")
+ //! .send() // <- Send request
+ //! .await; // <- Wait for response
//!
//! println!("Response: {:?}", response);
//! }
From 08f9a340754fbf9b62bf88440e8fa46e32a8f8fb Mon Sep 17 00:00:00 2001
From: Yuki Okushi
Date: Tue, 7 Jul 2020 04:00:18 +0900
Subject: [PATCH 15/78] Use tarpaulin 0.13 to avoid failure
---
.github/workflows/linux.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml
index a2dfc5da6..7529c8494 100644
--- a/.github/workflows/linux.yml
+++ b/.github/workflows/linux.yml
@@ -55,7 +55,7 @@ jobs:
- name: Generate coverage file
if: matrix.version == 'stable' && (github.ref == 'refs/heads/master' || github.event_name == 'pull_request')
run: |
- cargo install cargo-tarpaulin
+ cargo install cargo-tarpaulin --vers "^0.13"
cargo tarpaulin --out Xml
- name: Upload to Codecov
if: matrix.version == 'stable' && (github.ref == 'refs/heads/master' || github.event_name == 'pull_request')
From a2662b928bda6c79110d04b73db048ea530be5e7 Mon Sep 17 00:00:00 2001
From: Rob Ede
Date: Fri, 10 Jul 2020 14:55:56 +0100
Subject: [PATCH 16/78] Update PULL_REQUEST_TEMPLATE.md
---
.github/PULL_REQUEST_TEMPLATE.md | 8 ++------
1 file changed, 2 insertions(+), 6 deletions(-)
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 6426eab65..cc34e2bc6 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -1,12 +1,8 @@
## PR Type
What kind of change does this PR make?
-
-
-- [ ] Bug fix
-- [ ] Feature
-- [ ] Refactor / code style change (no functional or public API changes)
-- [ ] Other
+
+INSERT_PR_TYPE
## PR Checklist
From e10eb648d94ad2b4edaef648847360be42d51b82 Mon Sep 17 00:00:00 2001
From: Patrick Tescher
Date: Fri, 10 Jul 2020 14:35:22 -0700
Subject: [PATCH 17/78] Fix leaks with actix_http's client (#1580)
---
actix-http/CHANGES.md | 1 +
actix-http/src/client/pool.rs | 117 ++++++++++++++++++----------------
awc/src/request.rs | 28 ++++----
benches/server.rs | 7 +-
4 files changed, 81 insertions(+), 72 deletions(-)
diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md
index d18af162d..138210f95 100644
--- a/actix-http/CHANGES.md
+++ b/actix-http/CHANGES.md
@@ -7,6 +7,7 @@
* Migrate cookie handling to `cookie` crate.
* Update `sha-1` to 0.9
* MSRV is now 1.41.1
+* Fix client leak [#1580]
## [2.0.0-alpha.4] - 2020-05-21
diff --git a/actix-http/src/client/pool.rs b/actix-http/src/client/pool.rs
index 5a10725b0..3ce443794 100644
--- a/actix-http/src/client/pool.rs
+++ b/actix-http/src/client/pool.rs
@@ -2,7 +2,7 @@ use std::cell::RefCell;
use std::collections::VecDeque;
use std::future::Future;
use std::pin::Pin;
-use std::rc::Rc;
+use std::rc::{Rc, Weak};
use std::task::{Context, Poll};
use std::time::{Duration, Instant};
@@ -53,16 +53,25 @@ where
+ 'static,
{
pub(crate) fn new(connector: T, config: ConnectorConfig) -> Self {
+ let connector_rc = Rc::new(RefCell::new(connector));
+ let inner_rc = Rc::new(RefCell::new(Inner {
+ config,
+ acquired: 0,
+ waiters: Slab::new(),
+ waiters_queue: IndexSet::new(),
+ available: FxHashMap::default(),
+ waker: LocalWaker::new(),
+ }));
+
+ // start support future
+ actix_rt::spawn(ConnectorPoolSupport {
+ connector: connector_rc.clone(),
+ inner: Rc::downgrade(&inner_rc),
+ });
+
ConnectionPool(
- Rc::new(RefCell::new(connector)),
- Rc::new(RefCell::new(Inner {
- config,
- acquired: 0,
- waiters: Slab::new(),
- waiters_queue: IndexSet::new(),
- available: FxHashMap::default(),
- waker: LocalWaker::new(),
- })),
+ connector_rc,
+ inner_rc,
)
}
}
@@ -92,12 +101,6 @@ where
}
fn call(&mut self, req: Connect) -> Self::Future {
- // start support future
- actix_rt::spawn(ConnectorPoolSupport {
- connector: self.0.clone(),
- inner: self.1.clone(),
- });
-
let mut connector = self.0.clone();
let inner = self.1.clone();
@@ -421,7 +424,7 @@ where
Io: AsyncRead + AsyncWrite + Unpin + 'static,
{
connector: T,
- inner: Rc>>,
+ inner: Weak>>,
}
impl Future for ConnectorPoolSupport
@@ -435,51 +438,55 @@ where
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll {
let this = self.project();
- let mut inner = this.inner.as_ref().borrow_mut();
- inner.waker.register(cx.waker());
+ if let Some(this_inner) = this.inner.upgrade() {
+ let mut inner = this_inner.as_ref().borrow_mut();
+ inner.waker.register(cx.waker());
- // check waiters
- loop {
- let (key, token) = {
- if let Some((key, token)) = inner.waiters_queue.get_index(0) {
- (key.clone(), *token)
- } else {
- break;
+ // check waiters
+ loop {
+ let (key, token) = {
+ if let Some((key, token)) = inner.waiters_queue.get_index(0) {
+ (key.clone(), *token)
+ } else {
+ break;
+ }
+ };
+ if inner.waiters.get(token).unwrap().is_none() {
+ continue;
}
- };
- if inner.waiters.get(token).unwrap().is_none() {
- continue;
- }
- match inner.acquire(&key, cx) {
- Acquire::NotAvailable => break,
- Acquire::Acquired(io, created) => {
- let tx = inner.waiters.get_mut(token).unwrap().take().unwrap().1;
- if let Err(conn) = tx.send(Ok(IoConnection::new(
- io,
- created,
- Some(Acquired(key.clone(), Some(this.inner.clone()))),
- ))) {
- let (io, created) = conn.unwrap().into_inner();
- inner.release_conn(&key, io, created);
+ match inner.acquire(&key, cx) {
+ Acquire::NotAvailable => break,
+ Acquire::Acquired(io, created) => {
+ let tx = inner.waiters.get_mut(token).unwrap().take().unwrap().1;
+ if let Err(conn) = tx.send(Ok(IoConnection::new(
+ io,
+ created,
+ Some(Acquired(key.clone(), Some(this_inner.clone()))),
+ ))) {
+ let (io, created) = conn.unwrap().into_inner();
+ inner.release_conn(&key, io, created);
+ }
+ }
+ Acquire::Available => {
+ let (connect, tx) =
+ inner.waiters.get_mut(token).unwrap().take().unwrap();
+ OpenWaitingConnection::spawn(
+ key.clone(),
+ tx,
+ this_inner.clone(),
+ this.connector.call(connect),
+ inner.config.clone(),
+ );
}
}
- Acquire::Available => {
- let (connect, tx) =
- inner.waiters.get_mut(token).unwrap().take().unwrap();
- OpenWaitingConnection::spawn(
- key.clone(),
- tx,
- this.inner.clone(),
- this.connector.call(connect),
- inner.config.clone(),
- );
- }
+ let _ = inner.waiters_queue.swap_remove_index(0);
}
- let _ = inner.waiters_queue.swap_remove_index(0);
- }
- Poll::Pending
+ Poll::Pending
+ } else {
+ Poll::Ready(())
+ }
}
}
diff --git a/awc/src/request.rs b/awc/src/request.rs
index 3dd8cb2ce..c34a8e221 100644
--- a/awc/src/request.rs
+++ b/awc/src/request.rs
@@ -586,16 +586,16 @@ mod tests {
use super::*;
use crate::Client;
- #[test]
- fn test_debug() {
+ #[actix_rt::test]
+ async fn test_debug() {
let request = Client::new().get("/").header("x-test", "111");
let repr = format!("{:?}", request);
assert!(repr.contains("ClientRequest"));
assert!(repr.contains("x-test"));
}
- #[test]
- fn test_basics() {
+ #[actix_rt::test]
+ async fn test_basics() {
let mut req = Client::new()
.put("/")
.version(Version::HTTP_2)
@@ -621,8 +621,8 @@ mod tests {
let _ = req.send_body("");
}
- #[test]
- fn test_client_header() {
+ #[actix_rt::test]
+ async fn test_client_header() {
let req = Client::build()
.header(header::CONTENT_TYPE, "111")
.finish()
@@ -639,8 +639,8 @@ mod tests {
);
}
- #[test]
- fn test_client_header_override() {
+ #[actix_rt::test]
+ async fn test_client_header_override() {
let req = Client::build()
.header(header::CONTENT_TYPE, "111")
.finish()
@@ -658,8 +658,8 @@ mod tests {
);
}
- #[test]
- fn client_basic_auth() {
+ #[actix_rt::test]
+ async fn client_basic_auth() {
let req = Client::new()
.get("/")
.basic_auth("username", Some("password"));
@@ -685,8 +685,8 @@ mod tests {
);
}
- #[test]
- fn client_bearer_auth() {
+ #[actix_rt::test]
+ async fn client_bearer_auth() {
let req = Client::new().get("/").bearer_auth("someS3cr3tAutht0k3n");
assert_eq!(
req.head
@@ -699,8 +699,8 @@ mod tests {
);
}
- #[test]
- fn client_query() {
+ #[actix_rt::test]
+ async fn client_query() {
let req = Client::new()
.get("/")
.query(&[("key1", "val1"), ("key2", "val2")])
diff --git a/benches/server.rs b/benches/server.rs
index 70531adf7..041d0fa57 100644
--- a/benches/server.rs
+++ b/benches/server.rs
@@ -27,15 +27,16 @@ const STR: &str = "Hello World Hello World Hello World Hello World Hello World \
// benchmark sending all requests at the same time
fn bench_async_burst(c: &mut Criterion) {
+ // We are using System here, since Runtime requires preinitialized tokio
+ // Maybe add to actix_rt docs
+ let mut rt = actix_rt::System::new("test");
+
let srv = test::start(|| {
App::new()
.service(web::resource("/").route(web::to(|| HttpResponse::Ok().body(STR))))
});
- // We are using System here, since Runtime requires preinitialized tokio
- // Maybe add to actix_rt docs
let url = srv.url("/");
- let mut rt = actix_rt::System::new("test");
c.bench_function("get_body_async_burst", move |b| {
b.iter_custom(|iters| {
From 327e472e440c98815c4705c2dcec0e1f9ffaac18 Mon Sep 17 00:00:00 2001
From: Rob Ede
Date: Mon, 13 Jul 2020 15:35:30 +0100
Subject: [PATCH 18/78] prepare http-2.0.0-beta.1 release (#1596)
---
actix-http/CHANGES.md | 16 +++++++++++-----
actix-http/Cargo.toml | 4 ++--
2 files changed, 13 insertions(+), 7 deletions(-)
diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md
index 138210f95..a185a9f81 100644
--- a/actix-http/CHANGES.md
+++ b/actix-http/CHANGES.md
@@ -1,13 +1,19 @@
# Changes
-## [Unreleased]
+## [Unreleased] - xxx
+
+## [2.0.0-beta.1] - 2020-07-11
### Changed
-* Migrate cookie handling to `cookie` crate.
-* Update `sha-1` to 0.9
-* MSRV is now 1.41.1
-* Fix client leak [#1580]
+* Migrate cookie handling to `cookie` crate. [#1558]
+* Update `sha-1` to 0.9. [#1586]
+* Fix leak in client pool. [#1580]
+* MSRV is now 1.41.1.
+
+[#1558]: https://github.com/actix/actix-web/pull/1558
+[#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
diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml
index 6095f89ea..a9bec59de 100644
--- a/actix-http/Cargo.toml
+++ b/actix-http/Cargo.toml
@@ -1,8 +1,8 @@
[package]
name = "actix-http"
-version = "2.0.0-alpha.4"
+version = "2.0.0-beta.1"
authors = ["Nikolay Kim "]
-description = "Actix http primitives"
+description = "Actix HTTP primitives"
readme = "README.md"
keywords = ["actix", "http", "framework", "async", "futures"]
homepage = "https://actix.rs"
From 78594a72bdd5705531e0974a65685e770f7832b9 Mon Sep 17 00:00:00 2001
From: Rob Ede
Date: Tue, 14 Jul 2020 03:16:26 +0100
Subject: [PATCH 19/78] prepare awc v2.0.0-beta.1 release
---
awc/CHANGES.md | 4 ++++
awc/Cargo.toml | 6 +++---
test-server/Cargo.toml | 4 ++--
3 files changed, 9 insertions(+), 5 deletions(-)
diff --git a/awc/CHANGES.md b/awc/CHANGES.md
index 6d5a81b5e..19154d35d 100644
--- a/awc/CHANGES.md
+++ b/awc/CHANGES.md
@@ -1,5 +1,9 @@
# Changes
+## [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
### Changed
diff --git a/awc/Cargo.toml b/awc/Cargo.toml
index b36e735ca..e0f89c8dc 100644
--- a/awc/Cargo.toml
+++ b/awc/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "awc"
-version = "2.0.0-alpha.2"
+version = "2.0.0-beta.1"
authors = ["Nikolay Kim "]
description = "Actix http client."
readme = "README.md"
@@ -36,7 +36,7 @@ compress = ["actix-http/compress"]
[dependencies]
actix-codec = "0.2.0"
actix-service = "1.0.1"
-actix-http = "2.0.0-alpha.4"
+actix-http = "2.0.0-beta.1"
actix-rt = "1.0.0"
base64 = "0.12"
@@ -56,7 +56,7 @@ rust-tls = { version = "0.17.0", package = "rustls", optional = true, features =
[dev-dependencies]
actix-connect = { version = "2.0.0-alpha.2", features = ["openssl"] }
actix-web = { version = "3.0.0-alpha.3", features = ["openssl"] }
-actix-http = { version = "2.0.0-alpha.4", features = ["openssl"] }
+actix-http = { version = "2.0.0-beta.1", features = ["openssl"] }
actix-http-test = { version = "2.0.0-alpha.1", features = ["openssl"] }
actix-utils = "1.0.3"
actix-server = "1.0.0"
diff --git a/test-server/Cargo.toml b/test-server/Cargo.toml
index 2c526e672..aeb40daee 100644
--- a/test-server/Cargo.toml
+++ b/test-server/Cargo.toml
@@ -2,7 +2,7 @@
name = "actix-http-test"
version = "2.0.0-alpha.1"
authors = ["Nikolay Kim "]
-description = "Actix http test server"
+description = "Actix HTTP test server"
readme = "README.md"
keywords = ["http", "web", "framework", "async", "futures"]
homepage = "https://actix.rs"
@@ -53,4 +53,4 @@ open-ssl = { version = "0.10", package = "openssl", optional = true }
[dev-dependencies]
actix-web = "3.0.0-alpha.3"
-actix-http = "2.0.0-alpha.4"
+actix-http = "2.0.0-beta.1"
From 1382094c152d9d201513ae280934d58fc5158b40 Mon Sep 17 00:00:00 2001
From: Yuki Okushi
Date: Tue, 14 Jul 2020 11:19:56 +0900
Subject: [PATCH 20/78] Avoid using deprecated `/` in license field
---
Cargo.toml | 2 +-
actix-files/Cargo.toml | 2 +-
actix-http/Cargo.toml | 2 +-
actix-multipart/Cargo.toml | 2 +-
actix-web-actors/Cargo.toml | 2 +-
actix-web-codegen/Cargo.toml | 2 +-
awc/Cargo.toml | 2 +-
test-server/Cargo.toml | 2 +-
8 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/Cargo.toml b/Cargo.toml
index 8c4719b92..146c4004f 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -11,7 +11,7 @@ documentation = "https://docs.rs/actix-web/"
categories = ["network-programming", "asynchronous",
"web-programming::http-server",
"web-programming::websocket"]
-license = "MIT/Apache-2.0"
+license = "MIT OR Apache-2.0"
edition = "2018"
[package.metadata.docs.rs]
diff --git a/actix-files/Cargo.toml b/actix-files/Cargo.toml
index 12fd03ce1..b7ba75960 100644
--- a/actix-files/Cargo.toml
+++ b/actix-files/Cargo.toml
@@ -9,7 +9,7 @@ homepage = "https://actix.rs"
repository = "https://github.com/actix/actix-web.git"
documentation = "https://docs.rs/actix-files/"
categories = ["asynchronous", "web-programming::http-server"]
-license = "MIT/Apache-2.0"
+license = "MIT OR Apache-2.0"
edition = "2018"
[lib]
diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml
index a9bec59de..232b5c3f0 100644
--- a/actix-http/Cargo.toml
+++ b/actix-http/Cargo.toml
@@ -11,7 +11,7 @@ documentation = "https://docs.rs/actix-http/"
categories = ["network-programming", "asynchronous",
"web-programming::http-server",
"web-programming::websocket"]
-license = "MIT/Apache-2.0"
+license = "MIT OR Apache-2.0"
edition = "2018"
[package.metadata.docs.rs]
diff --git a/actix-multipart/Cargo.toml b/actix-multipart/Cargo.toml
index c5f315d75..f6d6a37a1 100644
--- a/actix-multipart/Cargo.toml
+++ b/actix-multipart/Cargo.toml
@@ -8,7 +8,7 @@ keywords = ["http", "web", "framework", "async", "futures"]
homepage = "https://actix.rs"
repository = "https://github.com/actix/actix-web.git"
documentation = "https://docs.rs/actix-multipart/"
-license = "MIT/Apache-2.0"
+license = "MIT OR Apache-2.0"
edition = "2018"
[lib]
diff --git a/actix-web-actors/Cargo.toml b/actix-web-actors/Cargo.toml
index 746aca2d5..7383b2032 100644
--- a/actix-web-actors/Cargo.toml
+++ b/actix-web-actors/Cargo.toml
@@ -8,7 +8,7 @@ keywords = ["actix", "http", "web", "framework", "async"]
homepage = "https://actix.rs"
repository = "https://github.com/actix/actix-web.git"
documentation = "https://docs.rs/actix-web-actors/"
-license = "MIT/Apache-2.0"
+license = "MIT OR Apache-2.0"
edition = "2018"
[lib]
diff --git a/actix-web-codegen/Cargo.toml b/actix-web-codegen/Cargo.toml
index 178eeeb7e..10c33ea74 100644
--- a/actix-web-codegen/Cargo.toml
+++ b/actix-web-codegen/Cargo.toml
@@ -7,7 +7,7 @@ homepage = "https://actix.rs"
repository = "https://github.com/actix/actix-web"
documentation = "https://docs.rs/actix-web-codegen"
authors = ["Nikolay Kim "]
-license = "MIT/Apache-2.0"
+license = "MIT OR Apache-2.0"
edition = "2018"
[lib]
diff --git a/awc/Cargo.toml b/awc/Cargo.toml
index b36e735ca..563c5cae5 100644
--- a/awc/Cargo.toml
+++ b/awc/Cargo.toml
@@ -11,7 +11,7 @@ documentation = "https://docs.rs/awc/"
categories = ["network-programming", "asynchronous",
"web-programming::http-client",
"web-programming::websocket"]
-license = "MIT/Apache-2.0"
+license = "MIT OR Apache-2.0"
edition = "2018"
[lib]
diff --git a/test-server/Cargo.toml b/test-server/Cargo.toml
index 2c526e672..1cdf77031 100644
--- a/test-server/Cargo.toml
+++ b/test-server/Cargo.toml
@@ -11,7 +11,7 @@ documentation = "https://docs.rs/actix-http-test/"
categories = ["network-programming", "asynchronous",
"web-programming::http-server",
"web-programming::websocket"]
-license = "MIT/Apache-2.0"
+license = "MIT OR Apache-2.0"
exclude = [".gitignore", ".cargo/config"]
edition = "2018"
From ad7c6d26332395e4693a68da8c15de799b259ca5 Mon Sep 17 00:00:00 2001
From: Rob Ede
Date: Wed, 15 Jul 2020 00:44:44 +0100
Subject: [PATCH 21/78] prepare actix-web v3.0.0-beta.1 release (#1600)
---
CHANGES.md | 14 ++++----------
Cargo.toml | 6 +++---
actix-web-codegen/CHANGES.md | 11 ++++++-----
actix-web-codegen/Cargo.toml | 4 ++--
4 files changed, 15 insertions(+), 20 deletions(-)
diff --git a/CHANGES.md b/CHANGES.md
index 8f15eea15..149ff8fcb 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,38 +1,32 @@
# Changes
-## [Unreleased]
+## Unreleased - 2020-xx-xx
+
+## 3.0.0-beta.1 - 2020-07-13
### Added
-
* Re-export `actix_rt::main` as `actix_web::main`.
* `HttpRequest::match_pattern` and `ServiceRequest::match_pattern` for extracting the matched
resource pattern.
* `HttpRequest::match_name` and `ServiceRequest::match_name` for extracting matched resource name.
### Changed
-
* Fix actix_http::h1::dispatcher so it returns when HW_BUFFER_SIZE is reached. Should reduce peak memory consumption during large uploads. [#1550]
* Migrate cookie handling to `cookie` crate. Actix-web no longer requires `ring` dependency.
* MSRV is now 1.41.1
### Fixed
-
* `NormalizePath` improved consistency when path needs slashes added _and_ removed.
-## [3.0.0-alpha.3] - 2020-05-21
+## 3.0.0-alpha.3 - 2020-05-21
### Added
-
* Add option to create `Data` from `Arc` [#1509]
### Changed
-
* Resources and Scopes can now access non-overridden data types set on App (or containing scopes) when setting their own data. [#1486]
-
* Fix audit issue logging by default peer address [#1485]
-
* Bump minimum supported Rust version to 1.40
-
* Replace deprecated `net2` crate with `socket2`
[#1485]: https://github.com/actix/actix-web/pull/1485
diff --git a/Cargo.toml b/Cargo.toml
index 146c4004f..49de9fd96 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "actix-web"
-version = "3.0.0-alpha.3"
+version = "3.0.0-beta.1"
authors = ["Nikolay Kim "]
description = "Actix web is a simple, pragmatic and extremely fast web framework for Rust."
readme = "README.md"
@@ -76,9 +76,9 @@ actix-macros = "0.1.0"
actix-threadpool = "0.3.1"
actix-tls = "2.0.0-alpha.1"
-actix-web-codegen = "0.2.2"
+actix-web-codegen = "0.3.0-beta.1"
actix-http = "2.0.0-alpha.4"
-awc = { version = "2.0.0-alpha.2", default-features = false }
+awc = { version = "2.0.0-beta.1", default-features = false }
bytes = "0.5.3"
derive_more = "0.99.2"
diff --git a/actix-web-codegen/CHANGES.md b/actix-web-codegen/CHANGES.md
index 8259aaac2..242c5f8de 100644
--- a/actix-web-codegen/CHANGES.md
+++ b/actix-web-codegen/CHANGES.md
@@ -1,18 +1,20 @@
# Changes
-## [Unreleased] - XXXX-XX-XX
+## Unreleased - 2020-xx-xx
-* Add main entry-point macro that uses re-exported runtime.
+
+## 0.3.0-beta.1 - 2020-07-14
+* Add main entry-point macro that uses re-exported runtime. [#1559]
+
+[#1559]: https://github.com/actix/actix-web/pull/1559
## [0.2.2] - 2020-05-23
-
* Add resource middleware on actix-web-codegen [#1467]
[#1467]: https://github.com/actix/actix-web/pull/1467
## [0.2.1] - 2020-02-25
-
* Add `#[allow(missing_docs)]` attribute to generated structs [#1368]
* Allow the handler function to be named as `config` [#1290]
@@ -26,7 +28,6 @@
## [0.1.3] - 2019-10-14
* Bump up `syn` & `quote` to 1.0
-
* Provide better error message
## [0.1.2] - 2019-06-04
diff --git a/actix-web-codegen/Cargo.toml b/actix-web-codegen/Cargo.toml
index 10c33ea74..070c09899 100644
--- a/actix-web-codegen/Cargo.toml
+++ b/actix-web-codegen/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "actix-web-codegen"
-version = "0.2.2"
+version = "0.3.0-beta.1"
description = "Actix web proc macros"
readme = "README.md"
homepage = "https://actix.rs"
@@ -20,5 +20,5 @@ proc-macro2 = "1"
[dev-dependencies]
actix-rt = "1.0.0"
-actix-web = "3.0.0-alpha.3"
+actix-web = "3.0.0-beta.1"
futures-util = { version = "0.3.5", default-features = false }
From 2fd96c03e5a2d93671412252d864859e5de1b345 Mon Sep 17 00:00:00 2001
From: Rob Ede
Date: Thu, 16 Jul 2020 11:38:57 +0100
Subject: [PATCH 22/78] prepare beta.1 release for multipart/files/actors
(#1605)
---
actix-files/CHANGES.md | 5 +++--
actix-files/Cargo.toml | 8 ++++----
actix-multipart/CHANGES.md | 12 +++++++-----
actix-multipart/Cargo.toml | 6 +++---
actix-web-actors/CHANGES.md | 5 ++++-
actix-web-actors/Cargo.toml | 6 +++---
6 files changed, 24 insertions(+), 18 deletions(-)
diff --git a/actix-files/CHANGES.md b/actix-files/CHANGES.md
index 5d1845e37..75d616ff9 100644
--- a/actix-files/CHANGES.md
+++ b/actix-files/CHANGES.md
@@ -1,11 +1,12 @@
# Changes
-## [unreleased] - xxx
+## [Unreleased] - 2020-xx-xx
+## [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
diff --git a/actix-files/Cargo.toml b/actix-files/Cargo.toml
index b7ba75960..379ba8f89 100644
--- a/actix-files/Cargo.toml
+++ b/actix-files/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "actix-files"
-version = "0.3.0-alpha.1"
+version = "0.3.0-beta.1"
authors = ["Nikolay Kim "]
description = "Static files support for actix web."
readme = "README.md"
@@ -17,8 +17,8 @@ name = "actix_files"
path = "src/lib.rs"
[dependencies]
-actix-web = { version = "3.0.0-alpha.3", default-features = false }
-actix-http = "2.0.0-alpha.4"
+actix-web = { version = "3.0.0-beta.1", default-features = false }
+actix-http = "2.0.0-beta.1"
actix-service = "1.0.1"
bitflags = "1"
bytes = "0.5.3"
@@ -33,4 +33,4 @@ v_htmlescape = "0.10"
[dev-dependencies]
actix-rt = "1.0.0"
-actix-web = { version = "3.0.0-alpha.3", features = ["openssl"] }
+actix-web = { version = "3.0.0-beta.1", features = ["openssl"] }
diff --git a/actix-multipart/CHANGES.md b/actix-multipart/CHANGES.md
index df3cecf71..261836223 100644
--- a/actix-multipart/CHANGES.md
+++ b/actix-multipart/CHANGES.md
@@ -1,15 +1,17 @@
# Changes
-## [0.3.0-alpha.1] - 2020-05-25
+## Unreleased - 2020-xx-xx
+
+## 0.3.0-beta.1 - 2020-07-15
+* Update `actix-web` to 3.0.0-beta.1
+
+
+## 0.3.0-alpha.1 - 2020-05-25
* Update `actix-web` to 3.0.0-alpha.3
-
* Bump minimum supported Rust version to 1.40
-
* Minimize `futures` dependencies
-
* Remove the unused `time` dependency
-
* Fix missing `std::error::Error` implement for `MultipartError`.
## [0.2.0] - 2019-12-20
diff --git a/actix-multipart/Cargo.toml b/actix-multipart/Cargo.toml
index f6d6a37a1..7e81035cc 100644
--- a/actix-multipart/Cargo.toml
+++ b/actix-multipart/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "actix-multipart"
-version = "0.3.0-alpha.1"
+version = "0.3.0-beta.1"
authors = ["Nikolay Kim "]
description = "Multipart support for actix web framework."
readme = "README.md"
@@ -16,7 +16,7 @@ name = "actix_multipart"
path = "src/lib.rs"
[dependencies]
-actix-web = { version = "3.0.0-alpha.3", default-features = false }
+actix-web = { version = "3.0.0-beta.1", default-features = false }
actix-service = "1.0.1"
actix-utils = "1.0.3"
bytes = "0.5.3"
@@ -29,4 +29,4 @@ twoway = "0.2"
[dev-dependencies]
actix-rt = "1.0.0"
-actix-http = "2.0.0-alpha.4"
+actix-http = "2.0.0-beta.1"
diff --git a/actix-web-actors/CHANGES.md b/actix-web-actors/CHANGES.md
index 8fd48f77c..868f17e2a 100644
--- a/actix-web-actors/CHANGES.md
+++ b/actix-web-actors/CHANGES.md
@@ -2,10 +2,13 @@
## [Unreleased] - 2020-xx-xx
+
+## [3.0.0-beta.1] - 2020-xx-xx
+* Update `actix-web` & `actix-http` dependencies to beta.1
* Bump minimum supported Rust version to 1.40
-## [3.0.0-alpha.1] - 2020-05-08
+## [3.0.0-alpha.1] - 2020-05-08
* Update the actix-web dependency to 3.0.0-alpha.1
* Update the actix dependency to 0.10.0-alpha.2
* Update the actix-http dependency to 2.0.0-alpha.3
diff --git a/actix-web-actors/Cargo.toml b/actix-web-actors/Cargo.toml
index 7383b2032..8de679da5 100644
--- a/actix-web-actors/Cargo.toml
+++ b/actix-web-actors/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "actix-web-actors"
-version = "3.0.0-alpha.1"
+version = "3.0.0-beta.1"
authors = ["Nikolay Kim "]
description = "Actix actors support for actix web framework."
readme = "README.md"
@@ -17,8 +17,8 @@ path = "src/lib.rs"
[dependencies]
actix = "0.10.0-alpha.2"
-actix-web = { version = "3.0.0-alpha.3", default-features = false }
-actix-http = "2.0.0-alpha.4"
+actix-web = { version = "3.0.0-beta.1", default-features = false }
+actix-http = "2.0.0-beta.1"
actix-codec = "0.2.0"
bytes = "0.5.2"
futures-channel = { version = "0.3.5", default-features = false }
From 971ba3eee14b4191ccddd6ddae0a5c135ae789e9 Mon Sep 17 00:00:00 2001
From: Rob Ede
Date: Sat, 18 Jul 2020 16:17:00 +0100
Subject: [PATCH 23/78] fix continous growth of app data in pooled requests
(#1609)
fixes #1606
fixes #1607
---
CHANGES.md | 4 ++++
src/app_service.rs | 3 ++-
src/request.rs | 22 +++++++++++++++-------
3 files changed, 21 insertions(+), 8 deletions(-)
diff --git a/CHANGES.md b/CHANGES.md
index 149ff8fcb..fca46d1c1 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,6 +1,10 @@
# Changes
## Unreleased - 2020-xx-xx
+### Fixed
+* Memory leak of app data in pooled requests. [#1609]
+
+[#1609]: https://github.com/actix/actix-web/pull/1609
## 3.0.0-beta.1 - 2020-07-13
diff --git a/src/app_service.rs b/src/app_service.rs
index 233cfc0d5..d41cee9fd 100644
--- a/src/app_service.rs
+++ b/src/app_service.rs
@@ -10,6 +10,7 @@ use actix_router::{Path, ResourceDef, ResourceInfo, Router, Url};
use actix_service::boxed::{self, BoxService, BoxServiceFactory};
use actix_service::{fn_service, Service, ServiceFactory};
use futures_util::future::{join_all, ok, FutureExt, LocalBoxFuture};
+use tinyvec::tiny_vec;
use crate::config::{AppConfig, AppService};
use crate::data::{DataFactory, FnDataFactory};
@@ -245,7 +246,7 @@ where
inner.path.reset();
inner.head = head;
inner.payload = payload;
- inner.app_data.push(self.data.clone());
+ inner.app_data = tiny_vec![self.data.clone()];
req
} else {
HttpRequest::new(
diff --git a/src/request.rs b/src/request.rs
index 85f409016..a1b42f926 100644
--- a/src/request.rs
+++ b/src/request.rs
@@ -276,6 +276,7 @@ impl HttpMessage for HttpRequest {
impl Drop for HttpRequest {
fn drop(&mut self) {
+ // if possible, contribute to current worker's HttpRequest allocation pool
if Rc::strong_count(&self.0) == 1 {
let v = &mut self.0.pool.0.borrow_mut();
if v.len() < 128 {
@@ -340,25 +341,32 @@ impl fmt::Debug for HttpRequest {
}
}
-/// Request's objects pool
+/// Slab-allocated `HttpRequest` Pool
+///
+/// Since request processing may yield for asynchronous events to complete, a worker may have many
+/// requests in-flight at any time. Pooling requests like this amortizes the performance and memory
+/// costs of allocating and de-allocating HttpRequest objects as frequently as they otherwise would.
+///
+/// Request objects are added when they are dropped (see `::drop`) and re-used
+/// in `::call` when there are available objects in the list.
+///
+/// The pool's initial capacity is 128 items.
pub(crate) struct HttpRequestPool(RefCell>>);
impl HttpRequestPool {
+ /// Allocates a slab of memory for pool use.
pub(crate) fn create() -> &'static HttpRequestPool {
let pool = HttpRequestPool(RefCell::new(Vec::with_capacity(128)));
Box::leak(Box::new(pool))
}
- /// Get message from the pool
+ /// Re-use a previously allocated (but now completed/discarded) HttpRequest object.
#[inline]
pub(crate) fn get_request(&self) -> Option {
- if let Some(inner) = self.0.borrow_mut().pop() {
- Some(HttpRequest(inner))
- } else {
- None
- }
+ self.0.borrow_mut().pop().map(HttpRequest)
}
+ /// Clears all allocated HttpRequest objects.
pub(crate) fn clear(&self) {
self.0.borrow_mut().clear()
}
From 43c362779dabb6f021d42e345d214aaea043adf9 Mon Sep 17 00:00:00 2001
From: Rob Ede
Date: Mon, 20 Jul 2020 17:40:58 +0100
Subject: [PATCH 24/78] also try extracting payload config as Data (#1610)
---
CHANGES.md | 5 ++
src/types/payload.rs | 133 ++++++++++++++++++++++++++++++++++++-------
2 files changed, 116 insertions(+), 22 deletions(-)
diff --git a/CHANGES.md b/CHANGES.md
index fca46d1c1..4b6697a7f 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,10 +1,15 @@
# Changes
## Unreleased - 2020-xx-xx
+### Changed
+* `PayloadConfig` is now also considered in `Bytes` and `String` extractors when set
+ using `App::data`. [#1610]
+
### Fixed
* Memory leak of app data in pooled requests. [#1609]
[#1609]: https://github.com/actix/actix-web/pull/1609
+[#1610]: https://github.com/actix/actix-web/pull/1610
## 3.0.0-beta.1 - 2020-07-13
diff --git a/src/types/payload.rs b/src/types/payload.rs
index 0efdc2c09..653abf089 100644
--- a/src/types/payload.rs
+++ b/src/types/payload.rs
@@ -13,10 +13,10 @@ use futures_util::future::{err, ok, Either, FutureExt, LocalBoxFuture, Ready};
use futures_util::StreamExt;
use mime::Mime;
-use crate::dev;
use crate::extract::FromRequest;
use crate::http::header;
use crate::request::HttpRequest;
+use crate::{dev, web};
/// Payload extractor returns request 's payload stream.
///
@@ -142,13 +142,8 @@ impl FromRequest for Bytes {
#[inline]
fn from_request(req: &HttpRequest, payload: &mut dev::Payload) -> Self::Future {
- let tmp;
- let cfg = if let Some(cfg) = req.app_data::() {
- cfg
- } else {
- tmp = PayloadConfig::default();
- &tmp
- };
+ // allow both Config and Data
+ let cfg = PayloadConfig::from_req(req);
if let Err(e) = cfg.check_mimetype(req) {
return Either::Right(err(e));
@@ -197,13 +192,7 @@ impl FromRequest for String {
#[inline]
fn from_request(req: &HttpRequest, payload: &mut dev::Payload) -> Self::Future {
- let tmp;
- let cfg = if let Some(cfg) = req.app_data::() {
- cfg
- } else {
- tmp = PayloadConfig::default();
- &tmp
- };
+ let cfg = PayloadConfig::from_req(req);
// check content-type
if let Err(e) = cfg.check_mimetype(req) {
@@ -237,7 +226,12 @@ impl FromRequest for String {
)
}
}
-/// Payload configuration for request's payload.
+
+/// Configuration for request's payload.
+///
+/// Applies to the built-in `Bytes` and `String` extractors. Note that the Payload extractor does
+/// not automatically check conformance with this configuration to allow more flexibility when
+/// building extractors on top of `Payload`.
#[derive(Clone)]
pub struct PayloadConfig {
limit: usize,
@@ -284,14 +278,28 @@ impl PayloadConfig {
}
Ok(())
}
+
+ /// Allow payload config extraction from app data checking both `T` and `Data`, in that
+ /// order, and falling back to the default payload config.
+ fn from_req(req: &HttpRequest) -> &PayloadConfig {
+ req.app_data::()
+ .or_else(|| {
+ req.app_data::>()
+ .map(|d| d.as_ref())
+ })
+ .unwrap_or_else(|| &DEFAULT_PAYLOAD_CONFIG)
+ }
}
+// Allow shared refs to default.
+static DEFAULT_PAYLOAD_CONFIG: PayloadConfig = PayloadConfig {
+ limit: 262_144, // 2^18 bytes (~256kB)
+ mimetype: None,
+};
+
impl Default for PayloadConfig {
fn default() -> Self {
- PayloadConfig {
- limit: 262_144,
- mimetype: None,
- }
+ DEFAULT_PAYLOAD_CONFIG.clone()
}
}
@@ -407,8 +415,9 @@ mod tests {
use bytes::Bytes;
use super::*;
- use crate::http::header;
- use crate::test::TestRequest;
+ use crate::http::{header, StatusCode};
+ use crate::test::{call_service, init_service, TestRequest};
+ use crate::{web, App, Responder};
#[actix_rt::test]
async fn test_payload_config() {
@@ -428,6 +437,86 @@ mod tests {
assert!(cfg.check_mimetype(&req).is_ok());
}
+ #[actix_rt::test]
+ async fn test_config_recall_locations() {
+ async fn bytes_handler(_: Bytes) -> impl Responder {
+ "payload is probably json bytes"
+ }
+
+ async fn string_handler(_: String) -> impl Responder {
+ "payload is probably json string"
+ }
+
+ let mut srv = init_service(
+ App::new()
+ .service(
+ web::resource("/bytes-app-data")
+ .app_data(
+ PayloadConfig::default().mimetype(mime::APPLICATION_JSON),
+ )
+ .route(web::get().to(bytes_handler)),
+ )
+ .service(
+ web::resource("/bytes-data")
+ .data(PayloadConfig::default().mimetype(mime::APPLICATION_JSON))
+ .route(web::get().to(bytes_handler)),
+ )
+ .service(
+ web::resource("/string-app-data")
+ .app_data(
+ PayloadConfig::default().mimetype(mime::APPLICATION_JSON),
+ )
+ .route(web::get().to(string_handler)),
+ )
+ .service(
+ web::resource("/string-data")
+ .data(PayloadConfig::default().mimetype(mime::APPLICATION_JSON))
+ .route(web::get().to(string_handler)),
+ ),
+ )
+ .await;
+
+ let req = TestRequest::with_uri("/bytes-app-data").to_request();
+ let resp = call_service(&mut srv, req).await;
+ assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
+
+ let req = TestRequest::with_uri("/bytes-data").to_request();
+ let resp = call_service(&mut srv, req).await;
+ assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
+
+ let req = TestRequest::with_uri("/string-app-data").to_request();
+ let resp = call_service(&mut srv, req).await;
+ assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
+
+ let req = TestRequest::with_uri("/string-data").to_request();
+ let resp = call_service(&mut srv, req).await;
+ assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
+
+ let req = TestRequest::with_uri("/bytes-app-data")
+ .header(header::CONTENT_TYPE, mime::APPLICATION_JSON)
+ .to_request();
+ let resp = call_service(&mut srv, req).await;
+ assert_eq!(resp.status(), StatusCode::OK);
+
+ let req = TestRequest::with_uri("/bytes-data")
+ .header(header::CONTENT_TYPE, mime::APPLICATION_JSON)
+ .to_request();
+ let resp = call_service(&mut srv, req).await;
+ assert_eq!(resp.status(), StatusCode::OK);
+
+ let req = TestRequest::with_uri("/string-app-data")
+ .header(header::CONTENT_TYPE, mime::APPLICATION_JSON)
+ .to_request();
+ let resp = call_service(&mut srv, req).await;
+ assert_eq!(resp.status(), StatusCode::OK);
+
+ let req = TestRequest::with_uri("/string-data")
+ .header(header::CONTENT_TYPE, mime::APPLICATION_JSON)
+ .to_request();
+ let resp = call_service(&mut srv, req).await;
+ assert_eq!(resp.status(), StatusCode::OK);
+ }
+
#[actix_rt::test]
async fn test_bytes() {
let (req, mut pl) = TestRequest::with_header(header::CONTENT_LENGTH, "11")
From f8d5ad6b5349843d16ec391c711b7b05eddce428 Mon Sep 17 00:00:00 2001
From: Jonas Platte
Date: Tue, 21 Jul 2020 01:54:26 +0200
Subject: [PATCH 25/78] Make web::Path a tuple struct with a public inner value
(#1594)
Co-authored-by: Rob Ede
---
CHANGES.md | 3 +++
MIGRATION.md | 20 +++++++++++++++++
README.md | 4 ++--
src/lib.rs | 4 ++--
src/types/path.rs | 57 +++++++++++++++++++++++------------------------
5 files changed, 55 insertions(+), 33 deletions(-)
diff --git a/CHANGES.md b/CHANGES.md
index 4b6697a7f..5783da75a 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -4,10 +4,13 @@
### Changed
* `PayloadConfig` is now also considered in `Bytes` and `String` extractors when set
using `App::data`. [#1610]
+* `web::Path` now has a public representation: `web::Path(pub T)` that enables
+ destructuring. [#1594]
### Fixed
* Memory leak of app data in pooled requests. [#1609]
+[#1594]: https://github.com/actix/actix-web/pull/1594
[#1609]: https://github.com/actix/actix-web/pull/1609
[#1610]: https://github.com/actix/actix-web/pull/1610
diff --git a/MIGRATION.md b/MIGRATION.md
index d2e9735fb..7459b5b2d 100644
--- a/MIGRATION.md
+++ b/MIGRATION.md
@@ -12,6 +12,26 @@
* `BodySize::Sized64` variant has been removed. `BodySize::Sized` now receives a
`u64` instead of a `usize`.
+* Code that was using `path.` to access a `web::Path<(A, B, C)>`s elements now needs to use
+ destructuring or `.into_inner()`. For example:
+
+ ```rust
+ // Previously:
+ async fn some_route(path: web::Path<(String, String)>) -> String {
+ format!("Hello, {} {}", path.0, path.1)
+ }
+
+ // Now (this also worked before):
+ async fn some_route(path: web::Path<(String, String)>) -> String {
+ let (first_name, last_name) = path.into_inner();
+ format!("Hello, {} {}", first_name, last_name)
+ }
+ // Or (this wasn't previously supported):
+ async fn some_route(web::Path((first_name, last_name)): web::Path<(String, String)>) -> String {
+ format!("Hello, {} {}", first_name, last_name)
+ }
+ ```
+
## 2.0.0
* `HttpServer::start()` renamed to `HttpServer::run()`. It also possible to
diff --git a/README.md b/README.md
index 4d6bac29c..4f6a16199 100644
--- a/README.md
+++ b/README.md
@@ -61,8 +61,8 @@ Code:
use actix_web::{get, web, App, HttpServer, Responder};
#[get("/{id}/{name}/index.html")]
-async fn index(info: web::Path<(u32, String)>) -> impl Responder {
- format!("Hello {}! id:{}", info.1, info.0)
+async fn index(web::Path((id, name)): web::Path<(u32, String)>) -> impl Responder {
+ format!("Hello {}! id:{}", name, id)
}
#[actix_web::main]
diff --git a/src/lib.rs b/src/lib.rs
index eb46af664..8776a62b5 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -9,8 +9,8 @@
//! use actix_web::{get, web, App, HttpServer, Responder};
//!
//! #[get("/{id}/{name}/index.html")]
-//! async fn index(info: web::Path<(u32, String)>) -> impl Responder {
-//! format!("Hello {}! id:{}", info.1, info.0)
+//! async fn index(web::Path((id, name)): web::Path<(u32, String)>) -> impl Responder {
+//! format!("Hello {}! id:{}", name, id)
//! }
//!
//! #[actix_web::main]
diff --git a/src/types/path.rs b/src/types/path.rs
index 82050171c..dbb5f3ee0 100644
--- a/src/types/path.rs
+++ b/src/types/path.rs
@@ -25,8 +25,8 @@ use crate::FromRequest;
/// /// extract path info from "/{username}/{count}/index.html" url
/// /// {username} - deserializes to a String
/// /// {count} - - deserializes to a u32
-/// async fn index(info: web::Path<(String, u32)>) -> String {
-/// format!("Welcome {}! {}", info.0, info.1)
+/// async fn index(web::Path((username, count)): web::Path<(String, u32)>) -> String {
+/// format!("Welcome {}! {}", username, count)
/// }
///
/// fn main() {
@@ -61,20 +61,18 @@ use crate::FromRequest;
/// );
/// }
/// ```
-pub struct Path {
- inner: T,
-}
+pub struct Path(pub T);
impl Path {
/// Deconstruct to an inner value
pub fn into_inner(self) -> T {
- self.inner
+ self.0
}
}
impl AsRef for Path {
fn as_ref(&self) -> &T {
- &self.inner
+ &self.0
}
}
@@ -82,31 +80,31 @@ impl ops::Deref for Path {
type Target = T;
fn deref(&self) -> &T {
- &self.inner
+ &self.0
}
}
impl ops::DerefMut for Path {
fn deref_mut(&mut self) -> &mut T {
- &mut self.inner
+ &mut self.0
}
}
impl From for Path {
fn from(inner: T) -> Path {
- Path { inner }
+ Path(inner)
}
}
impl fmt::Debug for Path {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- self.inner.fmt(f)
+ self.0.fmt(f)
}
}
impl fmt::Display for Path {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- self.inner.fmt(f)
+ self.0.fmt(f)
}
}
@@ -120,8 +118,8 @@ impl fmt::Display for Path {
/// /// extract path info from "/{username}/{count}/index.html" url
/// /// {username} - deserializes to a String
/// /// {count} - - deserializes to a u32
-/// async fn index(info: web::Path<(String, u32)>) -> String {
-/// format!("Welcome {}! {}", info.0, info.1)
+/// async fn index(web::Path((username, count)): web::Path<(String, u32)>) -> String {
+/// format!("Welcome {}! {}", username, count)
/// }
///
/// fn main() {
@@ -173,7 +171,7 @@ where
ready(
de::Deserialize::deserialize(PathDeserializer::new(req.match_info()))
- .map(|inner| Path { inner })
+ .map(Path)
.map_err(move |e| {
log::debug!(
"Failed during Path extractor deserialization. \
@@ -290,21 +288,22 @@ mod tests {
resource.match_path(req.match_info_mut());
let (req, mut pl) = req.into_parts();
- let res = <(Path<(String, String)>,)>::from_request(&req, &mut pl)
+ let (Path(res),) = <(Path<(String, String)>,)>::from_request(&req, &mut pl)
.await
.unwrap();
- assert_eq!((res.0).0, "name");
- assert_eq!((res.0).1, "user1");
+ assert_eq!(res.0, "name");
+ assert_eq!(res.1, "user1");
- let res = <(Path<(String, String)>, Path<(String, String)>)>::from_request(
- &req, &mut pl,
- )
- .await
- .unwrap();
- assert_eq!((res.0).0, "name");
- assert_eq!((res.0).1, "user1");
- assert_eq!((res.1).0, "name");
- assert_eq!((res.1).1, "user1");
+ let (Path(a), Path(b)) =
+ <(Path<(String, String)>, Path<(String, String)>)>::from_request(
+ &req, &mut pl,
+ )
+ .await
+ .unwrap();
+ assert_eq!(a.0, "name");
+ assert_eq!(a.1, "user1");
+ assert_eq!(b.0, "name");
+ assert_eq!(b.1, "user1");
let () = <()>::from_request(&req, &mut pl).await.unwrap();
}
@@ -329,7 +328,7 @@ mod tests {
let s = s.into_inner();
assert_eq!(s.value, "user2");
- let s = Path::<(String, String)>::from_request(&req, &mut pl)
+ let Path(s) = Path::<(String, String)>::from_request(&req, &mut pl)
.await
.unwrap();
assert_eq!(s.0, "name");
@@ -344,7 +343,7 @@ mod tests {
assert_eq!(s.as_ref().key, "name");
assert_eq!(s.value, 32);
- let s = Path::<(String, u8)>::from_request(&req, &mut pl)
+ let Path(s) = Path::<(String, u8)>::from_request(&req, &mut pl)
.await
.unwrap();
assert_eq!(s.0, "name");
From 0ec335a39c660c89290a45e3d848d5e7c3ae5f02 Mon Sep 17 00:00:00 2001
From: Rob Ede
Date: Tue, 21 Jul 2020 08:40:30 +0100
Subject: [PATCH 26/78] bump MSRV to 1.42 (#1616)
---
.github/PULL_REQUEST_TEMPLATE.md | 6 ++++--
.github/workflows/linux.yml | 2 +-
CHANGES.md | 1 +
README.md | 4 ++--
actix-http/src/body.rs | 10 ++--------
actix-http/src/client/h2proto.rs | 5 +----
actix-http/src/h1/decoder.rs | 10 ++--------
actix-http/src/h1/dispatcher.rs | 10 ++--------
.../src/header/common/content_disposition.rs | 15 +++------------
actix-http/src/header/mod.rs | 5 +----
actix-http/src/ws/frame.rs | 5 +----
awc/src/response.rs | 11 +++--------
rust-toolchain | 2 +-
src/types/form.rs | 15 +++------------
src/types/json.rs | 10 ++--------
15 files changed, 29 insertions(+), 82 deletions(-)
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index cc34e2bc6..1c0d467b1 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -1,6 +1,8 @@
-## PR Type
-What kind of change does this PR make?
+
+
+## PR Type
+
INSERT_PR_TYPE
diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml
index 7529c8494..688db6cee 100644
--- a/.github/workflows/linux.yml
+++ b/.github/workflows/linux.yml
@@ -8,7 +8,7 @@ jobs:
fail-fast: false
matrix:
version:
- - 1.41.1 # MSRV
+ - 1.42.0 # MSRV
- stable
- nightly
diff --git a/CHANGES.md b/CHANGES.md
index 5783da75a..d80e87946 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -6,6 +6,7 @@
using `App::data`. [#1610]
* `web::Path` now has a public representation: `web::Path(pub T)` that enables
destructuring. [#1594]
+* MSRV is now 1.42.0.
### Fixed
* Memory leak of app data in pooled requests. [#1609]
diff --git a/README.md b/README.md
index 4f6a16199..a42a1a6f8 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,7 @@
[](https://crates.io/crates/actix-web)
[](https://docs.rs/actix-web)
-[](https://blog.rust-lang.org/2020/02/27/Rust-1.41.1.html)
+[](https://blog.rust-lang.org/2020/03/12/Rust-1.42.html)

[](https://travis-ci.org/actix/actix-web)
@@ -32,7 +32,7 @@
* Middlewares ([Logger, Session, CORS, etc](https://actix.rs/docs/middleware/))
* Includes an async [HTTP client](https://actix.rs/actix-web/actix_web/client/index.html)
* Supports [Actix actor framework](https://github.com/actix/actix)
-* Runs on stable Rust 1.41+
+* Runs on stable Rust 1.42+
## Documentation
diff --git a/actix-http/src/body.rs b/actix-http/src/body.rs
index 0b01aa8ce..137f462a4 100644
--- a/actix-http/src/body.rs
+++ b/actix-http/src/body.rs
@@ -192,14 +192,8 @@ impl MessageBody for Body {
impl PartialEq for Body {
fn eq(&self, other: &Body) -> bool {
match *self {
- Body::None => match *other {
- Body::None => true,
- _ => false,
- },
- Body::Empty => match *other {
- Body::Empty => true,
- _ => false,
- },
+ Body::None => matches!(*other, Body::None),
+ Body::Empty => matches!(*other, Body::Empty),
Body::Bytes(ref b) => match *other {
Body::Bytes(ref b2) => b == b2,
_ => false,
diff --git a/actix-http/src/client/h2proto.rs b/actix-http/src/client/h2proto.rs
index 48ab9fe4a..7d1d3dc51 100644
--- a/actix-http/src/client/h2proto.rs
+++ b/actix-http/src/client/h2proto.rs
@@ -37,10 +37,7 @@ where
trace!("Sending client request: {:?} {:?}", head, body.size());
let head_req = head.as_ref().method == Method::HEAD;
let length = body.size();
- let eof = match length {
- BodySize::None | BodySize::Empty | BodySize::Sized(0) => true,
- _ => false,
- };
+ let eof = matches!(length, BodySize::None | BodySize::Empty | BodySize::Sized(0));
let mut req = Request::new(());
*req.uri_mut() = head.as_ref().uri.clone();
diff --git a/actix-http/src/h1/decoder.rs b/actix-http/src/h1/decoder.rs
index c9d3edf33..89a18aea9 100644
--- a/actix-http/src/h1/decoder.rs
+++ b/actix-http/src/h1/decoder.rs
@@ -655,10 +655,7 @@ mod tests {
}
fn is_unhandled(&self) -> bool {
- match self {
- PayloadType::Stream(_) => true,
- _ => false,
- }
+ matches!(self, PayloadType::Stream(_))
}
}
@@ -670,10 +667,7 @@ mod tests {
}
}
fn eof(&self) -> bool {
- match *self {
- PayloadItem::Eof => true,
- _ => false,
- }
+ matches!(*self, PayloadItem::Eof)
}
}
diff --git a/actix-http/src/h1/dispatcher.rs b/actix-http/src/h1/dispatcher.rs
index 51f107efb..a9ab29881 100644
--- a/actix-http/src/h1/dispatcher.rs
+++ b/actix-http/src/h1/dispatcher.rs
@@ -156,14 +156,8 @@ enum PollResponse {
impl PartialEq for PollResponse {
fn eq(&self, other: &PollResponse) -> bool {
match self {
- PollResponse::DrainWriteBuf => match other {
- PollResponse::DrainWriteBuf => true,
- _ => false,
- },
- PollResponse::DoNothing => match other {
- PollResponse::DoNothing => true,
- _ => false,
- },
+ PollResponse::DrainWriteBuf => matches!(other, PollResponse::DrainWriteBuf),
+ PollResponse::DoNothing => matches!(other, PollResponse::DoNothing),
_ => false,
}
}
diff --git a/actix-http/src/header/common/content_disposition.rs b/actix-http/src/header/common/content_disposition.rs
index d65933901..051dcfe80 100644
--- a/actix-http/src/header/common/content_disposition.rs
+++ b/actix-http/src/header/common/content_disposition.rs
@@ -387,26 +387,17 @@ impl ContentDisposition {
/// Returns `true` if it is [`Inline`](DispositionType::Inline).
pub fn is_inline(&self) -> bool {
- match self.disposition {
- DispositionType::Inline => true,
- _ => false,
- }
+ matches!(self.disposition, DispositionType::Inline)
}
/// Returns `true` if it is [`Attachment`](DispositionType::Attachment).
pub fn is_attachment(&self) -> bool {
- match self.disposition {
- DispositionType::Attachment => true,
- _ => false,
- }
+ matches!(self.disposition, DispositionType::Attachment)
}
/// Returns `true` if it is [`FormData`](DispositionType::FormData).
pub fn is_form_data(&self) -> bool {
- match self.disposition {
- DispositionType::FormData => true,
- _ => false,
- }
+ matches!(self.disposition, DispositionType::FormData)
}
/// Returns `true` if it is [`Ext`](DispositionType::Ext) and the `disp_type` matches.
diff --git a/actix-http/src/header/mod.rs b/actix-http/src/header/mod.rs
index 0db26ceb0..46fb31a62 100644
--- a/actix-http/src/header/mod.rs
+++ b/actix-http/src/header/mod.rs
@@ -148,10 +148,7 @@ impl ContentEncoding {
#[inline]
/// Is the content compressed?
pub fn is_compression(self) -> bool {
- match self {
- ContentEncoding::Identity | ContentEncoding::Auto => false,
- _ => true,
- }
+ matches!(self, ContentEncoding::Identity | ContentEncoding::Auto)
}
#[inline]
diff --git a/actix-http/src/ws/frame.rs b/actix-http/src/ws/frame.rs
index 8f7004f18..0598a9b4e 100644
--- a/actix-http/src/ws/frame.rs
+++ b/actix-http/src/ws/frame.rs
@@ -229,10 +229,7 @@ mod tests {
fn is_none(
frm: &Result