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:
commit
bedc1353c8
4
.github/workflows/ci-post-merge.yml
vendored
4
.github/workflows/ci-post-merge.yml
vendored
@ -35,7 +35,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
toolchain: nightly
|
toolchain: nightly
|
||||||
|
|
||||||
- uses: taiki-e/install-action@v2.20.3
|
- uses: taiki-e/install-action@v2.21.7
|
||||||
with:
|
with:
|
||||||
tool: cargo-hack
|
tool: cargo-hack
|
||||||
|
|
||||||
@ -77,7 +77,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
toolchain: nightly
|
toolchain: nightly
|
||||||
|
|
||||||
- uses: taiki-e/install-action@v2.20.3
|
- uses: taiki-e/install-action@v2.21.7
|
||||||
with:
|
with:
|
||||||
tool: cargo-hack
|
tool: cargo-hack
|
||||||
|
|
||||||
|
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
@ -49,7 +49,7 @@ jobs:
|
|||||||
toolchain: ${{ matrix.version.version }}
|
toolchain: ${{ matrix.version.version }}
|
||||||
|
|
||||||
- name: Install cargo-hack
|
- name: Install cargo-hack
|
||||||
uses: taiki-e/install-action@v2.20.3
|
uses: taiki-e/install-action@v2.21.7
|
||||||
with:
|
with:
|
||||||
tool: cargo-hack
|
tool: cargo-hack
|
||||||
|
|
||||||
@ -100,7 +100,7 @@ jobs:
|
|||||||
toolchain: ${{ matrix.version.version }}
|
toolchain: ${{ matrix.version.version }}
|
||||||
|
|
||||||
- name: Install cargo-hack
|
- name: Install cargo-hack
|
||||||
uses: taiki-e/install-action@v2.20.3
|
uses: taiki-e/install-action@v2.21.7
|
||||||
with:
|
with:
|
||||||
tool: cargo-hack
|
tool: cargo-hack
|
||||||
|
|
||||||
|
16
.github/workflows/upload-doc.yml
vendored
16
.github/workflows/upload-doc.yml
vendored
@ -1,9 +1,11 @@
|
|||||||
name: Upload Documentation
|
name: Upload Documentation
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push: { branches: [master] }
|
push:
|
||||||
|
branches: [master]
|
||||||
|
|
||||||
permissions: { contents: write }
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.workflow }}-${{ github.ref }}
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
@ -18,10 +20,11 @@ jobs:
|
|||||||
|
|
||||||
- name: Install Rust (nightly)
|
- name: Install Rust (nightly)
|
||||||
uses: actions-rust-lang/setup-rust-toolchain@v1.5.0
|
uses: actions-rust-lang/setup-rust-toolchain@v1.5.0
|
||||||
with: { toolchain: nightly }
|
with:
|
||||||
|
toolchain: nightly
|
||||||
|
|
||||||
- name: Build Docs
|
- name: Build Docs
|
||||||
run: cargo doc --workspace --all-features --no-deps
|
run: cargo doc --no-deps --workspace --all-features
|
||||||
|
|
||||||
- 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
|
||||||
@ -29,6 +32,5 @@ jobs:
|
|||||||
- name: Deploy to GitHub Pages
|
- name: Deploy to GitHub Pages
|
||||||
uses: JamesIves/github-pages-deploy-action@v4.4.3
|
uses: JamesIves/github-pages-deploy-action@v4.4.3
|
||||||
with:
|
with:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
folder: target/doc
|
||||||
BRANCH: gh-pages
|
branch: gh-pages
|
||||||
FOLDER: target/doc
|
|
||||||
|
@ -9,6 +9,7 @@ members = [
|
|||||||
"actix-session",
|
"actix-session",
|
||||||
"actix-settings",
|
"actix-settings",
|
||||||
"actix-web-httpauth",
|
"actix-web-httpauth",
|
||||||
|
"actix-ws",
|
||||||
]
|
]
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
|
@ -186,8 +186,7 @@
|
|||||||
same "printed page" as the copyright notice for easier
|
same "printed page" as the copyright notice for easier
|
||||||
identification within third-party archives.
|
identification within third-party archives.
|
||||||
|
|
||||||
Copyright 2017-NOW Nikolay Kim
|
Copyright 2017-NOW Actix team
|
||||||
Copyright 2017-NOW svartalf and Actix team
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
Copyright (c) 2017 Nikolay Kim
|
Copyright (c) 2023 Actix team
|
||||||
Copyright (c) 2017 svartalf and Actix team
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any
|
Permission is hereby granted, free of charge, to any
|
||||||
person obtaining a copy of this software and associated
|
person obtaining a copy of this software and associated
|
||||||
|
52
README.md
52
README.md
@ -10,15 +10,16 @@
|
|||||||
## Crates by @actix
|
## Crates by @actix
|
||||||
|
|
||||||
| Crate | | |
|
| Crate | | |
|
||||||
| -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
|
| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
|
||||||
| [actix-cors] | [](https://crates.io/crates/actix-cors) [](https://deps.rs/crate/actix-cors/0.6.4) | Cross-Origin Resource Sharing (CORS) controls. |
|
| [actix-cors] | [](https://crates.io/crates/actix-cors) [](https://deps.rs/crate/actix-cors) | Cross-Origin Resource Sharing (CORS) controls. |
|
||||||
| [actix-identity] | [](https://crates.io/crates/actix-identity) [](https://deps.rs/crate/actix-identity/0.6.0) | Identity management. |
|
| [actix-identity] | [](https://crates.io/crates/actix-identity) [](https://deps.rs/crate/actix-identity) | Identity management. |
|
||||||
| [actix-limitation] | [](https://crates.io/crates/actix-limitation) [](https://deps.rs/crate/actix-limitation/0.5.1) | Rate-limiting using a fixed window counter for arbitrary keys, backed by Redis. |
|
| [actix-limitation] | [](https://crates.io/crates/actix-limitation) [](https://deps.rs/crate/actix-limitation) | Rate-limiting using a fixed window counter for arbitrary keys, backed by Redis. |
|
||||||
| [actix-protobuf] | [](https://crates.io/crates/actix-protobuf) [](https://deps.rs/crate/actix-protobuf/0.10.0) | Protobuf payload extractor. |
|
| [actix-protobuf] | [](https://crates.io/crates/actix-protobuf) [](https://deps.rs/crate/actix-protobuf) | Protobuf payload extractor. |
|
||||||
| [actix-redis] | [](https://crates.io/crates/actix-redis) [](https://deps.rs/crate/actix-redis/0.13.0) | Actor-based Redis client. |
|
| [actix-redis] | [](https://crates.io/crates/actix-redis) [](https://deps.rs/crate/actix-redis) | Actor-based Redis client. |
|
||||||
| [actix-session] | [](https://crates.io/crates/actix-session) [](https://deps.rs/crate/actix-session/0.8.0) | Session management. |
|
| [actix-session] | [](https://crates.io/crates/actix-session) [](https://deps.rs/crate/actix-session) | Session management. |
|
||||||
| [actix-settings] | [](https://crates.io/crates/actix-settings) [](https://deps.rs/crate/actix-settings/0.6.0) | Easily manage Actix Web's settings from a TOML file and environment variables. |
|
| [actix-settings] | [](https://crates.io/crates/actix-settings) [](https://deps.rs/crate/actix-settings) | Easily manage Actix Web's settings from a TOML file and environment variables. |
|
||||||
| [actix-web-httpauth] | [](https://crates.io/crates/actix-web-httpauth) [](https://deps.rs/crate/actix-web-httpauth/0.8.1) | HTTP authentication schemes. |
|
| [actix-web-httpauth] | [](https://crates.io/crates/actix-web-httpauth) [](https://deps.rs/crate/actix-web-httpauth) | HTTP authentication schemes. |
|
||||||
|
| [actix-ws] | [][actix-ws] [](https://deps.rs/crate/actix-ws) | WebSockets for Actix Web, without actors. |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -27,23 +28,22 @@
|
|||||||
These crates are provided by the community.
|
These crates are provided by the community.
|
||||||
|
|
||||||
| Crate | | |
|
| Crate | | |
|
||||||
| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
|
| -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
|
||||||
| [actix-web-lab] | [][actix-web-lab] [](https://deps.rs/crate/actix-web-lab/0.19.1) | Experimental extractors, middleware, and other extras for possible inclusion in Actix Web. |
|
| [actix-web-lab] | [][actix-web-lab] [](https://deps.rs/crate/actix-web-lab) | Experimental extractors, middleware, and other extras for possible inclusion in Actix Web. |
|
||||||
| [actix-multipart-extract] | [][actix-multipart-extract] [](https://deps.rs/crate/actix-multipart-extract/0.1.5) | Better multipart form support for Actix Web. |
|
| [actix-multipart-extract] | [][actix-multipart-extract] [](https://deps.rs/crate/actix-multipart-extract) | Better multipart form support for Actix Web. |
|
||||||
| [actix-form-data] | [][actix-form-data] [](https://deps.rs/crate/actix-form-data/0.7.0-beta.4) | Multipart form data from actix multipart streams |
|
| [actix-form-data] | [][actix-form-data] [](https://deps.rs/crate/actix-form-data) | Multipart form data from actix multipart streams |
|
||||||
| [actix-governor] | [][actix-governor] [](https://deps.rs/crate/actix-governor/0.5.1) | Rate-limiting backed by governor. |
|
| [actix-governor] | [][actix-governor] [](https://deps.rs/crate/actix-governor) | Rate-limiting backed by governor. |
|
||||||
| [actix-casbin] | [][actix-casbin] [](https://deps.rs/crate/actix-casbin/0.4.2) | Authorization library that supports access control models like ACL, RBAC & ABAC. |
|
| [actix-casbin] | [][actix-casbin] [](https://deps.rs/crate/actix-casbin) | Authorization library that supports access control models like ACL, RBAC & ABAC. |
|
||||||
| [actix-ip-filter] | [][actix-ip-filter] [](https://deps.rs/crate/actix-ip-filter/0.3.1) | IP address filter. Supports glob patterns. |
|
| [actix-ip-filter] | [][actix-ip-filter] [](https://deps.rs/crate/actix-ip-filter) | IP address filter. Supports glob patterns. |
|
||||||
| [actix-web-static-files] | [][actix-web-static-files] [](https://deps.rs/crate/actix-web-static-files/4.0.1) | Static files as embedded resources. |
|
| [actix-web-static-files] | [][actix-web-static-files] [](https://deps.rs/crate/actix-web-static-files) | Static files as embedded resources. |
|
||||||
| [actix-web-grants] | [][actix-web-grants] [](https://deps.rs/crate/actix-web-grants/3.0.2) | Extension for validating user authorities. |
|
| [actix-web-grants] | [][actix-web-grants] [](https://deps.rs/crate/actix-web-grants) | Extension for validating user authorities. |
|
||||||
| [aliri_actix] | [][aliri_actix] [](https://deps.rs/crate/aliri_actix/0.9.0) | Endpoint authorization and authentication using scoped OAuth2 JWT tokens. |
|
| [aliri_actix] | [][aliri_actix] [](https://deps.rs/crate/aliri_actix) | Endpoint authorization and authentication using scoped OAuth2 JWT tokens. |
|
||||||
| [actix-web-flash-messages] | [][actix-web-flash-messages] [](https://deps.rs/crate/actix-web-flash-messages/0.4.2) | Support for flash messages/one-time notifications in `actix-web`. |
|
| [actix-web-flash-messages] | [][actix-web-flash-messages] [](https://deps.rs/crate/actix-web-flash-messages) | Support for flash messages/one-time notifications in `actix-web`. |
|
||||||
| [awmp] | [][awmp] [](https://deps.rs/crate/awmp/0.8.1) | An easy to use wrapper around multipart fields for Actix Web. |
|
| [awmp] | [][awmp] [](https://deps.rs/crate/awmp) | An easy to use wrapper around multipart fields for Actix Web. |
|
||||||
| [tracing-actix-web] | [][tracing-actix-web] [](https://deps.rs/crate/tracing-actix-web/0.7.6) | A middleware to collect telemetry data from applications built on top of the Actix Web framework. |
|
| [tracing-actix-web] | [][tracing-actix-web] [](https://deps.rs/crate/tracing-actix-web) | A middleware to collect telemetry data from applications built on top of the Actix Web framework. |
|
||||||
| [actix-ws] | [][actix-ws] [](https://deps.rs/crate/actix-ws/0.2.5) | Actor-less WebSockets for the Actix Runtime. |
|
| [actix-hash] | [][actix-hash] [](https://deps.rs/crate/actix-hash) | Hashing utilities for Actix Web. |
|
||||||
| [actix-hash] | [][actix-hash] [](https://deps.rs/crate/actix-hash/0.5.0) | Hashing utilities for Actix Web. |
|
| [actix-bincode] |  [](https://deps.rs/crate/actix-bincode) | Bincode payload extractor for Actix Web |
|
||||||
| [actix-bincode] |  [](https://deps.rs/crate/actix-bincode/0.2.2) | Bincode payload extractor for Actix Web |
|
| [sentinel-actix] |  [](https://deps.rs/crate/sentinel-actix) | General and flexible protection for Actix Web |
|
||||||
| [sentinel-actix] |  [](https://deps.rs/crate/sentinel-actix/0.1.0) | General and flexible protection 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.
|
||||||
|
|
||||||
|
@ -52,13 +52,20 @@ static ALL_METHODS_SET: Lazy<HashSet<Method>> = Lazy::new(|| {
|
|||||||
/// The alternative [`Cors::permissive()`] constructor is available for local development, allowing
|
/// The alternative [`Cors::permissive()`] constructor is available for local development, allowing
|
||||||
/// all origins and headers, etc. **The permissive constructor should not be used in production.**
|
/// all origins and headers, etc. **The permissive constructor should not be used in production.**
|
||||||
///
|
///
|
||||||
|
/// # Behavior
|
||||||
|
///
|
||||||
|
/// In all cases, behavior for this crate follows the [Fetch Standard CORS protocol]. See that
|
||||||
|
/// document for information on exact semantics for configuration options and combinations.
|
||||||
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
|
///
|
||||||
/// Errors surface in the middleware initialization phase. This means that, if you have logs enabled
|
/// Errors surface in the middleware initialization phase. This means that, if you have logs enabled
|
||||||
/// in Actix Web (using `env_logger` or other crate that exposes logs from the `log` crate), error
|
/// in Actix Web (using `env_logger` or other crate that exposes logs from the `log` crate), error
|
||||||
/// messages will outline what is wrong with the CORS configuration in the server logs and the
|
/// messages will outline what is wrong with the CORS configuration in the server logs and the
|
||||||
/// server will fail to start up or serve requests.
|
/// server will fail to start up or serve requests.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use actix_cors::Cors;
|
/// use actix_cors::Cors;
|
||||||
/// use actix_web::http::header;
|
/// use actix_web::http::header;
|
||||||
@ -72,6 +79,8 @@ static ALL_METHODS_SET: Lazy<HashSet<Method>> = Lazy::new(|| {
|
|||||||
///
|
///
|
||||||
/// // `cors` can now be used in `App::wrap`.
|
/// // `cors` can now be used in `App::wrap`.
|
||||||
/// ```
|
/// ```
|
||||||
|
///
|
||||||
|
/// [Fetch Standard CORS protocol]: https://fetch.spec.whatwg.org/#http-cors-protocol
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Cors {
|
pub struct Cors {
|
||||||
inner: Rc<Inner>,
|
inner: Rc<Inner>,
|
||||||
@ -79,7 +88,8 @@ pub struct Cors {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Cors {
|
impl Cors {
|
||||||
/// A very permissive set of default for quick development. Not recommended for production use.
|
/// Constructs a very permissive set of defaults for quick development. (Not recommended for
|
||||||
|
/// production use.)
|
||||||
///
|
///
|
||||||
/// *All* origins, methods, request headers and exposed headers allowed. Credentials supported.
|
/// *All* origins, methods, request headers and exposed headers allowed. Credentials supported.
|
||||||
/// Max age 1 hour. Does not send wildcard.
|
/// Max age 1 hour. Does not send wildcard.
|
||||||
@ -124,7 +134,7 @@ impl Cors {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add an origin that is allowed to make requests.
|
/// Adds an origin that is allowed to make requests.
|
||||||
///
|
///
|
||||||
/// This method allows specifying a finite set of origins to verify the value of the `Origin`
|
/// This method allows specifying a finite set of origins to verify the value of the `Origin`
|
||||||
/// request header. These are `origin-or-null` types in the [Fetch Standard].
|
/// request header. These are `origin-or-null` types in the [Fetch Standard].
|
||||||
@ -177,7 +187,7 @@ impl Cors {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determinate allowed origins by processing requests which didn't match any origins specified
|
/// Determinates allowed origins by processing requests which didn't match any origins specified
|
||||||
/// in the `allowed_origin`.
|
/// in the `allowed_origin`.
|
||||||
///
|
///
|
||||||
/// The function will receive two parameters, the Origin header value, and the `RequestHead` of
|
/// The function will receive two parameters, the Origin header value, and the `RequestHead` of
|
||||||
@ -209,14 +219,11 @@ impl Cors {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set a list of methods which allowed origins can perform.
|
/// Sets a list of methods which allowed origins can perform.
|
||||||
///
|
///
|
||||||
/// These will be sent in the `Access-Control-Allow-Methods` response header as specified in
|
/// These will be sent in the `Access-Control-Allow-Methods` response header.
|
||||||
/// the [Fetch Standard CORS protocol].
|
|
||||||
///
|
///
|
||||||
/// This defaults to an empty set.
|
/// This defaults to an empty set.
|
||||||
///
|
|
||||||
/// [Fetch Standard CORS protocol]: https://fetch.spec.whatwg.org/#http-cors-protocol
|
|
||||||
pub fn allowed_methods<U, M>(mut self, methods: U) -> Cors
|
pub fn allowed_methods<U, M>(mut self, methods: U) -> Cors
|
||||||
where
|
where
|
||||||
U: IntoIterator<Item = M>,
|
U: IntoIterator<Item = M>,
|
||||||
@ -279,16 +286,13 @@ impl Cors {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set a list of request header field names which can be used when this resource is accessed by
|
/// Sets a list of request header field names which can be used when this resource is accessed
|
||||||
/// allowed origins.
|
/// by allowed origins.
|
||||||
///
|
///
|
||||||
/// If `All` is set, whatever is requested by the client in `Access-Control-Request-Headers`
|
/// If `All` is set, whatever is requested by the client in `Access-Control-Request-Headers`
|
||||||
/// will be echoed back in the `Access-Control-Allow-Headers` header as specified in
|
/// will be echoed back in the `Access-Control-Allow-Headers` header.
|
||||||
/// the [Fetch Standard CORS protocol].
|
|
||||||
///
|
///
|
||||||
/// This defaults to an empty set.
|
/// This defaults to an empty set.
|
||||||
///
|
|
||||||
/// [Fetch Standard CORS protocol]: https://fetch.spec.whatwg.org/#http-cors-protocol
|
|
||||||
pub fn allowed_headers<U, H>(mut self, headers: U) -> Cors
|
pub fn allowed_headers<U, H>(mut self, headers: U) -> Cors
|
||||||
where
|
where
|
||||||
U: IntoIterator<Item = H>,
|
U: IntoIterator<Item = H>,
|
||||||
@ -329,13 +333,11 @@ impl Cors {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set a list of headers which are safe to expose to the API of a CORS API specification.
|
/// Sets a list of headers which are safe to expose to the API of a CORS API specification.
|
||||||
/// This corresponds to the `Access-Control-Expose-Headers` response header as specified in
|
///
|
||||||
/// the [Fetch Standard CORS protocol].
|
/// This corresponds to the `Access-Control-Expose-Headers` response header.
|
||||||
///
|
///
|
||||||
/// This defaults to an empty set.
|
/// This defaults to an empty set.
|
||||||
///
|
|
||||||
/// [Fetch Standard CORS protocol]: https://fetch.spec.whatwg.org/#http-cors-protocol
|
|
||||||
pub fn expose_headers<U, H>(mut self, headers: U) -> Cors
|
pub fn expose_headers<U, H>(mut self, headers: U) -> Cors
|
||||||
where
|
where
|
||||||
U: IntoIterator<Item = H>,
|
U: IntoIterator<Item = H>,
|
||||||
@ -364,12 +366,11 @@ impl Cors {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set a maximum time (in seconds) for which this CORS request may be cached. This value is set
|
/// Sets a maximum time (in seconds) for which this CORS request may be cached.
|
||||||
/// as the `Access-Control-Max-Age` header as specified in the [Fetch Standard CORS protocol].
|
///
|
||||||
|
/// This value is set as the `Access-Control-Max-Age` header.
|
||||||
///
|
///
|
||||||
/// Pass a number (of seconds) or use None to disable sending max age header.
|
/// Pass a number (of seconds) or use None to disable sending max age header.
|
||||||
///
|
|
||||||
/// [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();
|
||||||
@ -378,17 +379,17 @@ impl Cors {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set to use wildcard origins.
|
/// Configures use of wildcard (`*`) origin in responses when appropriate.
|
||||||
///
|
///
|
||||||
/// If send wildcard is set and the `allowed_origins` parameter is `All`, a wildcard
|
/// If send wildcard is set and the `allowed_origins` parameter is `All`, a wildcard
|
||||||
/// `Access-Control-Allow-Origin` response header is sent, rather than the request’s
|
/// `Access-Control-Allow-Origin` response header is sent, rather than the request’s
|
||||||
/// `Origin` header.
|
/// `Origin` header.
|
||||||
///
|
///
|
||||||
/// This **CANNOT** be used in conjunction with `allowed_origins` set to `All` and
|
/// This option **CANNOT** be used in conjunction with a [credential
|
||||||
/// `allow_credentials` set to `true`. Depending on the mode of usage, this will either result
|
/// supported](Self::supports_credentials()) configuration. Doing so will result in an error
|
||||||
/// in an `CorsError::CredentialsWithWildcardOrigin` error during actix launch or runtime.
|
/// during server startup.
|
||||||
///
|
///
|
||||||
/// Defaults to `false`.
|
/// Defaults to disabled.
|
||||||
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;
|
||||||
@ -397,21 +398,16 @@ impl Cors {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Allows users to make authenticated requests
|
/// Allows users to make authenticated requests.
|
||||||
///
|
///
|
||||||
/// If true, injects the `Access-Control-Allow-Credentials` header in responses. This allows
|
/// If true, injects the `Access-Control-Allow-Credentials` header in responses. This allows
|
||||||
/// cookies and credentials to be submitted across domains as specified in
|
/// cookies and credentials to be submitted across domains.
|
||||||
/// the [Fetch Standard CORS protocol].
|
|
||||||
///
|
///
|
||||||
/// This option cannot be used in conjunction with an `allowed_origin` set to `All` and
|
/// This option **CANNOT** be used in conjunction with option cannot be used in conjunction
|
||||||
/// `send_wildcards` set to `true`.
|
/// with [wildcard origins](Self::send_wildcard()) configured. Doing so will result in an error
|
||||||
|
/// during server startup.
|
||||||
///
|
///
|
||||||
/// Defaults to `false`.
|
/// Defaults to disabled.
|
||||||
///
|
|
||||||
/// A server initialization error will occur if credentials are allowed, but the Origin is set
|
|
||||||
/// to send wildcards (`*`); this is not allowed by the 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;
|
||||||
@ -439,7 +435,7 @@ impl Cors {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Disable `Vary` header support.
|
/// Disables `Vary` header support.
|
||||||
///
|
///
|
||||||
/// When enabled the header `Vary: Origin` will be returned as per the Fetch Standard
|
/// When enabled the header `Vary: Origin` will be returned as per the Fetch Standard
|
||||||
/// implementation guidelines.
|
/// implementation guidelines.
|
||||||
@ -457,12 +453,12 @@ impl Cors {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Disable support for preflight requests.
|
/// Disables preflight request handling.
|
||||||
///
|
///
|
||||||
/// When enabled CORS middleware automatically handles `OPTIONS` requests.
|
/// When enabled CORS middleware automatically handles `OPTIONS` requests. This is useful for
|
||||||
/// This is useful for application level middleware.
|
/// application level middleware.
|
||||||
///
|
///
|
||||||
/// 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;
|
||||||
@ -480,7 +476,7 @@ impl Cors {
|
|||||||
/// and block requests based on pre-flight requests. Use this setting to allow cURL and other
|
/// and block requests based on pre-flight requests. Use this setting to allow cURL and other
|
||||||
/// non-browser HTTP clients to function as normal, no matter what `Origin` the request has.
|
/// non-browser HTTP clients to function as normal, no matter what `Origin` the request has.
|
||||||
///
|
///
|
||||||
/// 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;
|
||||||
|
@ -89,6 +89,9 @@
|
|||||||
#![forbid(unsafe_code)]
|
#![forbid(unsafe_code)]
|
||||||
#![deny(rust_2018_idioms, nonstandard_style, missing_docs)]
|
#![deny(rust_2018_idioms, nonstandard_style, missing_docs)]
|
||||||
#![warn(future_incompatible)]
|
#![warn(future_incompatible)]
|
||||||
|
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
||||||
|
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
||||||
|
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||||
|
|
||||||
pub mod config;
|
pub mod config;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
|
@ -49,6 +49,7 @@
|
|||||||
#![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_auto_cfg))]
|
||||||
|
|
||||||
use std::{borrow::Cow, fmt, sync::Arc, time::Duration};
|
use std::{borrow::Cow, fmt, sync::Arc, time::Duration};
|
||||||
|
|
||||||
|
@ -3,6 +3,9 @@
|
|||||||
#![forbid(unsafe_code)]
|
#![forbid(unsafe_code)]
|
||||||
#![deny(rust_2018_idioms, nonstandard_style)]
|
#![deny(rust_2018_idioms, nonstandard_style)]
|
||||||
#![warn(future_incompatible)]
|
#![warn(future_incompatible)]
|
||||||
|
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
||||||
|
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
||||||
|
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
fmt,
|
fmt,
|
||||||
|
@ -3,6 +3,9 @@
|
|||||||
#![forbid(unsafe_code)]
|
#![forbid(unsafe_code)]
|
||||||
#![deny(rust_2018_idioms, nonstandard_style)]
|
#![deny(rust_2018_idioms, nonstandard_style)]
|
||||||
#![warn(future_incompatible)]
|
#![warn(future_incompatible)]
|
||||||
|
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
||||||
|
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
||||||
|
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||||
|
|
||||||
use derive_more::{Display, Error, From};
|
use derive_more::{Display, Error, From};
|
||||||
pub use redis_async::{error::Error as RespError, resp::RespValue, resp_array};
|
pub use redis_async::{error::Error as RespError, resp::RespValue, resp_array};
|
||||||
|
@ -5,7 +5,7 @@ authors = [
|
|||||||
"Nikolay Kim <fafhrd91@gmail.com>",
|
"Nikolay Kim <fafhrd91@gmail.com>",
|
||||||
"Luca Palmieri <rust@lpalmieri.com>",
|
"Luca Palmieri <rust@lpalmieri.com>",
|
||||||
]
|
]
|
||||||
description = "Session management for Actix We"
|
description = "Session management for Actix Web"
|
||||||
keywords = ["http", "web", "framework", "async", "session"]
|
keywords = ["http", "web", "framework", "async", "session"]
|
||||||
homepage = "https://actix.rs"
|
homepage = "https://actix.rs"
|
||||||
repository = "https://github.com/actix/actix-extras.git"
|
repository = "https://github.com/actix/actix-extras.git"
|
||||||
|
@ -2,6 +2,15 @@
|
|||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
|
## 0.7.1
|
||||||
|
|
||||||
|
- Fix doc examples.
|
||||||
|
|
||||||
|
## 0.7.0
|
||||||
|
|
||||||
|
- The `ApplySettings` trait now includes a type parameter, allowing multiple types to be implemented per configuration target.
|
||||||
|
- Implement `ApplySettings` for `ActixSettings`.
|
||||||
|
- `BasicSettings::from_default_template()` is now infallible.
|
||||||
- Rename `AtError => Error`.
|
- Rename `AtError => Error`.
|
||||||
- Remove `AtResult` type alias.
|
- Remove `AtResult` type alias.
|
||||||
- Update `toml` dependency to `0.8`.
|
- Update `toml` dependency to `0.8`.
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "actix-settings"
|
name = "actix-settings"
|
||||||
version = "0.6.0"
|
version = "0.7.1"
|
||||||
authors = [
|
authors = [
|
||||||
"Joey Ezechiels <joey.ezechiels@gmail.com>",
|
"Joey Ezechiels <joey.ezechiels@gmail.com>",
|
||||||
"Rob Ede <robjtede@icloud.com>",
|
"Rob Ede <robjtede@icloud.com>",
|
||||||
|
@ -3,9 +3,9 @@
|
|||||||
> Easily manage Actix Web's settings from a TOML file and environment variables.
|
> Easily manage Actix Web's settings from a TOML file and environment variables.
|
||||||
|
|
||||||
[](https://crates.io/crates/actix-settings)
|
[](https://crates.io/crates/actix-settings)
|
||||||
[](https://docs.rs/actix-settings/0.6.0)
|
[](https://docs.rs/actix-settings/0.7.1)
|
||||||

|

|
||||||
[](https://deps.rs/crate/actix-settings/0.6.0)
|
[](https://deps.rs/crate/actix-settings/0.7.1)
|
||||||
|
|
||||||
## Documentation & Resources
|
## Documentation & Resources
|
||||||
|
|
||||||
|
@ -65,6 +65,7 @@
|
|||||||
#![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_auto_cfg))]
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
env, fmt,
|
env, fmt,
|
||||||
@ -154,14 +155,11 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Parse an instance of `Self` straight from the default TOML template.
|
/// Parse an instance of `Self` straight from the default TOML template.
|
||||||
// TODO: make infallible
|
pub fn from_default_template() -> Self {
|
||||||
// TODO: consider "template" rename
|
Self::from_template(Self::DEFAULT_TOML_TEMPLATE).unwrap()
|
||||||
pub fn from_default_template() -> AsResult<Self> {
|
|
||||||
Self::from_template(Self::DEFAULT_TOML_TEMPLATE)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse an instance of `Self` straight from the default TOML template.
|
/// Parse an instance of `Self` straight from the default TOML template.
|
||||||
// TODO: consider "template" rename
|
|
||||||
pub fn from_template(template: &str) -> AsResult<Self> {
|
pub fn from_template(template: &str) -> AsResult<Self> {
|
||||||
Ok(toml::from_str(template)?)
|
Ok(toml::from_str(template)?)
|
||||||
}
|
}
|
||||||
@ -196,7 +194,7 @@ where
|
|||||||
/// use actix_settings::{Settings, Mode};
|
/// use actix_settings::{Settings, Mode};
|
||||||
///
|
///
|
||||||
/// # fn inner() -> Result<(), actix_settings::Error> {
|
/// # fn inner() -> Result<(), actix_settings::Error> {
|
||||||
/// let mut settings = Settings::from_default_template()?;
|
/// let mut settings = Settings::from_default_template();
|
||||||
/// assert_eq!(settings.actix.mode, Mode::Development);
|
/// assert_eq!(settings.actix.mode, Mode::Development);
|
||||||
///
|
///
|
||||||
/// Settings::override_field(&mut settings.actix.mode, "production")?;
|
/// Settings::override_field(&mut settings.actix.mode, "production")?;
|
||||||
@ -221,7 +219,7 @@ where
|
|||||||
/// std::env::set_var("OVERRIDE__MODE", "production");
|
/// std::env::set_var("OVERRIDE__MODE", "production");
|
||||||
///
|
///
|
||||||
/// # fn inner() -> Result<(), actix_settings::Error> {
|
/// # fn inner() -> Result<(), actix_settings::Error> {
|
||||||
/// let mut settings = Settings::from_default_template()?;
|
/// let mut settings = Settings::from_default_template();
|
||||||
/// assert_eq!(settings.actix.mode, Mode::Development);
|
/// assert_eq!(settings.actix.mode, Mode::Development);
|
||||||
///
|
///
|
||||||
/// Settings::override_field_with_env_var(&mut settings.actix.mode, "OVERRIDE__MODE")?;
|
/// Settings::override_field_with_env_var(&mut settings.actix.mode, "OVERRIDE__MODE")?;
|
||||||
@ -242,17 +240,13 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Extension trait for applying parsed settings to the server object.
|
/// Extension trait for applying parsed settings to the server object.
|
||||||
pub trait ApplySettings {
|
pub trait ApplySettings<S> {
|
||||||
/// Apply a [`BasicSettings`] value to `self`.
|
/// Apply some settings object value to `self`.
|
||||||
///
|
|
||||||
/// [`BasicSettings`]: ./struct.BasicSettings.html
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn apply_settings<A>(self, settings: &BasicSettings<A>) -> Self
|
fn apply_settings(self, settings: &S) -> Self;
|
||||||
where
|
|
||||||
A: de::DeserializeOwned;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F, I, S, B> ApplySettings for HttpServer<F, I, S, B>
|
impl<F, I, S, B> ApplySettings<ActixSettings> for HttpServer<F, I, S, B>
|
||||||
where
|
where
|
||||||
F: Fn() -> I + Send + Clone + 'static,
|
F: Fn() -> I + Send + Clone + 'static,
|
||||||
I: IntoServiceFactory<S, Request>,
|
I: IntoServiceFactory<S, Request>,
|
||||||
@ -263,51 +257,48 @@ where
|
|||||||
S::Future: 'static,
|
S::Future: 'static,
|
||||||
B: MessageBody + 'static,
|
B: MessageBody + 'static,
|
||||||
{
|
{
|
||||||
fn apply_settings<A>(mut self, settings: &BasicSettings<A>) -> Self
|
fn apply_settings(mut self, settings: &ActixSettings) -> Self {
|
||||||
where
|
if settings.tls.enabled {
|
||||||
A: de::DeserializeOwned,
|
|
||||||
{
|
|
||||||
if settings.actix.tls.enabled {
|
|
||||||
// for Address { host, port } in &settings.actix.hosts {
|
// for Address { host, port } in &settings.actix.hosts {
|
||||||
// self = self.bind(format!("{}:{}", host, port))
|
// self = self.bind(format!("{}:{}", host, port))
|
||||||
// .unwrap(/*TODO*/);
|
// .unwrap(/*TODO*/);
|
||||||
// }
|
// }
|
||||||
todo!("[ApplySettings] TLS support has not been implemented yet.");
|
unimplemented!("[ApplySettings] TLS support has not been implemented yet.");
|
||||||
} else {
|
} else {
|
||||||
for Address { host, port } in &settings.actix.hosts {
|
for Address { host, port } in &settings.hosts {
|
||||||
self = self.bind(format!("{host}:{port}"))
|
self = self.bind(format!("{host}:{port}"))
|
||||||
.unwrap(/*TODO*/);
|
.unwrap(/*TODO*/);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self = match settings.actix.num_workers {
|
self = match settings.num_workers {
|
||||||
NumWorkers::Default => self,
|
NumWorkers::Default => self,
|
||||||
NumWorkers::Manual(n) => self.workers(n),
|
NumWorkers::Manual(n) => self.workers(n),
|
||||||
};
|
};
|
||||||
|
|
||||||
self = match settings.actix.backlog {
|
self = match settings.backlog {
|
||||||
Backlog::Default => self,
|
Backlog::Default => self,
|
||||||
Backlog::Manual(n) => self.backlog(n as u32),
|
Backlog::Manual(n) => self.backlog(n as u32),
|
||||||
};
|
};
|
||||||
|
|
||||||
self = match settings.actix.max_connections {
|
self = match settings.max_connections {
|
||||||
MaxConnections::Default => self,
|
MaxConnections::Default => self,
|
||||||
MaxConnections::Manual(n) => self.max_connections(n),
|
MaxConnections::Manual(n) => self.max_connections(n),
|
||||||
};
|
};
|
||||||
|
|
||||||
self = match settings.actix.max_connection_rate {
|
self = match settings.max_connection_rate {
|
||||||
MaxConnectionRate::Default => self,
|
MaxConnectionRate::Default => self,
|
||||||
MaxConnectionRate::Manual(n) => self.max_connection_rate(n),
|
MaxConnectionRate::Manual(n) => self.max_connection_rate(n),
|
||||||
};
|
};
|
||||||
|
|
||||||
self = match settings.actix.keep_alive {
|
self = match settings.keep_alive {
|
||||||
KeepAlive::Default => self,
|
KeepAlive::Default => self,
|
||||||
KeepAlive::Disabled => self.keep_alive(ActixKeepAlive::Disabled),
|
KeepAlive::Disabled => self.keep_alive(ActixKeepAlive::Disabled),
|
||||||
KeepAlive::Os => self.keep_alive(ActixKeepAlive::Os),
|
KeepAlive::Os => self.keep_alive(ActixKeepAlive::Os),
|
||||||
KeepAlive::Seconds(n) => self.keep_alive(Duration::from_secs(n as u64)),
|
KeepAlive::Seconds(n) => self.keep_alive(Duration::from_secs(n as u64)),
|
||||||
};
|
};
|
||||||
|
|
||||||
self = match settings.actix.client_timeout {
|
self = match settings.client_timeout {
|
||||||
Timeout::Default => self,
|
Timeout::Default => self,
|
||||||
Timeout::Milliseconds(n) => {
|
Timeout::Milliseconds(n) => {
|
||||||
self.client_request_timeout(Duration::from_millis(n as u64))
|
self.client_request_timeout(Duration::from_millis(n as u64))
|
||||||
@ -315,7 +306,7 @@ where
|
|||||||
Timeout::Seconds(n) => self.client_request_timeout(Duration::from_secs(n as u64)),
|
Timeout::Seconds(n) => self.client_request_timeout(Duration::from_secs(n as u64)),
|
||||||
};
|
};
|
||||||
|
|
||||||
self = match settings.actix.client_shutdown {
|
self = match settings.client_shutdown {
|
||||||
Timeout::Default => self,
|
Timeout::Default => self,
|
||||||
Timeout::Milliseconds(n) => {
|
Timeout::Milliseconds(n) => {
|
||||||
self.client_disconnect_timeout(Duration::from_millis(n as u64))
|
self.client_disconnect_timeout(Duration::from_millis(n as u64))
|
||||||
@ -323,7 +314,7 @@ where
|
|||||||
Timeout::Seconds(n) => self.client_disconnect_timeout(Duration::from_secs(n as u64)),
|
Timeout::Seconds(n) => self.client_disconnect_timeout(Duration::from_secs(n as u64)),
|
||||||
};
|
};
|
||||||
|
|
||||||
self = match settings.actix.shutdown_timeout {
|
self = match settings.shutdown_timeout {
|
||||||
Timeout::Default => self,
|
Timeout::Default => self,
|
||||||
Timeout::Milliseconds(_) => self.shutdown_timeout(1),
|
Timeout::Milliseconds(_) => self.shutdown_timeout(1),
|
||||||
Timeout::Seconds(n) => self.shutdown_timeout(n as u64),
|
Timeout::Seconds(n) => self.shutdown_timeout(n as u64),
|
||||||
@ -333,6 +324,23 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<F, I, S, B, A> ApplySettings<BasicSettings<A>> for HttpServer<F, I, S, B>
|
||||||
|
where
|
||||||
|
F: Fn() -> I + Send + Clone + 'static,
|
||||||
|
I: IntoServiceFactory<S, Request>,
|
||||||
|
S: ServiceFactory<Request, Config = AppConfig> + 'static,
|
||||||
|
S::Error: Into<WebError> + 'static,
|
||||||
|
S::InitError: fmt::Debug,
|
||||||
|
S::Response: Into<Response<B>> + 'static,
|
||||||
|
S::Future: 'static,
|
||||||
|
B: MessageBody + 'static,
|
||||||
|
A: de::DeserializeOwned,
|
||||||
|
{
|
||||||
|
fn apply_settings(self, settings: &BasicSettings<A>) -> Self {
|
||||||
|
self.apply_settings(&settings.actix)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use actix_web::App;
|
use actix_web::App;
|
||||||
@ -347,7 +355,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn override_field_hosts() {
|
fn override_field_hosts() {
|
||||||
let mut settings = Settings::from_default_template().unwrap();
|
let mut settings = Settings::from_default_template();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
settings.actix.hosts,
|
settings.actix.hosts,
|
||||||
@ -383,7 +391,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn override_field_with_env_var_hosts() {
|
fn override_field_with_env_var_hosts() {
|
||||||
let mut settings = Settings::from_default_template().unwrap();
|
let mut settings = Settings::from_default_template();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
settings.actix.hosts,
|
settings.actix.hosts,
|
||||||
@ -421,7 +429,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn override_field_mode() {
|
fn override_field_mode() {
|
||||||
let mut settings = Settings::from_default_template().unwrap();
|
let mut settings = Settings::from_default_template();
|
||||||
assert_eq!(settings.actix.mode, Mode::Development);
|
assert_eq!(settings.actix.mode, Mode::Development);
|
||||||
Settings::override_field(&mut settings.actix.mode, "production").unwrap();
|
Settings::override_field(&mut settings.actix.mode, "production").unwrap();
|
||||||
assert_eq!(settings.actix.mode, Mode::Production);
|
assert_eq!(settings.actix.mode, Mode::Production);
|
||||||
@ -429,7 +437,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn override_field_with_env_var_mode() {
|
fn override_field_with_env_var_mode() {
|
||||||
let mut settings = Settings::from_default_template().unwrap();
|
let mut settings = Settings::from_default_template();
|
||||||
assert_eq!(settings.actix.mode, Mode::Development);
|
assert_eq!(settings.actix.mode, Mode::Development);
|
||||||
std::env::set_var("OVERRIDE__MODE", "production");
|
std::env::set_var("OVERRIDE__MODE", "production");
|
||||||
Settings::override_field_with_env_var(&mut settings.actix.mode, "OVERRIDE__MODE").unwrap();
|
Settings::override_field_with_env_var(&mut settings.actix.mode, "OVERRIDE__MODE").unwrap();
|
||||||
@ -438,7 +446,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn override_field_enable_compression() {
|
fn override_field_enable_compression() {
|
||||||
let mut settings = Settings::from_default_template().unwrap();
|
let mut settings = Settings::from_default_template();
|
||||||
assert!(settings.actix.enable_compression);
|
assert!(settings.actix.enable_compression);
|
||||||
Settings::override_field(&mut settings.actix.enable_compression, "false").unwrap();
|
Settings::override_field(&mut settings.actix.enable_compression, "false").unwrap();
|
||||||
assert!(!settings.actix.enable_compression);
|
assert!(!settings.actix.enable_compression);
|
||||||
@ -446,7 +454,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn override_field_with_env_var_enable_compression() {
|
fn override_field_with_env_var_enable_compression() {
|
||||||
let mut settings = Settings::from_default_template().unwrap();
|
let mut settings = Settings::from_default_template();
|
||||||
assert!(settings.actix.enable_compression);
|
assert!(settings.actix.enable_compression);
|
||||||
std::env::set_var("OVERRIDE__ENABLE_COMPRESSION", "false");
|
std::env::set_var("OVERRIDE__ENABLE_COMPRESSION", "false");
|
||||||
Settings::override_field_with_env_var(
|
Settings::override_field_with_env_var(
|
||||||
@ -459,7 +467,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn override_field_enable_log() {
|
fn override_field_enable_log() {
|
||||||
let mut settings = Settings::from_default_template().unwrap();
|
let mut settings = Settings::from_default_template();
|
||||||
assert!(settings.actix.enable_log);
|
assert!(settings.actix.enable_log);
|
||||||
Settings::override_field(&mut settings.actix.enable_log, "false").unwrap();
|
Settings::override_field(&mut settings.actix.enable_log, "false").unwrap();
|
||||||
assert!(!settings.actix.enable_log);
|
assert!(!settings.actix.enable_log);
|
||||||
@ -467,7 +475,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn override_field_with_env_var_enable_log() {
|
fn override_field_with_env_var_enable_log() {
|
||||||
let mut settings = Settings::from_default_template().unwrap();
|
let mut settings = Settings::from_default_template();
|
||||||
assert!(settings.actix.enable_log);
|
assert!(settings.actix.enable_log);
|
||||||
std::env::set_var("OVERRIDE__ENABLE_LOG", "false");
|
std::env::set_var("OVERRIDE__ENABLE_LOG", "false");
|
||||||
Settings::override_field_with_env_var(
|
Settings::override_field_with_env_var(
|
||||||
@ -480,7 +488,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn override_field_num_workers() {
|
fn override_field_num_workers() {
|
||||||
let mut settings = Settings::from_default_template().unwrap();
|
let mut settings = Settings::from_default_template();
|
||||||
assert_eq!(settings.actix.num_workers, NumWorkers::Default);
|
assert_eq!(settings.actix.num_workers, NumWorkers::Default);
|
||||||
Settings::override_field(&mut settings.actix.num_workers, "42").unwrap();
|
Settings::override_field(&mut settings.actix.num_workers, "42").unwrap();
|
||||||
assert_eq!(settings.actix.num_workers, NumWorkers::Manual(42));
|
assert_eq!(settings.actix.num_workers, NumWorkers::Manual(42));
|
||||||
@ -488,7 +496,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn override_field_with_env_var_num_workers() {
|
fn override_field_with_env_var_num_workers() {
|
||||||
let mut settings = Settings::from_default_template().unwrap();
|
let mut settings = Settings::from_default_template();
|
||||||
assert_eq!(settings.actix.num_workers, NumWorkers::Default);
|
assert_eq!(settings.actix.num_workers, NumWorkers::Default);
|
||||||
std::env::set_var("OVERRIDE__NUM_WORKERS", "42");
|
std::env::set_var("OVERRIDE__NUM_WORKERS", "42");
|
||||||
Settings::override_field_with_env_var(
|
Settings::override_field_with_env_var(
|
||||||
@ -501,7 +509,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn override_field_backlog() {
|
fn override_field_backlog() {
|
||||||
let mut settings = Settings::from_default_template().unwrap();
|
let mut settings = Settings::from_default_template();
|
||||||
assert_eq!(settings.actix.backlog, Backlog::Default);
|
assert_eq!(settings.actix.backlog, Backlog::Default);
|
||||||
Settings::override_field(&mut settings.actix.backlog, "42").unwrap();
|
Settings::override_field(&mut settings.actix.backlog, "42").unwrap();
|
||||||
assert_eq!(settings.actix.backlog, Backlog::Manual(42));
|
assert_eq!(settings.actix.backlog, Backlog::Manual(42));
|
||||||
@ -509,7 +517,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn override_field_with_env_var_backlog() {
|
fn override_field_with_env_var_backlog() {
|
||||||
let mut settings = Settings::from_default_template().unwrap();
|
let mut settings = Settings::from_default_template();
|
||||||
assert_eq!(settings.actix.backlog, Backlog::Default);
|
assert_eq!(settings.actix.backlog, Backlog::Default);
|
||||||
std::env::set_var("OVERRIDE__BACKLOG", "42");
|
std::env::set_var("OVERRIDE__BACKLOG", "42");
|
||||||
Settings::override_field_with_env_var(&mut settings.actix.backlog, "OVERRIDE__BACKLOG")
|
Settings::override_field_with_env_var(&mut settings.actix.backlog, "OVERRIDE__BACKLOG")
|
||||||
@ -519,7 +527,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn override_field_max_connections() {
|
fn override_field_max_connections() {
|
||||||
let mut settings = Settings::from_default_template().unwrap();
|
let mut settings = Settings::from_default_template();
|
||||||
assert_eq!(settings.actix.max_connections, MaxConnections::Default);
|
assert_eq!(settings.actix.max_connections, MaxConnections::Default);
|
||||||
Settings::override_field(&mut settings.actix.max_connections, "42").unwrap();
|
Settings::override_field(&mut settings.actix.max_connections, "42").unwrap();
|
||||||
assert_eq!(settings.actix.max_connections, MaxConnections::Manual(42));
|
assert_eq!(settings.actix.max_connections, MaxConnections::Manual(42));
|
||||||
@ -527,7 +535,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn override_field_with_env_var_max_connections() {
|
fn override_field_with_env_var_max_connections() {
|
||||||
let mut settings = Settings::from_default_template().unwrap();
|
let mut settings = Settings::from_default_template();
|
||||||
assert_eq!(settings.actix.max_connections, MaxConnections::Default);
|
assert_eq!(settings.actix.max_connections, MaxConnections::Default);
|
||||||
std::env::set_var("OVERRIDE__MAX_CONNECTIONS", "42");
|
std::env::set_var("OVERRIDE__MAX_CONNECTIONS", "42");
|
||||||
Settings::override_field_with_env_var(
|
Settings::override_field_with_env_var(
|
||||||
@ -540,7 +548,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn override_field_max_connection_rate() {
|
fn override_field_max_connection_rate() {
|
||||||
let mut settings = Settings::from_default_template().unwrap();
|
let mut settings = Settings::from_default_template();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
settings.actix.max_connection_rate,
|
settings.actix.max_connection_rate,
|
||||||
MaxConnectionRate::Default
|
MaxConnectionRate::Default
|
||||||
@ -554,7 +562,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn override_field_with_env_var_max_connection_rate() {
|
fn override_field_with_env_var_max_connection_rate() {
|
||||||
let mut settings = Settings::from_default_template().unwrap();
|
let mut settings = Settings::from_default_template();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
settings.actix.max_connection_rate,
|
settings.actix.max_connection_rate,
|
||||||
MaxConnectionRate::Default
|
MaxConnectionRate::Default
|
||||||
@ -573,7 +581,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn override_field_keep_alive() {
|
fn override_field_keep_alive() {
|
||||||
let mut settings = Settings::from_default_template().unwrap();
|
let mut settings = Settings::from_default_template();
|
||||||
assert_eq!(settings.actix.keep_alive, KeepAlive::Default);
|
assert_eq!(settings.actix.keep_alive, KeepAlive::Default);
|
||||||
Settings::override_field(&mut settings.actix.keep_alive, "42 seconds").unwrap();
|
Settings::override_field(&mut settings.actix.keep_alive, "42 seconds").unwrap();
|
||||||
assert_eq!(settings.actix.keep_alive, KeepAlive::Seconds(42));
|
assert_eq!(settings.actix.keep_alive, KeepAlive::Seconds(42));
|
||||||
@ -581,7 +589,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn override_field_with_env_var_keep_alive() {
|
fn override_field_with_env_var_keep_alive() {
|
||||||
let mut settings = Settings::from_default_template().unwrap();
|
let mut settings = Settings::from_default_template();
|
||||||
assert_eq!(settings.actix.keep_alive, KeepAlive::Default);
|
assert_eq!(settings.actix.keep_alive, KeepAlive::Default);
|
||||||
std::env::set_var("OVERRIDE__KEEP_ALIVE", "42 seconds");
|
std::env::set_var("OVERRIDE__KEEP_ALIVE", "42 seconds");
|
||||||
Settings::override_field_with_env_var(
|
Settings::override_field_with_env_var(
|
||||||
@ -594,7 +602,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn override_field_client_timeout() {
|
fn override_field_client_timeout() {
|
||||||
let mut settings = Settings::from_default_template().unwrap();
|
let mut settings = Settings::from_default_template();
|
||||||
assert_eq!(settings.actix.client_timeout, Timeout::Default);
|
assert_eq!(settings.actix.client_timeout, Timeout::Default);
|
||||||
Settings::override_field(&mut settings.actix.client_timeout, "42 seconds").unwrap();
|
Settings::override_field(&mut settings.actix.client_timeout, "42 seconds").unwrap();
|
||||||
assert_eq!(settings.actix.client_timeout, Timeout::Seconds(42));
|
assert_eq!(settings.actix.client_timeout, Timeout::Seconds(42));
|
||||||
@ -602,7 +610,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn override_field_with_env_var_client_timeout() {
|
fn override_field_with_env_var_client_timeout() {
|
||||||
let mut settings = Settings::from_default_template().unwrap();
|
let mut settings = Settings::from_default_template();
|
||||||
assert_eq!(settings.actix.client_timeout, Timeout::Default);
|
assert_eq!(settings.actix.client_timeout, Timeout::Default);
|
||||||
std::env::set_var("OVERRIDE__CLIENT_TIMEOUT", "42 seconds");
|
std::env::set_var("OVERRIDE__CLIENT_TIMEOUT", "42 seconds");
|
||||||
Settings::override_field_with_env_var(
|
Settings::override_field_with_env_var(
|
||||||
@ -615,7 +623,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn override_field_client_shutdown() {
|
fn override_field_client_shutdown() {
|
||||||
let mut settings = Settings::from_default_template().unwrap();
|
let mut settings = Settings::from_default_template();
|
||||||
assert_eq!(settings.actix.client_shutdown, Timeout::Default);
|
assert_eq!(settings.actix.client_shutdown, Timeout::Default);
|
||||||
Settings::override_field(&mut settings.actix.client_shutdown, "42 seconds").unwrap();
|
Settings::override_field(&mut settings.actix.client_shutdown, "42 seconds").unwrap();
|
||||||
assert_eq!(settings.actix.client_shutdown, Timeout::Seconds(42));
|
assert_eq!(settings.actix.client_shutdown, Timeout::Seconds(42));
|
||||||
@ -623,7 +631,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn override_field_with_env_var_client_shutdown() {
|
fn override_field_with_env_var_client_shutdown() {
|
||||||
let mut settings = Settings::from_default_template().unwrap();
|
let mut settings = Settings::from_default_template();
|
||||||
assert_eq!(settings.actix.client_shutdown, Timeout::Default);
|
assert_eq!(settings.actix.client_shutdown, Timeout::Default);
|
||||||
std::env::set_var("OVERRIDE__CLIENT_SHUTDOWN", "42 seconds");
|
std::env::set_var("OVERRIDE__CLIENT_SHUTDOWN", "42 seconds");
|
||||||
Settings::override_field_with_env_var(
|
Settings::override_field_with_env_var(
|
||||||
@ -636,7 +644,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn override_field_shutdown_timeout() {
|
fn override_field_shutdown_timeout() {
|
||||||
let mut settings = Settings::from_default_template().unwrap();
|
let mut settings = Settings::from_default_template();
|
||||||
assert_eq!(settings.actix.shutdown_timeout, Timeout::Default);
|
assert_eq!(settings.actix.shutdown_timeout, Timeout::Default);
|
||||||
Settings::override_field(&mut settings.actix.shutdown_timeout, "42 seconds").unwrap();
|
Settings::override_field(&mut settings.actix.shutdown_timeout, "42 seconds").unwrap();
|
||||||
assert_eq!(settings.actix.shutdown_timeout, Timeout::Seconds(42));
|
assert_eq!(settings.actix.shutdown_timeout, Timeout::Seconds(42));
|
||||||
@ -644,7 +652,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn override_field_with_env_var_shutdown_timeout() {
|
fn override_field_with_env_var_shutdown_timeout() {
|
||||||
let mut settings = Settings::from_default_template().unwrap();
|
let mut settings = Settings::from_default_template();
|
||||||
assert_eq!(settings.actix.shutdown_timeout, Timeout::Default);
|
assert_eq!(settings.actix.shutdown_timeout, Timeout::Default);
|
||||||
std::env::set_var("OVERRIDE__SHUTDOWN_TIMEOUT", "42 seconds");
|
std::env::set_var("OVERRIDE__SHUTDOWN_TIMEOUT", "42 seconds");
|
||||||
Settings::override_field_with_env_var(
|
Settings::override_field_with_env_var(
|
||||||
@ -657,7 +665,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn override_field_tls_enabled() {
|
fn override_field_tls_enabled() {
|
||||||
let mut settings = Settings::from_default_template().unwrap();
|
let mut settings = Settings::from_default_template();
|
||||||
assert!(!settings.actix.tls.enabled);
|
assert!(!settings.actix.tls.enabled);
|
||||||
Settings::override_field(&mut settings.actix.tls.enabled, "true").unwrap();
|
Settings::override_field(&mut settings.actix.tls.enabled, "true").unwrap();
|
||||||
assert!(settings.actix.tls.enabled);
|
assert!(settings.actix.tls.enabled);
|
||||||
@ -665,7 +673,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn override_field_with_env_var_tls_enabled() {
|
fn override_field_with_env_var_tls_enabled() {
|
||||||
let mut settings = Settings::from_default_template().unwrap();
|
let mut settings = Settings::from_default_template();
|
||||||
assert!(!settings.actix.tls.enabled);
|
assert!(!settings.actix.tls.enabled);
|
||||||
std::env::set_var("OVERRIDE__TLS_ENABLED", "true");
|
std::env::set_var("OVERRIDE__TLS_ENABLED", "true");
|
||||||
Settings::override_field_with_env_var(
|
Settings::override_field_with_env_var(
|
||||||
@ -678,7 +686,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn override_field_tls_certificate() {
|
fn override_field_tls_certificate() {
|
||||||
let mut settings = Settings::from_default_template().unwrap();
|
let mut settings = Settings::from_default_template();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
settings.actix.tls.certificate,
|
settings.actix.tls.certificate,
|
||||||
Path::new("path/to/cert/cert.pem")
|
Path::new("path/to/cert/cert.pem")
|
||||||
@ -696,7 +704,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn override_field_with_env_var_tls_certificate() {
|
fn override_field_with_env_var_tls_certificate() {
|
||||||
let mut settings = Settings::from_default_template().unwrap();
|
let mut settings = Settings::from_default_template();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
settings.actix.tls.certificate,
|
settings.actix.tls.certificate,
|
||||||
Path::new("path/to/cert/cert.pem")
|
Path::new("path/to/cert/cert.pem")
|
||||||
@ -718,7 +726,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn override_field_tls_private_key() {
|
fn override_field_tls_private_key() {
|
||||||
let mut settings = Settings::from_default_template().unwrap();
|
let mut settings = Settings::from_default_template();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
settings.actix.tls.private_key,
|
settings.actix.tls.private_key,
|
||||||
Path::new("path/to/cert/key.pem")
|
Path::new("path/to/cert/key.pem")
|
||||||
@ -736,7 +744,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn override_field_with_env_var_tls_private_key() {
|
fn override_field_with_env_var_tls_private_key() {
|
||||||
let mut settings = Settings::from_default_template().unwrap();
|
let mut settings = Settings::from_default_template();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
settings.actix.tls.private_key,
|
settings.actix.tls.private_key,
|
||||||
Path::new("path/to/cert/key.pem")
|
Path::new("path/to/cert/key.pem")
|
||||||
|
@ -26,7 +26,7 @@ pub struct ActixSettings {
|
|||||||
/// Marker of intended deployment environment.
|
/// Marker of intended deployment environment.
|
||||||
pub mode: Mode,
|
pub mode: Mode,
|
||||||
|
|
||||||
/// True if the [`Compress`](actix_web::middleware::Compress) middleware should be enabled.
|
/// True if the `Compress` middleware should be enabled.
|
||||||
pub enable_compression: bool,
|
pub enable_compression: bool,
|
||||||
|
|
||||||
/// True if the [`Logger`](actix_web::middleware::Logger) middleware should be enabled.
|
/// True if the [`Logger`](actix_web::middleware::Logger) middleware should be enabled.
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "actix-web-httpauth"
|
name = "actix-web-httpauth"
|
||||||
version = "0.8.1"
|
version = "0.8.1"
|
||||||
|
description = "HTTP authentication schemes for Actix Web"
|
||||||
|
categories = ["web-programming"]
|
||||||
|
keywords = ["http", "web", "framework", "authentication", "security"]
|
||||||
authors = [
|
authors = [
|
||||||
"svartalf <self@svartalf.info>",
|
"svartalf <self@svartalf.info>",
|
||||||
"Yuki Okushi <huyuumi.dev@gmail.com>",
|
"Yuki Okushi <huyuumi.dev@gmail.com>",
|
||||||
]
|
]
|
||||||
description = "HTTP authentication schemes for Actix Web"
|
|
||||||
keywords = ["http", "web", "framework", "authentication", "security"]
|
|
||||||
homepage = "https://actix.rs"
|
homepage = "https://actix.rs"
|
||||||
repository = "https://github.com/actix/actix-extras.git"
|
repository = "https://github.com/actix/actix-extras"
|
||||||
categories = ["web-programming::http-server"]
|
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
rust-version.workspace = true
|
rust-version.workspace = true
|
||||||
|
@ -2,7 +2,7 @@ use super::AuthenticationError;
|
|||||||
use crate::headers::www_authenticate::Challenge;
|
use crate::headers::www_authenticate::Challenge;
|
||||||
|
|
||||||
/// Trait implemented for types that provides configuration for the authentication
|
/// Trait implemented for types that provides configuration for the authentication
|
||||||
/// [extractors](super::AuthExtractor).
|
/// [extractors](crate::extractors).
|
||||||
pub trait AuthExtractorConfig {
|
pub trait AuthExtractorConfig {
|
||||||
/// Associated challenge type.
|
/// Associated challenge type.
|
||||||
type Inner: Challenge;
|
type Inner: Challenge;
|
||||||
|
@ -9,9 +9,10 @@ use crate::headers::authorization::{errors::ParseError, scheme::Scheme};
|
|||||||
|
|
||||||
/// Credentials for `Bearer` authentication scheme, defined in [RFC 6750].
|
/// Credentials for `Bearer` authentication scheme, defined in [RFC 6750].
|
||||||
///
|
///
|
||||||
/// Should be used in combination with [`Authorization`](super::Authorization) header.
|
/// Should be used in combination with [`Authorization`] header.
|
||||||
///
|
///
|
||||||
/// [RFC 6750]: https://tools.ietf.org/html/rfc6750
|
/// [RFC 6750]: https://tools.ietf.org/html/rfc6750
|
||||||
|
/// [`Authorization`]: crate::headers::authorization::Authorization
|
||||||
#[derive(Clone, Eq, Ord, PartialEq, PartialOrd)]
|
#[derive(Clone, Eq, Ord, PartialEq, PartialOrd)]
|
||||||
pub struct Bearer {
|
pub struct Bearer {
|
||||||
token: Cow<'static, str>,
|
token: Cow<'static, str>,
|
||||||
|
@ -17,6 +17,9 @@
|
|||||||
#![forbid(unsafe_code)]
|
#![forbid(unsafe_code)]
|
||||||
#![deny(rust_2018_idioms, nonstandard_style)]
|
#![deny(rust_2018_idioms, nonstandard_style)]
|
||||||
#![warn(future_incompatible, missing_docs)]
|
#![warn(future_incompatible, missing_docs)]
|
||||||
|
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
||||||
|
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
||||||
|
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||||
|
|
||||||
pub mod extractors;
|
pub mod extractors;
|
||||||
pub mod headers;
|
pub mod headers;
|
||||||
|
10
actix-ws/CHANGELOG.md
Normal file
10
actix-ws/CHANGELOG.md
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
## Unreleased
|
||||||
|
|
||||||
|
- Remove type parameters from `Session::{text, binary}()` methods, replacing with equivalent `impl Trait` parameters.
|
||||||
|
- `Session::text()` now receives an `impl Into<ByteString>`, making broadcasting text messages more efficient.
|
||||||
|
|
||||||
|
## 0.2.5
|
||||||
|
|
||||||
|
- Adopted into @actix org from <https://git.asonix.dog/asonix/actix-actorless-websockets>.
|
31
actix-ws/Cargo.toml
Normal file
31
actix-ws/Cargo.toml
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
[package]
|
||||||
|
name = "actix-ws"
|
||||||
|
version = "0.2.0"
|
||||||
|
description = "WebSockets for Actix Web, without actors"
|
||||||
|
categories = ["web-programming::websocket"]
|
||||||
|
keywords = ["actix", "web", "websocket", "websockets", "http"]
|
||||||
|
authors = [
|
||||||
|
"asonix <asonix@asonix.dog>",
|
||||||
|
"Rob Ede <robjtede@icloud.com>",
|
||||||
|
]
|
||||||
|
repository = "https://github.com/actix/actix-extras"
|
||||||
|
license.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
rust-version.workspace = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
actix-codec = "0.5"
|
||||||
|
actix-http = { version = "3", default-features = false, features = ["ws"] }
|
||||||
|
actix-web = { version = "4", default-features = false }
|
||||||
|
bytestring = "1"
|
||||||
|
futures-core = "0.3.17"
|
||||||
|
tokio = { version = "1", features = ["sync"] }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
actix-rt = "2.6"
|
||||||
|
actix-web = "4.0.1"
|
||||||
|
anyhow = "1.0"
|
||||||
|
futures = "0.3"
|
||||||
|
log = "0.4"
|
||||||
|
pretty_env_logger = "0.5"
|
||||||
|
tokio = { version = "1", features = ["sync"] }
|
1
actix-ws/LICENSE-APACHE
Symbolic link
1
actix-ws/LICENSE-APACHE
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../LICENSE-APACHE
|
1
actix-ws/LICENSE-MIT
Symbolic link
1
actix-ws/LICENSE-MIT
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../LICENSE-MIT
|
74
actix-ws/README.md
Normal file
74
actix-ws/README.md
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
# Actix WS (Next Gen)
|
||||||
|
|
||||||
|
> WebSockets for Actix Web, without actors.
|
||||||
|
|
||||||
|
[](https://crates.io/crates/actix-ws)
|
||||||
|
[](https://docs.rs/actix-ws/0.2.0)
|
||||||
|

|
||||||
|
[](https://deps.rs/crate/actix-ws/0.2.0)
|
||||||
|
|
||||||
|
## Documentation & Resources
|
||||||
|
|
||||||
|
- [API Documentation](https://docs.rs/actix-ws)
|
||||||
|
- [Example Projects](https://github.com/actix/examples/tree/master/websockets)
|
||||||
|
- Minimum Supported Rust Version (MSRV): 1.68
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```toml
|
||||||
|
# Cargo.toml
|
||||||
|
anyhow = "1"
|
||||||
|
actix-web = "4"
|
||||||
|
actix-ws-ng = "0.3"
|
||||||
|
```
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// main.rs
|
||||||
|
use actix_web::{middleware::Logger, web, App, Error, HttpRequest, HttpResponse, HttpServer};
|
||||||
|
use actix_ws::Message;
|
||||||
|
|
||||||
|
async fn ws(req: HttpRequest, body: web::Payload) -> Result<HttpResponse, Error> {
|
||||||
|
let (response, mut session, mut msg_stream) = actix_ws::handle(&req, body)?;
|
||||||
|
|
||||||
|
actix_rt::spawn(async move {
|
||||||
|
while let Some(Ok(msg)) = msg_stream.next().await {
|
||||||
|
match msg {
|
||||||
|
Message::Ping(bytes) => {
|
||||||
|
if session.pong(&bytes).await.is_err() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Message::Text(s) => println!("Got text, {}", s),
|
||||||
|
_ => break,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = session.close(None).await;
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[actix_web::main]
|
||||||
|
async fn main() -> Result<(), anyhow::Error> {
|
||||||
|
HttpServer::new(move || {
|
||||||
|
App::new()
|
||||||
|
.wrap(Logger::default())
|
||||||
|
.route("/ws", web::get().to(ws))
|
||||||
|
})
|
||||||
|
.bind("127.0.0.1:8080")?
|
||||||
|
.run()
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This project is licensed under either of
|
||||||
|
|
||||||
|
- Apache License, Version 2.0, (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
|
||||||
|
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
at your option.
|
208
actix-ws/examples/chat.rs
Normal file
208
actix-ws/examples/chat.rs
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
use std::{
|
||||||
|
sync::Arc,
|
||||||
|
time::{Duration, Instant},
|
||||||
|
};
|
||||||
|
|
||||||
|
use actix_web::{middleware::Logger, web, App, HttpRequest, HttpResponse, HttpServer};
|
||||||
|
use actix_ws::{Message, Session};
|
||||||
|
use futures::stream::{FuturesUnordered, StreamExt as _};
|
||||||
|
use log::info;
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct Chat {
|
||||||
|
inner: Arc<Mutex<ChatInner>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ChatInner {
|
||||||
|
sessions: Vec<Session>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Chat {
|
||||||
|
fn new() -> Self {
|
||||||
|
Chat {
|
||||||
|
inner: Arc::new(Mutex::new(ChatInner {
|
||||||
|
sessions: Vec::new(),
|
||||||
|
})),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn insert(&self, session: Session) {
|
||||||
|
self.inner.lock().await.sessions.push(session);
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn send(&self, msg: String) {
|
||||||
|
let mut inner = self.inner.lock().await;
|
||||||
|
let mut unordered = FuturesUnordered::new();
|
||||||
|
|
||||||
|
for mut session in inner.sessions.drain(..) {
|
||||||
|
let msg = msg.clone();
|
||||||
|
unordered.push(async move {
|
||||||
|
let res = session.text(msg).await;
|
||||||
|
res.map(|_| session).map_err(|_| info!("Dropping session"))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
while let Some(res) = unordered.next().await {
|
||||||
|
if let Ok(session) = res {
|
||||||
|
inner.sessions.push(session);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn ws(
|
||||||
|
req: HttpRequest,
|
||||||
|
body: web::Payload,
|
||||||
|
chat: web::Data<Chat>,
|
||||||
|
) -> Result<HttpResponse, actix_web::Error> {
|
||||||
|
let (response, mut session, mut stream) = actix_ws::handle(&req, body)?;
|
||||||
|
|
||||||
|
chat.insert(session.clone()).await;
|
||||||
|
info!("Inserted session");
|
||||||
|
|
||||||
|
let alive = Arc::new(Mutex::new(Instant::now()));
|
||||||
|
|
||||||
|
let mut session2 = session.clone();
|
||||||
|
let alive2 = alive.clone();
|
||||||
|
actix_rt::spawn(async move {
|
||||||
|
let mut interval = actix_rt::time::interval(Duration::from_secs(5));
|
||||||
|
loop {
|
||||||
|
interval.tick().await;
|
||||||
|
if session2.ping(b"").await.is_err() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if Instant::now().duration_since(*alive2.lock().await) > Duration::from_secs(10) {
|
||||||
|
let _ = session2.close(None).await;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
actix_rt::spawn(async move {
|
||||||
|
while let Some(Ok(msg)) = stream.next().await {
|
||||||
|
match msg {
|
||||||
|
Message::Ping(bytes) => {
|
||||||
|
if session.pong(&bytes).await.is_err() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Message::Text(s) => {
|
||||||
|
info!("Relaying text, {}", s);
|
||||||
|
let s: &str = s.as_ref();
|
||||||
|
chat.send(s.into()).await;
|
||||||
|
}
|
||||||
|
Message::Close(reason) => {
|
||||||
|
let _ = session.close(reason).await;
|
||||||
|
info!("Got close, bailing");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Message::Continuation(_) => {
|
||||||
|
let _ = session.close(None).await;
|
||||||
|
info!("Got continuation, bailing");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Message::Pong(_) => {
|
||||||
|
*alive.lock().await = Instant::now();
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
let _ = session.close(None).await;
|
||||||
|
});
|
||||||
|
info!("Spawned");
|
||||||
|
|
||||||
|
Ok(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn index() -> HttpResponse {
|
||||||
|
let s = r#"
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>Chat</title>
|
||||||
|
<script>
|
||||||
|
function onLoad() {
|
||||||
|
console.log("BOOTING");
|
||||||
|
const socket = new WebSocket("ws://localhost:8080/ws");
|
||||||
|
const input = document.getElementById("chat-input");
|
||||||
|
const logs = document.getElementById("chat-logs");
|
||||||
|
|
||||||
|
if (!input || !logs) {
|
||||||
|
alert("Couldn't find required elements");
|
||||||
|
console.err("Couldn't find required elements");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
input.addEventListener("keyup", event => {
|
||||||
|
if (event.isComposing) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (event.key != "Enter") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
socket.send(input.value);
|
||||||
|
input.value = "";
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
socket.onmessage = event => {
|
||||||
|
const newNode = document.createElement("li");
|
||||||
|
newNode.textContent = event.data;
|
||||||
|
|
||||||
|
let firstChild = null;
|
||||||
|
for (const n of logs.childNodes.values()) {
|
||||||
|
if (n.nodeType == 1) {
|
||||||
|
firstChild = n;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (firstChild) {
|
||||||
|
logs.insertBefore(newNode, firstChild);
|
||||||
|
} else {
|
||||||
|
logs.appendChild(newNode);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener("beforeunload", () => { socket.close() });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (document.readyState === "complete") {
|
||||||
|
onLoad();
|
||||||
|
} else {
|
||||||
|
document.addEventListener("DOMContentLoaded", onLoad, false);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<input id="chat-input" type="test" />
|
||||||
|
<ul id="chat-logs">
|
||||||
|
</ul>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"#;
|
||||||
|
|
||||||
|
HttpResponse::Ok().content_type("text/html").body(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[actix_rt::main]
|
||||||
|
async fn main() -> Result<(), anyhow::Error> {
|
||||||
|
std::env::set_var("RUST_LOG", "info");
|
||||||
|
pretty_env_logger::init();
|
||||||
|
let chat = Chat::new();
|
||||||
|
|
||||||
|
HttpServer::new(move || {
|
||||||
|
App::new()
|
||||||
|
.wrap(Logger::default())
|
||||||
|
.app_data(web::Data::new(chat.clone()))
|
||||||
|
.route("/", web::get().to(index))
|
||||||
|
.route("/ws", web::get().to(ws))
|
||||||
|
})
|
||||||
|
.bind("127.0.0.1:8080")?
|
||||||
|
.run()
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
183
actix-ws/src/fut.rs
Normal file
183
actix-ws/src/fut.rs
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
use std::{
|
||||||
|
collections::VecDeque,
|
||||||
|
future::poll_fn,
|
||||||
|
io,
|
||||||
|
pin::Pin,
|
||||||
|
task::{Context, Poll},
|
||||||
|
};
|
||||||
|
|
||||||
|
use actix_codec::{Decoder, Encoder};
|
||||||
|
use actix_http::{
|
||||||
|
ws::{Codec, Frame, Message, ProtocolError},
|
||||||
|
Payload,
|
||||||
|
};
|
||||||
|
use actix_web::{
|
||||||
|
web::{Bytes, BytesMut},
|
||||||
|
Error,
|
||||||
|
};
|
||||||
|
use futures_core::stream::Stream;
|
||||||
|
use tokio::sync::mpsc::Receiver;
|
||||||
|
|
||||||
|
/// A response body for Websocket HTTP Requests
|
||||||
|
pub struct StreamingBody {
|
||||||
|
session_rx: Receiver<Message>,
|
||||||
|
|
||||||
|
messages: VecDeque<Message>,
|
||||||
|
buf: BytesMut,
|
||||||
|
codec: Codec,
|
||||||
|
closing: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A stream of Messages from a websocket client
|
||||||
|
///
|
||||||
|
/// Messages can be accessed via the stream's `.next()` method
|
||||||
|
pub struct MessageStream {
|
||||||
|
payload: Payload,
|
||||||
|
|
||||||
|
messages: VecDeque<Message>,
|
||||||
|
buf: BytesMut,
|
||||||
|
codec: Codec,
|
||||||
|
closing: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StreamingBody {
|
||||||
|
pub(super) fn new(session_rx: Receiver<Message>) -> Self {
|
||||||
|
StreamingBody {
|
||||||
|
session_rx,
|
||||||
|
messages: VecDeque::new(),
|
||||||
|
buf: BytesMut::new(),
|
||||||
|
codec: Codec::new(),
|
||||||
|
closing: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MessageStream {
|
||||||
|
pub(super) fn new(payload: Payload) -> Self {
|
||||||
|
MessageStream {
|
||||||
|
payload,
|
||||||
|
messages: VecDeque::new(),
|
||||||
|
buf: BytesMut::new(),
|
||||||
|
codec: Codec::new(),
|
||||||
|
closing: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wait for the next item from the message stream
|
||||||
|
///
|
||||||
|
/// ```rust,ignore
|
||||||
|
/// while let Some(Ok(msg)) = stream.recv().await {
|
||||||
|
/// // handle message
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub async fn recv(&mut self) -> Option<Result<Message, ProtocolError>> {
|
||||||
|
poll_fn(|cx| Pin::new(&mut *self).poll_next(cx)).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Stream for StreamingBody {
|
||||||
|
type Item = Result<Bytes, Error>;
|
||||||
|
|
||||||
|
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||||
|
let this = self.get_mut();
|
||||||
|
|
||||||
|
if this.closing {
|
||||||
|
return Poll::Ready(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match Pin::new(&mut this.session_rx).poll_recv(cx) {
|
||||||
|
Poll::Ready(Some(msg)) => {
|
||||||
|
this.messages.push_back(msg);
|
||||||
|
}
|
||||||
|
Poll::Ready(None) => {
|
||||||
|
this.closing = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Poll::Pending => break,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while let Some(msg) = this.messages.pop_front() {
|
||||||
|
if let Err(e) = this.codec.encode(msg, &mut this.buf) {
|
||||||
|
return Poll::Ready(Some(Err(e.into())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !this.buf.is_empty() {
|
||||||
|
return Poll::Ready(Some(Ok(this.buf.split().freeze())));
|
||||||
|
}
|
||||||
|
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Stream for MessageStream {
|
||||||
|
type Item = Result<Message, ProtocolError>;
|
||||||
|
|
||||||
|
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||||
|
let this = self.get_mut();
|
||||||
|
|
||||||
|
// Return the first message in the queue if one exists
|
||||||
|
//
|
||||||
|
// This is faster than polling and parsing
|
||||||
|
if let Some(msg) = this.messages.pop_front() {
|
||||||
|
return Poll::Ready(Some(Ok(msg)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if !this.closing {
|
||||||
|
// Read in bytes until there's nothing left to read
|
||||||
|
loop {
|
||||||
|
match Pin::new(&mut this.payload).poll_next(cx) {
|
||||||
|
Poll::Ready(Some(Ok(bytes))) => {
|
||||||
|
this.buf.extend_from_slice(&bytes);
|
||||||
|
}
|
||||||
|
Poll::Ready(Some(Err(e))) => {
|
||||||
|
return Poll::Ready(Some(Err(ProtocolError::Io(io::Error::new(
|
||||||
|
io::ErrorKind::Other,
|
||||||
|
e.to_string(),
|
||||||
|
)))));
|
||||||
|
}
|
||||||
|
Poll::Ready(None) => {
|
||||||
|
this.closing = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Poll::Pending => break,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create messages until there's no more bytes left
|
||||||
|
while let Some(frame) = this.codec.decode(&mut this.buf)? {
|
||||||
|
let message = match frame {
|
||||||
|
Frame::Text(bytes) => {
|
||||||
|
let s = std::str::from_utf8(&bytes)
|
||||||
|
.map_err(|e| {
|
||||||
|
ProtocolError::Io(io::Error::new(io::ErrorKind::Other, e.to_string()))
|
||||||
|
})?
|
||||||
|
.to_string();
|
||||||
|
Message::Text(s.into())
|
||||||
|
}
|
||||||
|
Frame::Binary(bytes) => Message::Binary(bytes),
|
||||||
|
Frame::Ping(bytes) => Message::Ping(bytes),
|
||||||
|
Frame::Pong(bytes) => Message::Pong(bytes),
|
||||||
|
Frame::Close(reason) => Message::Close(reason),
|
||||||
|
Frame::Continuation(item) => Message::Continuation(item),
|
||||||
|
};
|
||||||
|
|
||||||
|
this.messages.push_back(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the first message in the queue
|
||||||
|
if let Some(msg) = this.messages.pop_front() {
|
||||||
|
return Poll::Ready(Some(Ok(msg)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we've exhausted our message queue and we're closing, close the stream
|
||||||
|
if this.closing {
|
||||||
|
return Poll::Ready(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
}
|
84
actix-ws/src/lib.rs
Normal file
84
actix-ws/src/lib.rs
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
//! WebSockets for Actix Web, without actors.
|
||||||
|
//!
|
||||||
|
//! For usage, see documentation on [`handle()`].
|
||||||
|
|
||||||
|
#![deny(rust_2018_idioms, nonstandard_style, future_incompatible)]
|
||||||
|
#![warn(missing_docs)]
|
||||||
|
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
||||||
|
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
||||||
|
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||||
|
|
||||||
|
pub use actix_http::ws::{CloseCode, CloseReason, Message, ProtocolError};
|
||||||
|
use actix_http::{
|
||||||
|
body::{BodyStream, MessageBody},
|
||||||
|
ws::handshake,
|
||||||
|
};
|
||||||
|
use actix_web::{web, HttpRequest, HttpResponse};
|
||||||
|
use tokio::sync::mpsc::channel;
|
||||||
|
|
||||||
|
mod fut;
|
||||||
|
mod session;
|
||||||
|
|
||||||
|
pub use self::{
|
||||||
|
fut::{MessageStream, StreamingBody},
|
||||||
|
session::{Closed, Session},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Begin handling websocket traffic
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use actix_web::{middleware::Logger, web, App, Error, HttpRequest, HttpResponse, HttpServer};
|
||||||
|
/// use actix_ws::Message;
|
||||||
|
/// use futures::stream::StreamExt as _;
|
||||||
|
///
|
||||||
|
/// async fn ws(req: HttpRequest, body: web::Payload) -> Result<HttpResponse, Error> {
|
||||||
|
/// let (response, mut session, mut msg_stream) = actix_ws::handle(&req, body)?;
|
||||||
|
///
|
||||||
|
/// actix_rt::spawn(async move {
|
||||||
|
/// while let Some(Ok(msg)) = msg_stream.next().await {
|
||||||
|
/// match msg {
|
||||||
|
/// Message::Ping(bytes) => {
|
||||||
|
/// if session.pong(&bytes).await.is_err() {
|
||||||
|
/// return;
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// Message::Text(s) => println!("Got text, {}", s),
|
||||||
|
/// _ => break,
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// let _ = session.close(None).await;
|
||||||
|
/// });
|
||||||
|
///
|
||||||
|
/// Ok(response)
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// #[actix_rt::main]
|
||||||
|
/// async fn main() -> Result<(), anyhow::Error> {
|
||||||
|
/// HttpServer::new(move || {
|
||||||
|
/// App::new()
|
||||||
|
/// .wrap(Logger::default())
|
||||||
|
/// .route("/ws", web::get().to(ws))
|
||||||
|
/// })
|
||||||
|
/// .bind("127.0.0.1:8080")?
|
||||||
|
/// .run()
|
||||||
|
/// .await?;
|
||||||
|
///
|
||||||
|
/// Ok(())
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub fn handle(
|
||||||
|
req: &HttpRequest,
|
||||||
|
body: web::Payload,
|
||||||
|
) -> Result<(HttpResponse, Session, MessageStream), actix_web::Error> {
|
||||||
|
let mut response = handshake(req.head())?;
|
||||||
|
let (tx, rx) = channel(32);
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
response
|
||||||
|
.message_body(BodyStream::new(StreamingBody::new(rx)).boxed())?
|
||||||
|
.into(),
|
||||||
|
Session::new(tx),
|
||||||
|
MessageStream::new(body.into_inner()),
|
||||||
|
))
|
||||||
|
}
|
143
actix-ws/src/session.rs
Normal file
143
actix-ws/src/session.rs
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
use std::sync::{
|
||||||
|
atomic::{AtomicBool, Ordering},
|
||||||
|
Arc,
|
||||||
|
};
|
||||||
|
|
||||||
|
use actix_http::ws::{CloseReason, Message};
|
||||||
|
use actix_web::web::Bytes;
|
||||||
|
use bytestring::ByteString;
|
||||||
|
use tokio::sync::mpsc::Sender;
|
||||||
|
|
||||||
|
/// A handle into the websocket session.
|
||||||
|
///
|
||||||
|
/// This type can be used to send messages into the websocket.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Session {
|
||||||
|
inner: Option<Sender<Message>>,
|
||||||
|
closed: Arc<AtomicBool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The error representing a closed websocket session
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Closed;
|
||||||
|
|
||||||
|
impl std::fmt::Display for Closed {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "Session is closed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for Closed {}
|
||||||
|
|
||||||
|
impl Session {
|
||||||
|
pub(super) fn new(inner: Sender<Message>) -> Self {
|
||||||
|
Session {
|
||||||
|
inner: Some(inner),
|
||||||
|
closed: Arc::new(AtomicBool::new(false)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pre_check(&mut self) {
|
||||||
|
if self.closed.load(Ordering::Relaxed) {
|
||||||
|
self.inner.take();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send text into the websocket
|
||||||
|
///
|
||||||
|
/// ```rust,ignore
|
||||||
|
/// if session.text("Some text").await.is_err() {
|
||||||
|
/// // session closed
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub async fn text(&mut self, msg: impl Into<ByteString>) -> Result<(), Closed> {
|
||||||
|
self.pre_check();
|
||||||
|
if let Some(inner) = self.inner.as_mut() {
|
||||||
|
inner
|
||||||
|
.send(Message::Text(msg.into()))
|
||||||
|
.await
|
||||||
|
.map_err(|_| Closed)
|
||||||
|
} else {
|
||||||
|
Err(Closed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send raw bytes into the websocket
|
||||||
|
///
|
||||||
|
/// ```rust,ignore
|
||||||
|
/// if session.binary(b"some bytes").await.is_err() {
|
||||||
|
/// // session closed
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub async fn binary(&mut self, msg: impl Into<Bytes>) -> Result<(), Closed> {
|
||||||
|
self.pre_check();
|
||||||
|
if let Some(inner) = self.inner.as_mut() {
|
||||||
|
inner
|
||||||
|
.send(Message::Binary(msg.into()))
|
||||||
|
.await
|
||||||
|
.map_err(|_| Closed)
|
||||||
|
} else {
|
||||||
|
Err(Closed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Ping the client
|
||||||
|
///
|
||||||
|
/// For many applications, it will be important to send regular pings to keep track of if the
|
||||||
|
/// client has disconnected
|
||||||
|
///
|
||||||
|
/// ```rust,ignore
|
||||||
|
/// if session.ping(b"").await.is_err() {
|
||||||
|
/// // session is closed
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub async fn ping(&mut self, msg: &[u8]) -> Result<(), Closed> {
|
||||||
|
self.pre_check();
|
||||||
|
if let Some(inner) = self.inner.as_mut() {
|
||||||
|
inner
|
||||||
|
.send(Message::Ping(Bytes::copy_from_slice(msg)))
|
||||||
|
.await
|
||||||
|
.map_err(|_| Closed)
|
||||||
|
} else {
|
||||||
|
Err(Closed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pong the client
|
||||||
|
///
|
||||||
|
/// ```rust,ignore
|
||||||
|
/// match msg {
|
||||||
|
/// Message::Ping(bytes) => {
|
||||||
|
/// let _ = session.pong(&bytes).await;
|
||||||
|
/// }
|
||||||
|
/// _ => (),
|
||||||
|
/// }
|
||||||
|
pub async fn pong(&mut self, msg: &[u8]) -> Result<(), Closed> {
|
||||||
|
self.pre_check();
|
||||||
|
if let Some(inner) = self.inner.as_mut() {
|
||||||
|
inner
|
||||||
|
.send(Message::Pong(Bytes::copy_from_slice(msg)))
|
||||||
|
.await
|
||||||
|
.map_err(|_| Closed)
|
||||||
|
} else {
|
||||||
|
Err(Closed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send a close message, and consume the session
|
||||||
|
///
|
||||||
|
/// All clones will return `Err(Closed)` if used after this call
|
||||||
|
///
|
||||||
|
/// ```rust,ignore
|
||||||
|
/// session.close(None).await
|
||||||
|
/// ```
|
||||||
|
pub async fn close(mut self, reason: Option<CloseReason>) -> Result<(), Closed> {
|
||||||
|
self.pre_check();
|
||||||
|
if let Some(inner) = self.inner.take() {
|
||||||
|
self.closed.store(true, Ordering::Relaxed);
|
||||||
|
inner.send(Message::Close(reason)).await.map_err(|_| Closed)
|
||||||
|
} else {
|
||||||
|
Err(Closed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
37
justfile
Normal file
37
justfile
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
_list:
|
||||||
|
@just --list
|
||||||
|
|
||||||
|
# Document crates in workspace.
|
||||||
|
doc:
|
||||||
|
RUSTDOCFLAGS="--cfg=docsrs" cargo +nightly doc --no-deps --workspace --all-features
|
||||||
|
|
||||||
|
# Document crates in workspace and watch for changes.
|
||||||
|
doc-watch:
|
||||||
|
RUSTDOCFLAGS="--cfg=docsrs" cargo +nightly doc --no-deps --workspace --all-features --open
|
||||||
|
cargo watch -- RUSTDOCFLAGS="--cfg=docsrs" cargo +nightly doc --no-deps --workspace --all-features
|
||||||
|
|
||||||
|
# Check for unintentional external type exposure on all crates in workspace.
|
||||||
|
check-external-types-all toolchain="+nightly":
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
exit=0
|
||||||
|
for f in $(find . -mindepth 2 -maxdepth 2 -name Cargo.toml | grep -vE "\-codegen/|\-derive/|\-macros/"); do
|
||||||
|
if ! just check-external-types-manifest "$f" {{toolchain}}; then exit=1; fi
|
||||||
|
echo
|
||||||
|
echo
|
||||||
|
done
|
||||||
|
exit $exit
|
||||||
|
|
||||||
|
# Check for unintentional external type exposure on all crates in workspace.
|
||||||
|
check-external-types-all-table toolchain="+nightly":
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
for f in $(find . -mindepth 2 -maxdepth 2 -name Cargo.toml | grep -vE "\-codegen/|\-derive/|\-macros/"); do
|
||||||
|
echo
|
||||||
|
echo "Checking for $f"
|
||||||
|
just check-external-types-manifest "$f" {{toolchain}} --output-format=markdown-table
|
||||||
|
done
|
||||||
|
|
||||||
|
# Check for unintentional external type exposure on a crate.
|
||||||
|
check-external-types-manifest manifest_path toolchain="+nightly" *extra_args="":
|
||||||
|
cargo {{toolchain}} check-external-types --manifest-path "{{manifest_path}}" {{extra_args}}
|
Loading…
x
Reference in New Issue
Block a user