From 20ef05c36e9457b1f593831a06df68426db35b00 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sun, 27 Jun 2021 07:02:38 +0100 Subject: [PATCH] fix doctest ci (#188) --- .cargo/config.toml | 9 ++ .github/workflows/ci.yml | 85 ++++++++----------- Cargo.toml | 15 ++-- README.md | 4 +- actix-cors/Cargo.toml | 10 +-- actix-cors/src/builder.rs | 13 ++- actix-cors/src/inner.rs | 4 +- actix-cors/src/middleware.rs | 24 +++--- actix-identity/Cargo.toml | 7 +- actix-identity/src/cookie.rs | 43 +++++----- actix-identity/src/middleware.rs | 24 ++++-- actix-protobuf/Cargo.toml | 5 +- .../examples/prost-example/Cargo.toml | 2 +- actix-protobuf/src/lib.rs | 48 ++++++----- actix-redis/Cargo.toml | 24 ++++-- actix-redis/examples/authentication.rs | 9 +- actix-redis/src/session.rs | 34 +++++--- actix-session/Cargo.toml | 6 +- actix-session/src/cookie.rs | 62 ++++++++------ actix-web-httpauth/Cargo.toml | 8 +- actix-web-httpauth/src/extractors/basic.rs | 2 +- actix-web-httpauth/src/extractors/bearer.rs | 2 +- actix-web-httpauth/src/middleware.rs | 21 +++-- 23 files changed, 255 insertions(+), 206 deletions(-) create mode 100644 .cargo/config.toml diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 000000000..95d56f15f --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,9 @@ +[alias] +chk = "check --workspace --all-features --tests --examples --bins" +lint = "clippy --workspace --tests --examples" +ci-min = "hack check --workspace --no-default-features" +ci-min-test = "hack check --workspace --no-default-features --tests --examples" +ci-default = "check --workspace --bins --tests --examples" +ci-full = "check --workspace --all-features --bins --tests --examples" +ci-test = "test --workspace --all-features --lib --tests --no-fail-fast" +ci-doctest = "hack test --workspace --all-features --doc --no-fail-fast" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6ea2bb858..f296d5708 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,21 +31,6 @@ jobs: steps: - uses: actions/checkout@v2 - # # install OpenSSL on Windows - # - name: Set vcpkg root - # if: matrix.target.triple == 'x86_64-pc-windows-msvc' - # run: echo "VCPKG_ROOT=$env:VCPKG_INSTALLATION_ROOT" | Out-File -FilePath $env:GITHUB_ENV -Append - # - name: Install OpenSSL - # if: matrix.target.triple == 'x86_64-pc-windows-msvc' - # run: vcpkg install openssl:x64-windows - - - name: Install ${{ matrix.version }} - uses: actions-rs/toolchain@v1 - with: - toolchain: ${{ matrix.version }}-${{ matrix.target.triple }} - profile: minimal - override: true - - name: Install ${{ matrix.version }} uses: actions-rs/toolchain@v1 with: @@ -68,21 +53,32 @@ jobs: - name: check minimal uses: actions-rs/cargo@v1 - with: - command: hack - args: --clean-per-run check --workspace --no-default-features --tests + with: { command: ci-min } + + - name: check minimal + tests + uses: actions-rs/cargo@v1 + with: { command: ci-min-test } + + - name: check default + uses: actions-rs/cargo@v1 + with: { command: ci-default } - name: check full uses: actions-rs/cargo@v1 - with: - command: check - args: --workspace --bins --examples --tests + with: { command: ci-full } - name: tests uses: actions-rs/cargo@v1 + timeout-minutes: 40 + with: { command: ci-test } + + # only run on Linux due to unknown issue with running doc tests on macOS + - name: doc tests + uses: actions-rs/cargo@v1 + timeout-minutes: 40 with: - command: test - args: -v --workspace --all-features --no-fail-fast -- --nocapture + command: ci-doctest + args: -- --nocapture - name: Generate coverage file if: > @@ -101,7 +97,7 @@ jobs: - name: Clear the cargo caches run: | - cargo install cargo-cache --no-default-features --features ci-autoclean + cargo install cargo-cache --version 0.6.2 --no-default-features --features ci-autoclean cargo-cache build_and_test_other: @@ -122,21 +118,6 @@ jobs: steps: - uses: actions/checkout@v2 - # # install OpenSSL on Windows - # - name: Set vcpkg root - # if: matrix.target.triple == 'x86_64-pc-windows-msvc' - # run: echo "VCPKG_ROOT=$env:VCPKG_INSTALLATION_ROOT" | Out-File -FilePath $env:GITHUB_ENV -Append - # - name: Install OpenSSL - # if: matrix.target.triple == 'x86_64-pc-windows-msvc' - # run: vcpkg install openssl:x64-windows - - - name: Install ${{ matrix.version }} - uses: actions-rs/toolchain@v1 - with: - toolchain: ${{ matrix.version }}-${{ matrix.target.triple }} - profile: minimal - override: true - - name: Install ${{ matrix.version }} uses: actions-rs/toolchain@v1 with: @@ -159,26 +140,28 @@ jobs: - name: check minimal uses: actions-rs/cargo@v1 - with: - command: hack - args: --clean-per-run check --workspace --no-default-features --tests + with: { command: ci-min } + + - name: check minimal + tests + uses: actions-rs/cargo@v1 + with: { command: ci-min-test } + + - name: check default + uses: actions-rs/cargo@v1 + with: { command: ci-default } - name: check full uses: actions-rs/cargo@v1 - with: - command: check - args: --workspace --bins --examples --tests + with: { command: ci-full } - name: tests uses: actions-rs/cargo@v1 + timeout-minutes: 40 with: - command: test - args: -v - --workspace --all-features --no-fail-fast - --exclude=actix-redis - -- --nocapture + command: ci-test + args: --exclude=actix-redis -- --nocapture - name: Clear the cargo caches run: | - cargo install cargo-cache --no-default-features --features ci-autoclean + cargo install cargo-cache --version 0.6.2 --no-default-features --features ci-autoclean cargo-cache diff --git a/Cargo.toml b/Cargo.toml index 8d78c6939..dc3caa44a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,13 @@ [workspace] members = [ - "actix-cors", - "actix-identity", - "actix-protobuf", - "actix-protobuf/examples/prost-example", - "actix-redis", - "actix-session", - "actix-web-httpauth", + "actix-cors", + "actix-identity", + "actix-protobuf", + # TODO: move this example to examples repo + # "actix-protobuf/examples/prost-example", + "actix-redis", + "actix-session", + "actix-web-httpauth", ] [patch.crates-io] diff --git a/README.md b/README.md index c83aeb422..b3bc604d0 100644 --- a/README.md +++ b/README.md @@ -28,8 +28,8 @@ These crates are provided by the community. | [actix-limitation] | [![crates.io](https://img.shields.io/crates/v/actix-limitation?label=latest)](https://crates.io/crates/actix-limitation) [![Documentation](https://docs.rs/actix-limitation/badge.svg)](https://docs.rs/actix-limitation) [![dependency status](https://deps.rs/crate/actix-limitation/0.1.4/status.svg)](https://deps.rs/crate/actix-limitation/0.1.4) | Rate-limiting using a fixed window counter for arbitrary keys, backed by Redis. | | [actix-casbin] | [![crates.io](https://img.shields.io/crates/v/actix-casbin?label=latest)](https://crates.io/crates/actix-casbin) [![Documentation](https://docs.rs/actix-casbin/badge.svg)](https://docs.rs/actix-casbin) [![dependency status](https://deps.rs/crate/actix-casbin/0.4.2/status.svg)](https://deps.rs/crate/actix-casbin/0.4.2) | Authorization library that supports access control models like ACL, RBAC & ABAC. | | [actix-web-static-files] | [![crates.io](https://img.shields.io/crates/v/actix-web-static-files?label=latest)](https://crates.io/crates/actix-web-static-files) [![Documentation](https://docs.rs/actix-web-static-files/badge.svg)](https://docs.rs/actix-web-static-files) [![dependency status](https://deps.rs/crate/actix-web-static-files/3.0.5/status.svg)](https://deps.rs/crate/actix-web-static-files/3.0.5) | Static files as embedded resources. | -| [actix-web-grants] | [![crates.io](https://img.shields.io/crates/v/actix-web-grants?label=latest)](https://crates.io/crates/actix-web-grants) [![Documentation](https://docs.rs/actix-web-grants/badge.svg)](https://docs.rs/actix-web-grants) [![dependency status](https://deps.rs/crate/actix-web-grants/2.0.1/status.svg)](https://deps.rs/crate/actix-web-grants) | Extension for validating user authorities. | -| [aliri_actix] | [![crates.io](https://img.shields.io/crates/v/aliri_actix?label=latest)](https://crates.io/crates/aliri_actix) [![Documentation](https://docs.rs/aliri_actix/badge.svg)](https://docs.rs/aliri_actix) [![dependency status](https://deps.rs/crate/aliri_actix/0.5.0/status.svg)](https://deps.rs/crate/aliri_actix/0.5.0) | Endpoint authorization and authentication using scoped OAuth2 JWT tokens. | +| [actix-web-grants] | [![crates.io](https://img.shields.io/crates/v/actix-web-grants?label=latest)](https://crates.io/crates/actix-web-grants) [![Documentation](https://docs.rs/actix-web-grants/badge.svg)](https://docs.rs/actix-web-grants) [![dependency status](https://deps.rs/crate/actix-web-grants/2.0.1/status.svg)](https://deps.rs/crate/actix-web-grants/2.0.1) | Extension for validating user authorities. | +| [aliri_actix] | [![crates.io](https://img.shields.io/crates/v/aliri_actix?label=latest)](https://crates.io/crates/aliri_actix) [![Documentation](https://docs.rs/aliri_actix/badge.svg)](https://docs.rs/aliri_actix) [![dependency status](https://deps.rs/crate/aliri_actix/0.5.0/status.svg)](https://deps.rs/crate/aliri_actix/0.5.0) | Endpoint authorization and authentication using scoped OAuth2 JWT tokens. | To add a crate to this list, submit a pull request. diff --git a/actix-cors/Cargo.toml b/actix-cors/Cargo.toml index db3f40bfc..249de2f69 100644 --- a/actix-cors/Cargo.toml +++ b/actix-cors/Cargo.toml @@ -6,11 +6,9 @@ authors = [ "Rob Ede ", ] description = "Cross-Origin Resource Sharing (CORS) controls for Actix Web" -readme = "README.md" keywords = ["actix", "cors", "web", "security", "crossorigin"] homepage = "https://actix.rs" -repository = "https://github.com/actix/actix-extras.git" -documentation = "https://docs.rs/actix-cors" +repository = "https://github.com/actix/actix-extras" license = "MIT OR Apache-2.0" edition = "2018" @@ -19,14 +17,14 @@ name = "actix_cors" path = "src/lib.rs" [dependencies] -actix-web = { version = "4.0.0-beta.5", default-features = false } -actix-service = "2.0.0-beta.5" +actix-web = { version = "4.0.0-beta.8", default-features = false } +actix-service = "2.0.0" derive_more = "0.99.5" futures-util = { version = "0.3.7", default-features = false } log = "0.4" once_cell = "1" -tinyvec = { version = "1", features = ["alloc"] } +smallvec = "1.6" [dev-dependencies] actix-rt = "2" diff --git a/actix-cors/src/builder.rs b/actix-cors/src/builder.rs index c66b7307f..998804b82 100644 --- a/actix-cors/src/builder.rs +++ b/actix-cors/src/builder.rs @@ -9,7 +9,7 @@ use actix_web::{ use futures_util::future::{self, Ready}; use log::error; use once_cell::sync::Lazy; -use tinyvec::tiny_vec; +use smallvec::smallvec; use crate::{AllOrSome, CorsError, CorsMiddleware, Inner, OriginFn}; @@ -82,7 +82,7 @@ impl Cors { pub fn permissive() -> Self { let inner = Inner { allowed_origins: AllOrSome::All, - allowed_origins_fns: tiny_vec![], + allowed_origins_fns: smallvec![], allowed_methods: ALL_METHODS_SET.clone(), allowed_methods_baked: None, @@ -458,7 +458,7 @@ impl Default for Cors { fn default() -> Cors { let inner = Inner { allowed_origins: AllOrSome::Some(HashSet::with_capacity(8)), - allowed_origins_fns: tiny_vec![], + allowed_origins_fns: smallvec![], allowed_methods: HashSet::with_capacity(8), allowed_methods_baked: None, @@ -483,13 +483,12 @@ impl Default for Cors { } } -impl Transform for Cors +impl Transform for Cors where - S: Service, Error = Error>, + S: Service, S::Future: 'static, - B: 'static, { - type Response = ServiceResponse; + type Response = ServiceResponse; type Error = Error; type InitError = (); type Transform = CorsMiddleware; diff --git a/actix-cors/src/inner.rs b/actix-cors/src/inner.rs index d44461630..c862b5401 100644 --- a/actix-cors/src/inner.rs +++ b/actix-cors/src/inner.rs @@ -9,7 +9,7 @@ use actix_web::{ }, }; use once_cell::sync::Lazy; -use tinyvec::TinyVec; +use smallvec::SmallVec; use crate::{AllOrSome, CorsError}; @@ -42,7 +42,7 @@ fn header_value_try_into_method(hdr: &HeaderValue) -> Option { #[derive(Debug, Clone)] pub(crate) struct Inner { pub(crate) allowed_origins: AllOrSome>, - pub(crate) allowed_origins_fns: TinyVec<[OriginFn; 4]>, + pub(crate) allowed_origins_fns: SmallVec<[OriginFn; 4]>, pub(crate) allowed_methods: HashSet, pub(crate) allowed_methods_baked: Option, diff --git a/actix-cors/src/middleware.rs b/actix-cors/src/middleware.rs index ae522bfab..2453b685a 100644 --- a/actix-cors/src/middleware.rs +++ b/actix-cors/src/middleware.rs @@ -1,6 +1,7 @@ -use std::{convert::TryInto, rc::Rc}; +use std::{convert::TryInto, error::Error as StdError, rc::Rc}; use actix_web::{ + body::{AnyBody, MessageBody}, dev::{Service, ServiceRequest, ServiceResponse}, error::{Error, Result}, http::{ @@ -9,7 +10,9 @@ use actix_web::{ }, HttpResponse, }; -use futures_util::future::{ok, Either, FutureExt as _, LocalBoxFuture, Ready}; +use futures_util::future::{ + ok, Either, FutureExt as _, LocalBoxFuture, Ready, TryFutureExt as _, +}; use log::debug; use crate::Inner; @@ -26,7 +29,7 @@ pub struct CorsMiddleware { } impl CorsMiddleware { - fn handle_preflight(inner: &Inner, req: ServiceRequest) -> ServiceResponse { + fn handle_preflight(inner: &Inner, req: ServiceRequest) -> ServiceResponse { if let Err(err) = inner .validate_origin(req.head()) .and_then(|_| inner.validate_allowed_method(req.head())) @@ -69,7 +72,6 @@ impl CorsMiddleware { } let res = res.finish(); - let res = res.into_body(); req.into_response(res) } @@ -112,20 +114,21 @@ impl CorsMiddleware { } } -type CorsMiddlewareServiceFuture = Either< - Ready, Error>>, - LocalBoxFuture<'static, Result, Error>>, +type CorsMiddlewareServiceFuture = Either< + Ready>, + LocalBoxFuture<'static, Result>, >; impl Service for CorsMiddleware where S: Service, Error = Error>, S::Future: 'static, - B: 'static, + B: MessageBody + 'static, + B::Error: StdError, { - type Response = ServiceResponse; + type Response = ServiceResponse; type Error = Error; - type Future = CorsMiddlewareServiceFuture; + type Future = CorsMiddlewareServiceFuture; actix_service::forward_ready!(service); @@ -158,6 +161,7 @@ where res } } + .map_ok(|res| res.map_body(|_, body| AnyBody::from_message(body))) .boxed_local(); Either::Right(res) diff --git a/actix-identity/Cargo.toml b/actix-identity/Cargo.toml index a7f123006..b0e50795b 100644 --- a/actix-identity/Cargo.toml +++ b/actix-identity/Cargo.toml @@ -3,10 +3,9 @@ name = "actix-identity" version = "0.4.0-beta.1" authors = ["Nikolay Kim "] description = "Identity service for Actix web" -readme = "README.md" keywords = ["actix", "auth", "identity", "web", "security"] homepage = "https://actix.rs" -repository = "https://github.com/actix/actix-extras.git" +repository = "https://github.com/actix/actix-extras" license = "MIT OR Apache-2.0" edition = "2018" @@ -15,8 +14,8 @@ name = "actix_identity" path = "src/lib.rs" [dependencies] -actix-service = "2.0.0-beta.5" -actix-web = { version = "4.0.0-beta.5", default-features = false, features = ["cookies", "secure-cookies"] } +actix-service = "2.0.0" +actix-web = { version = "4.0.0-beta.8", default-features = false, features = ["cookies", "secure-cookies"] } futures-util = { version = "0.3.7", default-features = false } serde = "1.0" serde_json = "1.0" diff --git a/actix-identity/src/cookie.rs b/actix-identity/src/cookie.rs index a78dcc6fb..02a1e1438 100644 --- a/actix-identity/src/cookie.rs +++ b/actix-identity/src/cookie.rs @@ -69,16 +69,17 @@ impl CookieIdentityInner { value: Option, ) -> Result<()> { let add_cookie = value.is_some(); - let val = value.map(|val| { - if !self.legacy_supported() { - serde_json::to_string(&val) - } else { - Ok(val.identity) - } - }); + let val = value + .map(|val| { + if !self.legacy_supported() { + serde_json::to_string(&val) + } else { + Ok(val.identity) + } + }) + .transpose()?; - let mut cookie = - Cookie::new(self.name.clone(), val.unwrap_or_else(|| Ok(String::new()))?); + let mut cookie = Cookie::new(self.name.clone(), val.unwrap_or_default()); cookie.set_path(self.path.clone()); cookie.set_secure(self.secure); cookie.set_http_only(true); @@ -108,10 +109,10 @@ impl CookieIdentityInner { }; if add_cookie { - jar.private(&key).add(cookie); + jar.private_mut(&key).add(cookie); } else { jar.add_original(cookie.clone()); - jar.private(&key).remove(cookie); + jar.private_mut(&key).remove(cookie); } for cookie in jar.delta() { @@ -128,17 +129,19 @@ impl CookieIdentityInner { jar.add_original(cookie.clone()); let res = if self.legacy_supported() { - jar.private(&self.key).get(&self.name).map(|n| CookieValue { - identity: n.value().to_string(), - login_timestamp: None, - visit_timestamp: None, - }) + jar.private_mut(&self.key) + .get(&self.name) + .map(|n| CookieValue { + identity: n.value().to_string(), + login_timestamp: None, + visit_timestamp: None, + }) } else { None }; res.or_else(|| { - jar.private(&self.key_v2) + jar.private_mut(&self.key_v2) .get(&self.name) .and_then(|c| self.parse(c)) }) @@ -391,7 +394,7 @@ mod tests { .copied() .collect(); - jar.private(&Key::derive_from(&key)).add(Cookie::new( + jar.private_mut(&Key::derive_from(&key)).add(Cookie::new( COOKIE_NAME, serde_json::to_string(&CookieValue { identity: identity.to_string(), @@ -575,7 +578,7 @@ mod tests { fn legacy_login_cookie(identity: &'static str) -> Cookie<'static> { let mut jar = CookieJar::new(); - jar.private(&Key::derive_from(&COOKIE_KEY_MASTER)) + jar.private_mut(&Key::derive_from(&COOKIE_KEY_MASTER)) .add(Cookie::new(COOKIE_NAME, identity)); jar.get(COOKIE_NAME).unwrap().clone() } @@ -592,7 +595,7 @@ mod tests { cookies.add(Cookie::parse(cookie.to_str().unwrap().to_string()).unwrap()); } let cookie = cookies - .private(&Key::derive_from(&COOKIE_KEY_MASTER)) + .private_mut(&Key::derive_from(&COOKIE_KEY_MASTER)) .get(COOKIE_NAME) .unwrap(); assert_eq!(cookie.value(), identity); diff --git a/actix-identity/src/middleware.rs b/actix-identity/src/middleware.rs index 676d62f57..4e9602e4e 100644 --- a/actix-identity/src/middleware.rs +++ b/actix-identity/src/middleware.rs @@ -1,10 +1,13 @@ -use std::rc::Rc; +use std::{error::Error as StdError, rc::Rc}; use actix_web::{ + body::{AnyBody, MessageBody}, dev::{Service, ServiceRequest, ServiceResponse, Transform}, Error, HttpMessage, Result, }; -use futures_util::future::{ready, FutureExt, LocalBoxFuture, Ready}; +use futures_util::future::{ + ready, FutureExt as _, LocalBoxFuture, Ready, TryFutureExt as _, +}; use crate::{identity::IdentityItem, IdentityPolicy}; @@ -41,9 +44,10 @@ where S: Service, Error = Error> + 'static, S::Future: 'static, T: IdentityPolicy, - B: 'static, + B: MessageBody + 'static, + B::Error: StdError, { - type Response = ServiceResponse; + type Response = ServiceResponse; type Error = Error; type InitError = (); type Transform = IdentityServiceMiddleware; @@ -73,12 +77,13 @@ impl Clone for IdentityServiceMiddleware { impl Service for IdentityServiceMiddleware where - B: 'static, S: Service, Error = Error> + 'static, S::Future: 'static, T: IdentityPolicy, + B: MessageBody + 'static, + B::Error: StdError, { - type Response = ServiceResponse; + type Response = ServiceResponse; type Error = Error; type Future = LocalBoxFuture<'static, Result>; @@ -100,16 +105,19 @@ where if let Some(id) = id { match backend.to_response(id.id, id.changed, &mut res).await { - Ok(_) => Ok(res), + Ok(_) => { + Ok(res.map_body(|_, body| AnyBody::from_message(body))) + } Err(e) => Ok(res.error_response(e)), } } else { - Ok(res) + Ok(res.map_body(|_, body| AnyBody::from_message(body))) } } Err(err) => Ok(req.error_response(err)), } } + .map_ok(|res| res.map_body(|_, body| AnyBody::from_message(body))) .boxed_local() } } diff --git a/actix-protobuf/Cargo.toml b/actix-protobuf/Cargo.toml index 867b0100e..163728116 100644 --- a/actix-protobuf/Cargo.toml +++ b/actix-protobuf/Cargo.toml @@ -7,10 +7,9 @@ authors = [ "Yuki Okushi " ] description = "Protobuf support for Actix web" -readme = "README.md" keywords = ["actix", "protobuf", "protocol", "rpc"] homepage = "https://actix.rs" -repository = "https://github.com/actix/actix-extras.git" +repository = "https://github.com/actix/actix-extras" license = "MIT OR Apache-2.0" exclude = [".cargo/config", "/examples/**"] @@ -20,7 +19,7 @@ path = "src/lib.rs" [dependencies] actix-rt = "2" -actix-web = { version = "4.0.0-beta.5", default_features = false } +actix-web = { version = "4.0.0-beta.8", default_features = false } derive_more = "0.99.5" futures-util = { version = "0.3.7", default-features = false } prost = "0.7" diff --git a/actix-protobuf/examples/prost-example/Cargo.toml b/actix-protobuf/examples/prost-example/Cargo.toml index 7e5728fff..140345573 100644 --- a/actix-protobuf/examples/prost-example/Cargo.toml +++ b/actix-protobuf/examples/prost-example/Cargo.toml @@ -8,7 +8,7 @@ authors = [ ] [dependencies] -actix-web = "4.0.0-beta.5" +actix-web = "4.0.0-beta.8" actix-protobuf = { path = "../../" } env_logger = "0.8" diff --git a/actix-protobuf/src/lib.rs b/actix-protobuf/src/lib.rs index 3100c2858..d5dae7252 100644 --- a/actix-protobuf/src/lib.rs +++ b/actix-protobuf/src/lib.rs @@ -1,39 +1,49 @@ -#![deny(rust_2018_idioms)] +#![forbid(unsafe_code)] +#![deny(rust_2018_idioms, nonstandard_style)] +use std::{ + fmt, + future::Future, + ops::{Deref, DerefMut}, + pin::Pin, + task::{self, Poll}, +}; + +use actix_web::{ + dev::Payload, + error::PayloadError, + http::header::{CONTENT_LENGTH, CONTENT_TYPE}, + web::BytesMut, + Error, FromRequest, HttpMessage, HttpRequest, HttpResponse, HttpResponseBuilder, + Responder, ResponseError, +}; use derive_more::Display; -use std::fmt; -use std::future::Future; -use std::ops::{Deref, DerefMut}; -use std::pin::Pin; -use std::task; -use std::task::Poll; - -use prost::DecodeError as ProtoBufDecodeError; -use prost::EncodeError as ProtoBufEncodeError; -use prost::Message; - -use actix_web::dev::{HttpResponseBuilder, Payload}; -use actix_web::error::{Error, PayloadError, ResponseError}; -use actix_web::http::header::{CONTENT_LENGTH, CONTENT_TYPE}; -use actix_web::web::BytesMut; -use actix_web::{FromRequest, HttpMessage, HttpRequest, HttpResponse, Responder}; -use futures_util::future::{FutureExt, LocalBoxFuture}; -use futures_util::StreamExt; +use futures_util::{ + future::{FutureExt as _, LocalBoxFuture}, + stream::StreamExt as _, +}; +use prost::{ + DecodeError as ProtoBufDecodeError, EncodeError as ProtoBufEncodeError, Message, +}; #[derive(Debug, Display)] pub enum ProtoBufPayloadError { /// Payload size is bigger than 256k #[display(fmt = "Payload size is bigger than 256k")] Overflow, + /// Content type error #[display(fmt = "Content type error")] ContentType, + /// Serialize error #[display(fmt = "ProtoBuf serialize error: {}", _0)] Serialize(ProtoBufEncodeError), + /// Deserialize error #[display(fmt = "ProtoBuf deserialize error: {}", _0)] Deserialize(ProtoBufDecodeError), + /// Payload error #[display(fmt = "Error that occur during reading payload: {}", _0)] Payload(PayloadError), diff --git a/actix-redis/Cargo.toml b/actix-redis/Cargo.toml index 1454e9e8f..c42d7ed9c 100644 --- a/actix-redis/Cargo.toml +++ b/actix-redis/Cargo.toml @@ -4,11 +4,9 @@ version = "0.10.0-beta.1" authors = ["Nikolay Kim "] description = "Redis integration for Actix and session store for Actix Web" license = "MIT OR Apache-2.0" -readme = "README.md" keywords = ["actix", "redis", "async", "session"] homepage = "https://actix.rs" -repository = "https://github.com/actix/actix-extras.git" -documentation = "https://docs.rs/actix-redis/" +repository = "https://github.com/actix/actix-extras" categories = ["network-programming", "asynchronous"] exclude = [".cargo/config"] edition = "2018" @@ -31,9 +29,9 @@ web = [ ] [dependencies] -actix = { version = "0.11.0", default-features = false } +actix = { version = "0.12.0", default-features = false } actix-rt = { version = "2.1", default-features = false } -actix-service = "2.0.0-beta.5" +actix-service = "2.0.0" actix-tls = { version = "3.0.0-beta.5", default-features = false, features = ["connect"] } log = "0.4.6" @@ -47,15 +45,23 @@ tokio = { version = "1", features = ["sync"] } tokio-util = "0.6.1" # actix-session -actix-web = { version = "4.0.0-beta.5", default_features = false, optional = true } +actix-web = { version = "4.0.0-beta.8", default_features = false, optional = true } actix-session = { version = "0.5.0-beta.1", optional = true } rand = { version = "0.8.0", optional = true } serde = { version = "1.0.101", optional = true } serde_json = { version = "1.0.40", optional = true } [dev-dependencies] -actix-test = "0.1.0-beta.1" +actix-test = "0.1.0-beta.3" actix-http = "3.0.0-beta.5" actix-rt = "2.1" -env_logger = "0.7" -serde_derive = "1.0" +env_logger = "0.8" +serde = { version = "1.0.101", features = ["derive"] } + +[[example]] +name = "basic" +required-features = ["web"] + +[[example]] +name = "authentication" +required-features = ["web"] diff --git a/actix-redis/examples/authentication.rs b/actix-redis/examples/authentication.rs index 16f9f0c8d..4df22362a 100644 --- a/actix-redis/examples/authentication.rs +++ b/actix-redis/examples/authentication.rs @@ -1,7 +1,8 @@ use actix_redis::RedisSession; use actix_session::Session; use actix_web::{ - cookie, middleware, web, App, Error, HttpResponse, HttpServer, Responder, + cookie, error::InternalError, middleware, web, App, Error, HttpResponse, HttpServer, + Responder, }; use serde::{Deserialize, Serialize}; @@ -49,12 +50,12 @@ pub fn validate_session(session: &Session) -> Result { async fn login( credentials: web::Json, session: Session, -) -> Result { +) -> Result { let credentials = credentials.into_inner(); match User::authenticate(credentials) { Ok(user) => session.insert("user_id", user.id).unwrap(), - Err(_) => return Err(HttpResponse::Unauthorized().json("Unauthorized")), + Err(err) => return Err(InternalError::from_response("", err).into()), }; Ok("Welcome!") @@ -63,7 +64,7 @@ async fn login( /// some protected resource async fn secret(session: Session) -> Result { // only allow access to this resource if the user has an active session - validate_session(&session)?; + validate_session(&session).map_err(|err| InternalError::from_response("", err))?; Ok("secret revealed") } diff --git a/actix-redis/src/session.rs b/actix-redis/src/session.rs index 55a25e87f..8a68490d0 100644 --- a/actix-redis/src/session.rs +++ b/actix-redis/src/session.rs @@ -3,14 +3,16 @@ use std::{collections::HashMap, iter, rc::Rc}; use actix::prelude::*; use actix_service::{Service, Transform}; use actix_session::{Session, SessionStatus}; -use actix_web::cookie::{Cookie, CookieJar, Key, SameSite}; -use actix_web::dev::{ServiceRequest, ServiceResponse}; -use actix_web::http::header::{self, HeaderValue}; -use actix_web::{error, Error, HttpMessage}; +use actix_web::{ + cookie::{Cookie, CookieJar, Key, SameSite}, + dev::{ServiceRequest, ServiceResponse}, + error, + http::header::{self, HeaderValue}, + Error, +}; use futures_core::future::LocalBoxFuture; use rand::{distributions::Alphanumeric, rngs::OsRng, Rng}; -use redis_async::resp::RespValue; -use redis_async::resp_array; +use redis_async::{resp::RespValue, resp_array}; use time::{self, Duration, OffsetDateTime}; use crate::redis::{Command, RedisActor}; @@ -311,7 +313,7 @@ impl Inner { // set cookie let mut jar = CookieJar::new(); - jar.signed(&self.key).add(cookie); + jar.signed_mut(&self.key).add(cookie); (value, Some(jar)) }; @@ -321,7 +323,7 @@ impl Inner { let state: HashMap<_, _> = state.collect(); let body = match serde_json::to_string(&state) { - Err(e) => return Err(e.into()), + Err(err) => return Err(err.into()), Ok(body) => body, }; @@ -442,12 +444,15 @@ mod test { async fn logout(session: Session) -> Result { let id: Option = session.get("user_id")?; - if let Some(x) = id { + + let body = if let Some(x) = id { session.purge(); - Ok(format!("Logged out: {}", x).into()) + format!("Logged out: {}", x) } else { - Ok("Could not log out anonymous user".into()) - } + "Could not log out anonymous user".to_owned() + }; + + Ok(HttpResponse::Ok().body(body)) } #[actix_rt::test] @@ -648,7 +653,10 @@ mod test { .unwrap(); assert_ne!( OffsetDateTime::now_utc().year(), - cookie_4.expires().map(|t| t.year()).unwrap() + cookie_4 + .expires() + .map(|t| t.datetime().expect("Expiration is a datetime").year()) + .unwrap() ); // Step 10: GET index, including session cookie #2 in request diff --git a/actix-session/Cargo.toml b/actix-session/Cargo.toml index 670a92265..c6e76184c 100644 --- a/actix-session/Cargo.toml +++ b/actix-session/Cargo.toml @@ -3,11 +3,9 @@ name = "actix-session" version = "0.5.0-beta.1" authors = ["Nikolay Kim "] description = "Sessions for Actix web" -readme = "README.md" keywords = ["http", "web", "framework", "async", "session"] homepage = "https://actix.rs" repository = "https://github.com/actix/actix-extras" -documentation = "https://docs.rs/actix-session/" license = "MIT OR Apache-2.0" edition = "2018" @@ -20,8 +18,8 @@ default = ["cookie-session"] cookie-session = ["actix-web/secure-cookies"] [dependencies] -actix-web = { version = "4.0.0-beta.5", default_features = false, features = ["cookies"] } -actix-service = "2.0.0-beta.5" +actix-web = { version = "4.0.0-beta.8", default_features = false, features = ["cookies"] } +actix-service = "2.0.0" derive_more = "0.99.5" futures-util = { version = "0.3.7", default-features = false } diff --git a/actix-session/src/cookie.rs b/actix-session/src/cookie.rs index b656e494f..24a42487c 100644 --- a/actix-session/src/cookie.rs +++ b/actix-session/src/cookie.rs @@ -1,14 +1,16 @@ //! Cookie based sessions. See docs for [`CookieSession`]. -use std::{collections::HashMap, rc::Rc}; +use std::{collections::HashMap, error::Error as StdError, rc::Rc}; -use actix_service::{Service, Transform}; -use actix_web::cookie::{Cookie, CookieJar, Key, SameSite}; -use actix_web::dev::{ServiceRequest, ServiceResponse}; -use actix_web::http::{header::SET_COOKIE, HeaderValue}; -use actix_web::{Error, HttpMessage, ResponseError}; +use actix_web::{ + body::{AnyBody, MessageBody}, + cookie::{Cookie, CookieJar, Key, SameSite}, + dev::{Service, ServiceRequest, ServiceResponse, Transform}, + http::{header::SET_COOKIE, HeaderValue}, + Error, ResponseError, +}; use derive_more::Display; -use futures_util::future::{ok, LocalBoxFuture, Ready}; +use futures_util::future::{ok, FutureExt as _, LocalBoxFuture, Ready}; use serde_json::error::Error as JsonError; use time::{Duration, OffsetDateTime}; @@ -106,8 +108,8 @@ impl CookieSessionInner { let mut jar = CookieJar::new(); match self.security { - CookieSecurity::Signed => jar.signed(&self.key).add(cookie), - CookieSecurity::Private => jar.private(&self.key).add(cookie), + CookieSecurity::Signed => jar.signed_mut(&self.key).add(cookie), + CookieSecurity::Private => jar.private_mut(&self.key).add(cookie), } for cookie in jar.delta() { @@ -292,13 +294,15 @@ impl CookieSession { } } -impl Transform for CookieSession +impl Transform for CookieSession where S: Service>, S::Future: 'static, S::Error: 'static, + B: MessageBody + 'static, + B::Error: StdError, { - type Response = ServiceResponse; + type Response = ServiceResponse; type Error = S::Error; type InitError = (); type Transform = CookieSessionMiddleware; @@ -318,13 +322,15 @@ pub struct CookieSessionMiddleware { inner: Rc, } -impl Service for CookieSessionMiddleware +impl Service for CookieSessionMiddleware where S: Service>, S::Future: 'static, S::Error: 'static, + B: MessageBody + 'static, + B::Error: StdError, { - type Response = ServiceResponse; + type Response = ServiceResponse; type Error = S::Error; type Future = LocalBoxFuture<'static, Result>; @@ -343,36 +349,40 @@ where let fut = self.service.call(req); - Box::pin(async move { + async move { let mut res = fut.await?; - let res = match Session::get_changes(&mut res) { + let result = match Session::get_changes(&mut res) { (SessionStatus::Changed, state) | (SessionStatus::Renewed, state) => { - res.checked_expr(|res| inner.set_cookie(res, state)) + inner.set_cookie(&mut res, state) } (SessionStatus::Unchanged, state) if prolong_expiration => { - res.checked_expr(|res| inner.set_cookie(res, state)) + inner.set_cookie(&mut res, state) } // set a new session cookie upon first request (new client) (SessionStatus::Unchanged, _) => { if is_new { let state: HashMap = HashMap::new(); - res.checked_expr(|res| inner.set_cookie(res, state.into_iter())) + inner.set_cookie(&mut res, state.into_iter()) } else { - res + Ok(()) } } (SessionStatus::Purged, _) => { let _ = inner.remove_cookie(&mut res); - res + Ok(()) } }; - Ok(res) - }) + match result { + Ok(()) => Ok(res.map_body(|_, body| AnyBody::from_message(body))), + Err(error) => Ok(res.error_response(error)), + } + } + .boxed_local() } } @@ -533,7 +543,9 @@ mod tests { .find(|c| c.name() == "actix-session") .expect("Cookie is set") .expires() - .expect("Expiration is set"); + .expect("Expiration is set") + .datetime() + .expect("Expiration is a datetime"); actix_rt::time::sleep(std::time::Duration::from_secs(1)).await; @@ -545,7 +557,9 @@ mod tests { .find(|c| c.name() == "actix-session") .expect("Cookie is set") .expires() - .expect("Expiration is set"); + .expect("Expiration is set") + .datetime() + .expect("Expiration is a datetime"); assert!(expires_2 - expires_1 >= Duration::seconds(1)); } diff --git a/actix-web-httpauth/Cargo.toml b/actix-web-httpauth/Cargo.toml index db45f9650..0e3e05344 100644 --- a/actix-web-httpauth/Cargo.toml +++ b/actix-web-httpauth/Cargo.toml @@ -6,11 +6,9 @@ authors = [ "Yuki Okushi ", ] description = "HTTP authentication schemes for Actix web" -readme = "README.md" keywords = ["http", "web", "framework", "authentication", "security"] homepage = "https://actix.rs" -repository = "https://github.com/actix/actix-extras.git" -documentation = "https://docs.rs/actix-web-httpauth/" +repository = "https://github.com/actix/actix-extras" categories = ["web-programming::http-server"] license = "MIT OR Apache-2.0" edition = "2018" @@ -20,8 +18,8 @@ name = "actix_web_httpauth" path = "src/lib.rs" [dependencies] -actix-web = { version = "4.0.0-beta.5", default_features = false } -actix-service = "2.0.0-beta.5" +actix-web = { version = "4.0.0-beta.8", default_features = false } +actix-service = "2.0.0" base64 = "0.13" futures-util = { version = "0.3.7", default-features = false } diff --git a/actix-web-httpauth/src/extractors/basic.rs b/actix-web-httpauth/src/extractors/basic.rs index ef262b45d..0f6654603 100644 --- a/actix-web-httpauth/src/extractors/basic.rs +++ b/actix-web-httpauth/src/extractors/basic.rs @@ -81,7 +81,7 @@ impl AuthExtractorConfig for Config { /// /// fn main() { /// let app = App::new() -/// .data(Config::default().realm("Restricted area")) +/// .app_data(Config::default().realm("Restricted area")) /// .service(web::resource("/index.html").route(web::get().to(index))); /// } /// ``` diff --git a/actix-web-httpauth/src/extractors/bearer.rs b/actix-web-httpauth/src/extractors/bearer.rs index f73c22d11..5c17f3f57 100644 --- a/actix-web-httpauth/src/extractors/bearer.rs +++ b/actix-web-httpauth/src/extractors/bearer.rs @@ -84,7 +84,7 @@ impl AuthExtractorConfig for Config { /// /// fn main() { /// let app = App::new() -/// .data( +/// .app_data( /// Config::default() /// .realm("Restricted area") /// .scope("email photo"), diff --git a/actix-web-httpauth/src/middleware.rs b/actix-web-httpauth/src/middleware.rs index 6f42299fa..49cbed175 100644 --- a/actix-web-httpauth/src/middleware.rs +++ b/actix-web-httpauth/src/middleware.rs @@ -1,8 +1,12 @@ //! HTTP Authentication middleware. -use std::{future::Future, marker::PhantomData, pin::Pin, rc::Rc, sync::Arc}; +use std::{ + error::Error as StdError, future::Future, marker::PhantomData, pin::Pin, rc::Rc, + sync::Arc, +}; use actix_web::{ + body::{AnyBody, MessageBody}, dev::{Service, ServiceRequest, ServiceResponse, Transform}, Error, }; @@ -120,8 +124,10 @@ where F: Fn(ServiceRequest, T) -> O + 'static, O: Future> + 'static, T: AuthExtractor + 'static, + B: MessageBody + 'static, + B::Error: StdError, { - type Response = ServiceResponse; + type Response = ServiceResponse; type Error = Error; type Transform = AuthenticationMiddleware; type InitError = (); @@ -153,10 +159,12 @@ where F: Fn(ServiceRequest, T) -> O + 'static, O: Future> + 'static, T: AuthExtractor + 'static, + B: MessageBody + 'static, + B::Error: StdError, { - type Response = ServiceResponse; + type Response = ServiceResponse; type Error = S::Error; - type Future = LocalBoxFuture<'static, Result, Error>>; + type Future = LocalBoxFuture<'static, Result>; actix_service::forward_ready!(service); @@ -177,7 +185,10 @@ where // middleware to do their thing (eg. cors adding headers) let req = process_fn(req, credentials).await?; - service.call(req).await + service + .call(req) + .await + .map(|res| res.map_body(|_, body| AnyBody::from_message(body))) } .boxed_local() }