1
0
mirror of https://github.com/actix/actix-extras.git synced 2025-02-22 18:33:18 +01:00

Merge branch 'master' into master

This commit is contained in:
Raphael C 2022-12-17 23:54:17 +01:00 committed by GitHub
commit 9da8d57ff2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 261 additions and 130 deletions

View File

@ -42,7 +42,7 @@ jobs:
with: with:
command: generate-lockfile command: generate-lockfile
- name: Cache Dependencies - name: Cache Dependencies
uses: Swatinem/rust-cache@v1.2.0 uses: Swatinem/rust-cache@v2.1.0
- name: check minimal - name: check minimal
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
@ -72,7 +72,11 @@ jobs:
matrix: matrix:
target: target:
- { name: macOS, os: macos-latest, triple: x86_64-apple-darwin } - { name: macOS, os: macos-latest, triple: x86_64-apple-darwin }
- { name: Windows, os: windows-latest, triple: x86_64-pc-windows-msvc } - {
name: Windows,
os: windows-latest,
triple: x86_64-pc-windows-msvc,
}
version: version:
- nightly - nightly
@ -83,44 +87,30 @@ jobs:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Install ${{ matrix.version }} - name: Install ${{ matrix.version }}
uses: actions-rs/toolchain@v1 run: |
with: rustup install ${{ matrix.version }}-${{ matrix.target.triple }}
toolchain: ${{ matrix.version }}-${{ matrix.target.triple }} rustup override set ${{ matrix.version }}-${{ matrix.target.triple }}
profile: minimal
override: true
- name: Install cargo-hack - name: Install cargo-hack
uses: taiki-e/install-action@cargo-hack uses: taiki-e/install-action@cargo-hack
- name: Generate Cargo.lock - name: Generate Cargo.lock
uses: actions-rs/cargo@v1 run: cargo generate-lockfile
with:
command: generate-lockfile
- name: Cache Dependencies - name: Cache Dependencies
uses: Swatinem/rust-cache@v1.2.0 uses: Swatinem/rust-cache@v2.1.0
- name: check minimal - name: check minimal
uses: actions-rs/cargo@v1 run: cargo ci-min
with: { command: ci-min }
- name: check minimal + examples - name: check minimal + examples
uses: actions-rs/cargo@v1 run: cargo ci-check-min-examples
with: { command: ci-check-min-examples }
- name: check default - name: check default
uses: actions-rs/cargo@v1 run: cargo ci-check
with: { command: ci-check }
- name: tests - name: tests
uses: actions-rs/cargo@v1
timeout-minutes: 40 timeout-minutes: 40
with: run: cargo ci-test --exclude=actix-redis --exclude=actix-session --exclude=actix-limitation -- --nocapture
command: ci-test
args: >-
--exclude=actix-redis
--exclude=actix-session
--exclude=actix-limitation
-- --nocapture
- name: Clear the cargo caches - name: Clear the cargo caches
run: | run: |

View File

@ -36,38 +36,30 @@ jobs:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Install ${{ matrix.version }} - name: Install ${{ matrix.version }}
uses: actions-rs/toolchain@v1 run: |
with: rustup install ${{ matrix.version }}-${{ matrix.target.triple }}
toolchain: ${{ matrix.version }}-${{ matrix.target.triple }} rustup override set ${{ matrix.version }}-${{ matrix.target.triple }}
profile: minimal
override: true
- name: Install cargo-hack - name: Install cargo-hack
uses: taiki-e/install-action@cargo-hack uses: taiki-e/install-action@cargo-hack
- name: Generate Cargo.lock - name: Generate Cargo.lock
uses: actions-rs/cargo@v1 run: cargo generate-lockfile
with:
command: generate-lockfile
- name: Cache Dependencies - name: Cache Dependencies
uses: Swatinem/rust-cache@v1.2.0 uses: Swatinem/rust-cache@v2.1.0
- name: check minimal - name: check minimal
uses: actions-rs/cargo@v1 run: cargo ci-min
with: { command: ci-min }
- name: check minimal + examples - name: check minimal + examples
uses: actions-rs/cargo@v1 run: cargo ci-check-min-examples
with: { command: ci-check-min-examples }
- name: check default - name: check default
uses: actions-rs/cargo@v1 run: cargo ci-check
with: { command: ci-check }
- name: tests - name: tests
uses: actions-rs/cargo@v1
timeout-minutes: 40 timeout-minutes: 40
with: { command: ci-test } run: cargo ci-test
- name: Clear the cargo caches - name: Clear the cargo caches
run: | run: |
@ -80,7 +72,11 @@ jobs:
matrix: matrix:
target: target:
- { name: macOS, os: macos-latest, triple: x86_64-apple-darwin } - { name: macOS, os: macos-latest, triple: x86_64-apple-darwin }
- { name: Windows, os: windows-latest, triple: x86_64-pc-windows-msvc } - {
name: Windows,
os: windows-latest,
triple: x86_64-pc-windows-msvc,
}
version: version:
- 1.59 # MSRV - 1.59 # MSRV
- stable - stable
@ -92,43 +88,30 @@ jobs:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Install ${{ matrix.version }} - name: Install ${{ matrix.version }}
uses: actions-rs/toolchain@v1 run: |
with: rustup install ${{ matrix.version }}-${{ matrix.target.triple }}
toolchain: ${{ matrix.version }}-${{ matrix.target.triple }} rustup override set ${{ matrix.version }}-${{ matrix.target.triple }}
profile: minimal
override: true
- name: Install cargo-hack - name: Install cargo-hack
uses: taiki-e/install-action@cargo-hack uses: taiki-e/install-action@cargo-hack
- name: Generate Cargo.lock - name: Generate Cargo.lock
uses: actions-rs/cargo@v1 run: cargo generate-lockfile
with:
command: generate-lockfile
- name: Cache Dependencies - name: Cache Dependencies
uses: Swatinem/rust-cache@v1.2.0 uses: Swatinem/rust-cache@v2.1.0
- name: check minimal - name: check minimal
uses: actions-rs/cargo@v1 run: cargo ci-min
with: { command: ci-min }
- name: check minimal + examples - name: check minimal + examples
uses: actions-rs/cargo@v1 run: cargo ci-check-min-examples
with: { command: ci-check-min-examples }
- name: check default - name: check default
uses: actions-rs/cargo@v1 run: cargo ci-check
with: { command: ci-check }
- name: tests - name: tests
uses: actions-rs/cargo@v1
timeout-minutes: 40 timeout-minutes: 40
with: run: cargo ci-test --exclude=actix-redis --exclude=actix-session --exclude=actix-limitation
command: ci-test
args: >-
--exclude=actix-redis
--exclude=actix-session
--exclude=actix-limitation
- name: Clear the cargo caches - name: Clear the cargo caches
run: | run: |
@ -142,21 +125,15 @@ jobs:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Install Rust (nightly) - name: Install Rust (nightly)
uses: actions-rs/toolchain@v1 run: |
with: rustup install nightly
toolchain: nightly-x86_64-unknown-linux-gnu rustup override set nightly
profile: minimal
override: true
- name: Generate Cargo.lock - name: Generate Cargo.lock
uses: actions-rs/cargo@v1 run: cargo generate-lockfile
with: { command: generate-lockfile }
- name: Cache Dependencies - name: Cache Dependencies
uses: Swatinem/rust-cache@v1.3.0 uses: Swatinem/rust-cache@v2.1.0
- name: doc tests - name: doc tests
uses: actions-rs/cargo@v1
timeout-minutes: 40 timeout-minutes: 40
with: run: cargo ci-doctest -- --nocapture
command: ci-doctest
args: -- --nocapture

View File

@ -21,22 +21,19 @@ jobs:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Install stable - name: Install stable
uses: actions-rs/toolchain@v1 run: |
with: rustup override set stable
toolchain: stable-x86_64-unknown-linux-gnu rustup update stable
profile: minimal
override: true
- name: Generate Cargo.lock - name: Generate Cargo.lock
uses: actions-rs/cargo@v1 run: cargo generate-lockfile
with: { command: generate-lockfile }
- name: Cache Dependencies - name: Cache Dependencies
uses: Swatinem/rust-cache@v1.2.0 uses: Swatinem/rust-cache@v2.1.0
- name: Generate coverage file - name: Generate coverage file
run: | run: |
cargo install cargo-tarpaulin --vers "^0.13" cargo install cargo-tarpaulin --vers "^0.13"
cargo tarpaulin --workspace --out Xml --verbose cargo tarpaulin --workspace --out Xml --verbose
- name: Upload to Codecov - name: Upload to Codecov
uses: codecov/codecov-action@v1 uses: codecov/codecov-action@v3
with: { file: cobertura.xml } with: { file: cobertura.xml }

View File

@ -11,15 +11,12 @@ jobs:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Install Rust - name: Install Rust
uses: actions-rs/toolchain@v1 run: |
with: rustup override set nightly
toolchain: nightly rustup update nightly
components: rustfmt rustup component add rustfmt
- name: Check with rustfmt - name: Check with rustfmt
uses: actions-rs/cargo@v1 run: cargo fmt --all -- --check
with:
command: fmt
args: --all -- --check
clippy: clippy:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@ -27,13 +24,9 @@ jobs:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Install Rust - name: Install Rust
uses: actions-rs/toolchain@v1 run: |
with: rustup override set stable
toolchain: stable rustup update stable
components: clippy rustup component add rustfmt
override: true
- name: Check with Clippy - name: Check with Clippy
uses: actions-rs/clippy-check@v1 run: cargo clippy --workspace --tests --all-features
with:
token: ${{ secrets.GITHUB_TOKEN }}
args: --workspace --tests --all-features

View File

@ -12,17 +12,12 @@ jobs:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Install Rust - name: Install Rust
uses: actions-rs/toolchain@v1 run: |
with: rustup override set nightly
toolchain: nightly-x86_64-unknown-linux-gnu rustup update nightly
profile: minimal
override: true
- name: Build Docs - name: Build Docs
uses: actions-rs/cargo@v1 run: cargo doc --workspace --all-features --no-deps
with:
command: doc
args: --workspace --all-features --no-deps
- name: Tweak HTML - name: Tweak HTML
run: echo '<meta http-equiv="refresh" content="0;url=actix_cors/index.html">' > target/doc/index.html run: echo '<meta http-equiv="refresh" content="0;url=actix_cors/index.html">' > target/doc/index.html

View File

@ -42,6 +42,7 @@ These crates are provided by the community.
| [tracing-actix-web] | [![crates.io](https://img.shields.io/crates/v/tracing-actix-web?label=latest)][tracing-actix-web] [![dependency status](https://deps.rs/crate/tracing-actix-web/0.6.0/status.svg)](https://deps.rs/crate/tracing-actix-web/0.6.0) | A middleware to collect telemetry data from applications built on top of the actix-web framework. | | [tracing-actix-web] | [![crates.io](https://img.shields.io/crates/v/tracing-actix-web?label=latest)][tracing-actix-web] [![dependency status](https://deps.rs/crate/tracing-actix-web/0.6.0/status.svg)](https://deps.rs/crate/tracing-actix-web/0.6.0) | A middleware to collect telemetry data from applications built on top of the actix-web framework. |
| [actix-ws] | [![crates.io](https://img.shields.io/crates/v/actix-ws?label=latest)][actix-ws] [![dependency status](https://deps.rs/crate/actix-ws/0.2.5/status.svg)](https://deps.rs/crate/actix-ws/0.2.5) | Actor-less WebSockets for the Actix Runtime. | | [actix-ws] | [![crates.io](https://img.shields.io/crates/v/actix-ws?label=latest)][actix-ws] [![dependency status](https://deps.rs/crate/actix-ws/0.2.5/status.svg)](https://deps.rs/crate/actix-ws/0.2.5) | Actor-less WebSockets for the Actix Runtime. |
| [actix-hash] | [![crates.io](https://img.shields.io/crates/v/actix-hash?label=latest)][actix-hash] [![dependency status](https://deps.rs/crate/actix-hash/0.4.0/status.svg)](https://deps.rs/crate/actix-hash/0.4.0) | Hashing utilities for Actix Web. | | [actix-hash] | [![crates.io](https://img.shields.io/crates/v/actix-hash?label=latest)][actix-hash] [![dependency status](https://deps.rs/crate/actix-hash/0.4.0/status.svg)](https://deps.rs/crate/actix-hash/0.4.0) | Hashing utilities for Actix Web. |
| [actix-bincode] | ![crates.io](https://img.shields.io/crates/v/actix-bincode?label=latest) [![dependency status](https://deps.rs/crate/actix-bincode/0.2.0/status.svg)](https://deps.rs/crate/actix-bincode/0.2.0) | Bincode payload extractor for Actix Web |
To add a crate to this list, submit a pull request. To add a crate to this list, submit a pull request.
@ -72,3 +73,4 @@ To add a crate to this list, submit a pull request.
[tracing-actix-web]: https://crates.io/crates/tracing-actix-web [tracing-actix-web]: https://crates.io/crates/tracing-actix-web
[actix-ws]: https://crates.io/crates/actix-ws [actix-ws]: https://crates.io/crates/actix-ws
[actix-hash]: https://crates.io/crates/actix-hash [actix-hash]: https://crates.io/crates/actix-hash
[actix-bincode]: https://crates.io/crates/actix-bincode

View File

@ -3,6 +3,12 @@
## Unreleased - 2022-xx-xx ## Unreleased - 2022-xx-xx
## 0.6.4 - 2022-10-28
- Add `Cors::allow_private_network_access()` behind an unstable flag (`draft-private-network-access`). [#297]
[#297]: https://github.com/actix/actix-extras/pull/297
## 0.6.3 - 2022-09-21 ## 0.6.3 - 2022-09-21
- Add `Cors::block_on_origin_mismatch()` option for controlling if requests are pre-emptively rejected. [#287] - Add `Cors::block_on_origin_mismatch()` option for controlling if requests are pre-emptively rejected. [#287]
- Minimum supported Rust version (MSRV) is now 1.59 due to transitive `time` dependency. - Minimum supported Rust version (MSRV) is now 1.59 due to transitive `time` dependency.

View File

@ -1,6 +1,6 @@
[package] [package]
name = "actix-cors" name = "actix-cors"
version = "0.6.3" version = "0.6.4"
authors = [ authors = [
"Nikolay Kim <fafhrd91@gmail.com>", "Nikolay Kim <fafhrd91@gmail.com>",
"Rob Ede <robjtede@icloud.com>", "Rob Ede <robjtede@icloud.com>",
@ -12,9 +12,12 @@ repository = "https://github.com/actix/actix-extras.git"
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
edition = "2018" edition = "2018"
[lib] [package.metadata.docs.rs]
name = "actix_cors" rustdoc-args = ["--cfg", "docsrs"]
path = "src/lib.rs" all-features = true
[features]
draft-private-network-access = []
[dependencies] [dependencies]
actix-utils = "3" actix-utils = "3"

View File

@ -3,9 +3,9 @@
> Cross-Origin Resource Sharing (CORS) controls for Actix Web. > Cross-Origin Resource Sharing (CORS) controls for Actix Web.
[![crates.io](https://img.shields.io/crates/v/actix-cors?label=latest)](https://crates.io/crates/actix-cors) [![crates.io](https://img.shields.io/crates/v/actix-cors?label=latest)](https://crates.io/crates/actix-cors)
[![Documentation](https://docs.rs/actix-cors/badge.svg?version=0.6.3)](https://docs.rs/actix-cors/0.6.3) [![Documentation](https://docs.rs/actix-cors/badge.svg?version=0.6.4)](https://docs.rs/actix-cors/0.6.4)
![Apache 2.0 or MIT licensed](https://img.shields.io/crates/l/actix-cors) ![Apache 2.0 or MIT licensed](https://img.shields.io/crates/l/actix-cors)
[![Dependency Status](https://deps.rs/crate/actix-cors/0.6.3/status.svg)](https://deps.rs/crate/actix-cors/0.6.3) [![Dependency Status](https://deps.rs/crate/actix-cors/0.6.4/status.svg)](https://deps.rs/crate/actix-cors/0.6.4)
## Documentation & Resources ## Documentation & Resources

View File

@ -101,6 +101,8 @@ impl Cors {
preflight: true, preflight: true,
send_wildcard: false, send_wildcard: false,
supports_credentials: true, supports_credentials: true,
#[cfg(feature = "draft-private-network-access")]
allow_private_network_access: false,
vary_header: true, vary_header: true,
block_on_origin_mismatch: true, block_on_origin_mismatch: true,
}; };
@ -370,7 +372,7 @@ impl Cors {
/// [Fetch Standard CORS protocol]: https://fetch.spec.whatwg.org/#http-cors-protocol /// [Fetch Standard CORS protocol]: https://fetch.spec.whatwg.org/#http-cors-protocol
pub fn max_age(mut self, max_age: impl Into<Option<usize>>) -> Cors { pub fn max_age(mut self, max_age: impl Into<Option<usize>>) -> Cors {
if let Some(cors) = cors(&mut self.inner, &self.error) { if let Some(cors) = cors(&mut self.inner, &self.error) {
cors.max_age = max_age.into() cors.max_age = max_age.into();
} }
self self
@ -389,7 +391,7 @@ impl Cors {
/// Defaults to `false`. /// Defaults to `false`.
pub fn send_wildcard(mut self) -> Cors { pub fn send_wildcard(mut self) -> Cors {
if let Some(cors) = cors(&mut self.inner, &self.error) { if let Some(cors) = cors(&mut self.inner, &self.error) {
cors.send_wildcard = true cors.send_wildcard = true;
} }
self self
@ -412,7 +414,27 @@ impl Cors {
/// [Fetch Standard CORS protocol]: https://fetch.spec.whatwg.org/#http-cors-protocol /// [Fetch Standard CORS protocol]: https://fetch.spec.whatwg.org/#http-cors-protocol
pub fn supports_credentials(mut self) -> Cors { pub fn supports_credentials(mut self) -> Cors {
if let Some(cors) = cors(&mut self.inner, &self.error) { if let Some(cors) = cors(&mut self.inner, &self.error) {
cors.supports_credentials = true cors.supports_credentials = true;
}
self
}
/// Allow private network access.
///
/// If true, injects the `Access-Control-Allow-Private-Network: true` header in responses if the
/// request contained the `Access-Control-Request-Private-Network: true` header.
///
/// For more information on this behavior, see the draft [Private Network Access] spec.
///
/// Defaults to `false`.
///
/// [Private Network Access]: https://wicg.github.io/private-network-access
#[cfg(feature = "draft-private-network-access")]
#[cfg_attr(docsrs, doc(cfg(feature = "draft-private-network-access")))]
pub fn allow_private_network_access(mut self) -> Cors {
if let Some(cors) = cors(&mut self.inner, &self.error) {
cors.allow_private_network_access = true;
} }
self self
@ -430,7 +452,7 @@ impl Cors {
/// By default, `Vary` header support is enabled. /// By default, `Vary` header support is enabled.
pub fn disable_vary_header(mut self) -> Cors { pub fn disable_vary_header(mut self) -> Cors {
if let Some(cors) = cors(&mut self.inner, &self.error) { if let Some(cors) = cors(&mut self.inner, &self.error) {
cors.vary_header = false cors.vary_header = false;
} }
self self
@ -444,7 +466,7 @@ impl Cors {
/// By default *preflight* support is enabled. /// By default *preflight* support is enabled.
pub fn disable_preflight(mut self) -> Cors { pub fn disable_preflight(mut self) -> Cors {
if let Some(cors) = cors(&mut self.inner, &self.error) { if let Some(cors) = cors(&mut self.inner, &self.error) {
cors.preflight = false cors.preflight = false;
} }
self self
@ -462,7 +484,7 @@ impl Cors {
/// Defaults to `true`. /// Defaults to `true`.
pub fn block_on_origin_mismatch(mut self, block: bool) -> Cors { pub fn block_on_origin_mismatch(mut self, block: bool) -> Cors {
if let Some(cors) = cors(&mut self.inner, &self.error) { if let Some(cors) = cors(&mut self.inner, &self.error) {
cors.block_on_origin_mismatch = block cors.block_on_origin_mismatch = block;
} }
self self
@ -492,6 +514,8 @@ impl Default for Cors {
preflight: true, preflight: true,
send_wildcard: false, send_wildcard: false,
supports_credentials: false, supports_credentials: false,
#[cfg(feature = "draft-private-network-access")]
allow_private_network_access: false,
vary_header: true, vary_header: true,
block_on_origin_mismatch: true, block_on_origin_mismatch: true,
}; };

View File

@ -64,6 +64,8 @@ pub(crate) struct Inner {
pub(crate) preflight: bool, pub(crate) preflight: bool,
pub(crate) send_wildcard: bool, pub(crate) send_wildcard: bool,
pub(crate) supports_credentials: bool, pub(crate) supports_credentials: bool,
#[cfg(feature = "draft-private-network-access")]
pub(crate) allow_private_network_access: bool,
pub(crate) vary_header: bool, pub(crate) vary_header: bool,
pub(crate) block_on_origin_mismatch: bool, pub(crate) block_on_origin_mismatch: bool,
} }
@ -219,8 +221,20 @@ pub(crate) fn add_vary_header(headers: &mut HeaderMap) {
let mut val: Vec<u8> = Vec::with_capacity(hdr.len() + 71); let mut val: Vec<u8> = Vec::with_capacity(hdr.len() + 71);
val.extend(hdr.as_bytes()); val.extend(hdr.as_bytes());
val.extend(b", Origin, Access-Control-Request-Method, Access-Control-Request-Headers"); val.extend(b", Origin, Access-Control-Request-Method, Access-Control-Request-Headers");
#[cfg(feature = "draft-private-network-access")]
val.extend(b", Access-Control-Allow-Private-Network");
val.try_into().unwrap() val.try_into().unwrap()
} }
#[cfg(feature = "draft-private-network-access")]
None => HeaderValue::from_static(
"Origin, Access-Control-Request-Method, Access-Control-Request-Headers, \
Access-Control-Allow-Private-Network",
),
#[cfg(not(feature = "draft-private-network-access"))]
None => HeaderValue::from_static( None => HeaderValue::from_static(
"Origin, Access-Control-Request-Method, Access-Control-Request-Headers", "Origin, Access-Control-Request-Method, Access-Control-Request-Headers",
), ),

View File

@ -1,11 +1,16 @@
//! Cross-Origin Resource Sharing (CORS) controls for Actix Web. //! Cross-Origin Resource Sharing (CORS) controls for Actix Web.
//! //!
//! This middleware can be applied to both applications and resources. Once built, a //! This middleware can be applied to both applications and resources. Once built, a [`Cors`]
//! [`Cors`] builder can be used as an argument for Actix Web's `App::wrap()`, //! builder can be used as an argument for Actix Web's `App::wrap()`, `Scope::wrap()`, or
//! `Scope::wrap()`, or `Resource::wrap()` methods. //! `Resource::wrap()` methods.
//! //!
//! This CORS middleware automatically handles `OPTIONS` preflight requests. //! This CORS middleware automatically handles `OPTIONS` preflight requests.
//! //!
//! # Crate Features
//! - `draft-private-network-access`: ⚠️ Unstable. Adds opt-in support for the [Private Network
//! Access] spec extensions. This feature is unstable since it will follow any breaking changes in
//! the draft spec until it is finalized.
//!
//! # Example //! # Example
//! ```no_run //! ```no_run
//! use actix_cors::Cors; //! use actix_cors::Cors;
@ -40,12 +45,15 @@
//! Ok(()) //! Ok(())
//! } //! }
//! ``` //! ```
//!
//! [Private Network Access]: https://wicg.github.io/private-network-access
#![forbid(unsafe_code)] #![forbid(unsafe_code)]
#![deny(rust_2018_idioms, nonstandard_style)] #![deny(rust_2018_idioms, nonstandard_style)]
#![warn(future_incompatible, missing_docs, missing_debug_implementations)] #![warn(future_incompatible, missing_docs, missing_debug_implementations)]
#![doc(html_logo_url = "https://actix.rs/img/logo.png")] #![doc(html_logo_url = "https://actix.rs/img/logo.png")]
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")] #![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
#![cfg_attr(docsrs, feature(doc_cfg))]
mod all_or_some; mod all_or_some;
mod builder; mod builder;

View File

@ -93,6 +93,18 @@ impl<S> CorsMiddleware<S> {
res.insert_header((header::ACCESS_CONTROL_ALLOW_HEADERS, headers.clone())); res.insert_header((header::ACCESS_CONTROL_ALLOW_HEADERS, headers.clone()));
} }
#[cfg(feature = "draft-private-network-access")]
if inner.allow_private_network_access
&& req
.headers()
.contains_key("access-control-request-private-network")
{
res.insert_header((
header::HeaderName::from_static("access-control-allow-private-network"),
HeaderValue::from_static("true"),
));
}
if inner.supports_credentials { if inner.supports_credentials {
res.insert_header(( res.insert_header((
header::ACCESS_CONTROL_ALLOW_CREDENTIALS, header::ACCESS_CONTROL_ALLOW_CREDENTIALS,
@ -162,6 +174,19 @@ impl<S> CorsMiddleware<S> {
); );
} }
#[cfg(feature = "draft-private-network-access")]
if inner.allow_private_network_access
&& res
.request()
.headers()
.contains_key("access-control-request-private-network")
{
res.headers_mut().insert(
header::HeaderName::from_static("access-control-allow-private-network"),
HeaderValue::from_static("true"),
);
}
if inner.vary_header { if inner.vary_header {
add_vary_header(res.headers_mut()); add_vary_header(res.headers_mut());
} }

View File

@ -264,10 +264,16 @@ async fn test_response() {
.get(header::ACCESS_CONTROL_ALLOW_ORIGIN) .get(header::ACCESS_CONTROL_ALLOW_ORIGIN)
.map(HeaderValue::as_bytes) .map(HeaderValue::as_bytes)
); );
#[cfg(not(feature = "draft-private-network-access"))]
assert_eq!( assert_eq!(
resp.headers().get(header::VARY).map(HeaderValue::as_bytes), resp.headers().get(header::VARY).map(HeaderValue::as_bytes),
Some(&b"Origin, Access-Control-Request-Method, Access-Control-Request-Headers"[..]), Some(&b"Origin, Access-Control-Request-Method, Access-Control-Request-Headers"[..]),
); );
#[cfg(feature = "draft-private-network-access")]
assert_eq!(
resp.headers().get(header::VARY).map(HeaderValue::as_bytes),
Some(&b"Origin, Access-Control-Request-Method, Access-Control-Request-Headers, Access-Control-Allow-Private-Network"[..]),
);
#[allow(clippy::needless_collect)] #[allow(clippy::needless_collect)]
{ {
@ -311,9 +317,18 @@ async fn test_response() {
.method(Method::OPTIONS) .method(Method::OPTIONS)
.to_srv_request(); .to_srv_request();
let resp = test::call_service(&cors, req).await; let resp = test::call_service(&cors, req).await;
#[cfg(not(feature = "draft-private-network-access"))]
assert_eq!( assert_eq!(
resp.headers().get(header::VARY).map(HeaderValue::as_bytes), resp.headers()
Some(&b"Accept, Origin, Access-Control-Request-Method, Access-Control-Request-Headers"[..]), .get(header::VARY)
.map(HeaderValue::as_bytes)
.unwrap(),
b"Accept, Origin, Access-Control-Request-Method, Access-Control-Request-Headers",
);
#[cfg(feature = "draft-private-network-access")]
assert_eq!(
resp.headers().get(header::VARY).map(HeaderValue::as_bytes).unwrap(),
b"Accept, Origin, Access-Control-Request-Method, Access-Control-Request-Headers, Access-Control-Allow-Private-Network",
); );
let cors = Cors::default() let cors = Cors::default()
@ -463,6 +478,7 @@ async fn vary_header_on_all_handled_responses() {
assert!(resp assert!(resp
.headers() .headers()
.contains_key(header::ACCESS_CONTROL_ALLOW_METHODS)); .contains_key(header::ACCESS_CONTROL_ALLOW_METHODS));
#[cfg(not(feature = "draft-private-network-access"))]
assert_eq!( assert_eq!(
resp.headers() resp.headers()
.get(header::VARY) .get(header::VARY)
@ -471,6 +487,15 @@ async fn vary_header_on_all_handled_responses() {
.unwrap(), .unwrap(),
"Origin, Access-Control-Request-Method, Access-Control-Request-Headers", "Origin, Access-Control-Request-Method, Access-Control-Request-Headers",
); );
#[cfg(feature = "draft-private-network-access")]
assert_eq!(
resp.headers()
.get(header::VARY)
.expect("response should have Vary header")
.to_str()
.unwrap(),
"Origin, Access-Control-Request-Method, Access-Control-Request-Headers, Access-Control-Allow-Private-Network",
);
// follow-up regular request // follow-up regular request
let req = TestRequest::default() let req = TestRequest::default()
@ -479,6 +504,7 @@ async fn vary_header_on_all_handled_responses() {
.to_srv_request(); .to_srv_request();
let resp = test::call_service(&cors, req).await; let resp = test::call_service(&cors, req).await;
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
#[cfg(not(feature = "draft-private-network-access"))]
assert_eq!( assert_eq!(
resp.headers() resp.headers()
.get(header::VARY) .get(header::VARY)
@ -487,6 +513,15 @@ async fn vary_header_on_all_handled_responses() {
.unwrap(), .unwrap(),
"Origin, Access-Control-Request-Method, Access-Control-Request-Headers", "Origin, Access-Control-Request-Method, Access-Control-Request-Headers",
); );
#[cfg(feature = "draft-private-network-access")]
assert_eq!(
resp.headers()
.get(header::VARY)
.expect("response should have Vary header")
.to_str()
.unwrap(),
"Origin, Access-Control-Request-Method, Access-Control-Request-Headers, Access-Control-Allow-Private-Network",
);
let cors = Cors::default() let cors = Cors::default()
.allow_any_method() .allow_any_method()
@ -501,6 +536,7 @@ async fn vary_header_on_all_handled_responses() {
.to_srv_request(); .to_srv_request();
let resp = test::call_service(&cors, req).await; let resp = test::call_service(&cors, req).await;
assert_eq!(resp.status(), StatusCode::BAD_REQUEST); assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
#[cfg(not(feature = "draft-private-network-access"))]
assert_eq!( assert_eq!(
resp.headers() resp.headers()
.get(header::VARY) .get(header::VARY)
@ -509,11 +545,21 @@ async fn vary_header_on_all_handled_responses() {
.unwrap(), .unwrap(),
"Origin, Access-Control-Request-Method, Access-Control-Request-Headers", "Origin, Access-Control-Request-Method, Access-Control-Request-Headers",
); );
#[cfg(feature = "draft-private-network-access")]
assert_eq!(
resp.headers()
.get(header::VARY)
.expect("response should have Vary header")
.to_str()
.unwrap(),
"Origin, Access-Control-Request-Method, Access-Control-Request-Headers, Access-Control-Allow-Private-Network",
);
// regular request no origin // regular request no origin
let req = TestRequest::default().method(Method::PUT).to_srv_request(); let req = TestRequest::default().method(Method::PUT).to_srv_request();
let resp = test::call_service(&cors, req).await; let resp = test::call_service(&cors, req).await;
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
#[cfg(not(feature = "draft-private-network-access"))]
assert_eq!( assert_eq!(
resp.headers() resp.headers()
.get(header::VARY) .get(header::VARY)
@ -522,6 +568,15 @@ async fn vary_header_on_all_handled_responses() {
.unwrap(), .unwrap(),
"Origin, Access-Control-Request-Method, Access-Control-Request-Headers", "Origin, Access-Control-Request-Method, Access-Control-Request-Headers",
); );
#[cfg(feature = "draft-private-network-access")]
assert_eq!(
resp.headers()
.get(header::VARY)
.expect("response should have Vary header")
.to_str()
.unwrap(),
"Origin, Access-Control-Request-Method, Access-Control-Request-Headers, Access-Control-Allow-Private-Network",
);
} }
#[actix_web::test] #[actix_web::test]
@ -578,3 +633,42 @@ async fn expose_all_request_header_values() {
assert!(cd_hdr.contains("content-disposition")); assert!(cd_hdr.contains("content-disposition"));
assert!(cd_hdr.contains("access-control-allow-origin")); assert!(cd_hdr.contains("access-control-allow-origin"));
} }
#[cfg(feature = "draft-private-network-access")]
#[actix_web::test]
async fn private_network_access() {
let cors = Cors::permissive()
.allowed_origin("https://public.site")
.allow_private_network_access()
.new_transform(fn_service(|req: ServiceRequest| async move {
let res = req.into_response(
HttpResponse::Ok()
.insert_header((header::CONTENT_DISPOSITION, "test disposition"))
.finish(),
);
Ok(res)
}))
.await
.unwrap();
let req = TestRequest::default()
.insert_header((header::ORIGIN, "https://public.site"))
.insert_header((header::ACCESS_CONTROL_REQUEST_METHOD, "POST"))
.insert_header((header::ACCESS_CONTROL_ALLOW_CREDENTIALS, "true"))
.to_srv_request();
let res = test::call_service(&cors, req).await;
assert!(res.headers().contains_key("access-control-allow-origin"));
let req = TestRequest::default()
.insert_header((header::ORIGIN, "https://public.site"))
.insert_header((header::ACCESS_CONTROL_REQUEST_METHOD, "POST"))
.insert_header((header::ACCESS_CONTROL_ALLOW_CREDENTIALS, "true"))
.insert_header(("Access-Control-Request-Private-Network", "true"))
.to_srv_request();
let res = test::call_service(&cors, req).await;
assert!(res.headers().contains_key("access-control-allow-origin"));
assert!(res
.headers()
.contains_key("access-control-allow-private-network"));
}

View File

@ -1,7 +1,9 @@
# Changes # Changes
## Unreleased - 2021-xx-xx ## Unreleased - 2021-xx-xx
- Set secure attribute when adding a session removal cookie. [#300]
[#300]: https://github.com/actix/actix-extras/pull/300
## 0.7.2 - 2022-09-11 ## 0.7.2 - 2022-09-11
- Set SameSite attribute when adding a session removal cookie. [#284] - Set SameSite attribute when adding a session removal cookie. [#284]

View File

@ -444,6 +444,7 @@ fn delete_session_cookie(
) -> Result<(), anyhow::Error> { ) -> Result<(), anyhow::Error> {
let removal_cookie = Cookie::build(config.name.clone(), "") let removal_cookie = Cookie::build(config.name.clone(), "")
.path(config.path.clone()) .path(config.path.clone())
.secure(config.secure)
.http_only(config.http_only) .http_only(config.http_only)
.same_site(config.same_site); .same_site(config.same_site);

View File

@ -48,7 +48,7 @@ async fn cookie_storage() -> std::io::Result<()> {
let deletion_cookie = logout_response.response().cookies().next().unwrap(); let deletion_cookie = logout_response.response().cookies().next().unwrap();
assert_eq!(deletion_cookie.name(), "id"); assert_eq!(deletion_cookie.name(), "id");
assert_eq!(deletion_cookie.path().unwrap(), "/test"); assert_eq!(deletion_cookie.path().unwrap(), "/test");
assert!(deletion_cookie.secure().is_none()); assert!(deletion_cookie.secure().unwrap());
assert!(deletion_cookie.http_only().unwrap()); assert!(deletion_cookie.http_only().unwrap());
assert_eq!(deletion_cookie.max_age().unwrap(), Duration::ZERO); assert_eq!(deletion_cookie.max_age().unwrap(), Duration::ZERO);
assert_eq!(deletion_cookie.domain().unwrap(), "localhost"); assert_eq!(deletion_cookie.domain().unwrap(), "localhost");