diff --git a/.github/workflows/ci-master.yml b/.github/workflows/ci-master.yml
index 548ec21b7..b78617dc5 100644
--- a/.github/workflows/ci-master.yml
+++ b/.github/workflows/ci-master.yml
@@ -5,6 +5,93 @@ on:
branches: [master]
jobs:
+ build_and_test_nightly:
+ strategy:
+ fail-fast: false
+ matrix:
+ target:
+ - { name: Linux, os: ubuntu-latest, triple: x86_64-unknown-linux-gnu }
+ - { name: macOS, os: macos-latest, triple: x86_64-apple-darwin }
+ - { name: Windows, os: windows-2022, triple: x86_64-pc-windows-msvc }
+ version:
+ - nightly
+
+ name: ${{ matrix.target.name }} / ${{ matrix.version }}
+ runs-on: ${{ matrix.target.os }}
+
+ env:
+ CI: 1
+ CARGO_INCREMENTAL: 0
+ VCPKGRS_DYNAMIC: 1
+
+ steps:
+ - uses: actions/checkout@v2
+
+ # install OpenSSL on Windows
+ # TODO: GitHub actions docs state that OpenSSL is
+ # already installed on these Windows machines somewhere
+ - name: Set vcpkg root
+ if: matrix.target.triple == 'x86_64-pc-windows-msvc'
+ run: echo "VCPKG_ROOT=$env:VCPKG_INSTALLATION_ROOT" | Out-File -FilePath $env:GITHUB_ENV -Append
+ - name: Install OpenSSL
+ if: matrix.target.triple == 'x86_64-pc-windows-msvc'
+ run: vcpkg install openssl:x64-windows
+
+ - name: Install ${{ matrix.version }}
+ uses: actions-rs/toolchain@v1
+ with:
+ toolchain: ${{ matrix.version }}-${{ matrix.target.triple }}
+ profile: minimal
+ override: true
+
+ - name: Generate Cargo.lock
+ uses: actions-rs/cargo@v1
+ with: { command: generate-lockfile }
+ - name: Cache Dependencies
+ uses: Swatinem/rust-cache@v1.2.0
+
+ - name: Install cargo-hack
+ uses: actions-rs/cargo@v1
+ with:
+ command: install
+ args: cargo-hack
+
+ - name: check minimal
+ uses: actions-rs/cargo@v1
+ with: { command: ci-check-min }
+
+ - name: check default
+ uses: actions-rs/cargo@v1
+ with: { command: ci-check-default }
+
+ - name: tests
+ timeout-minutes: 60
+ run: |
+ cargo test --lib --tests -p=actix-router --all-features
+ cargo test --lib --tests -p=actix-http --all-features
+ cargo test --lib --tests -p=actix-web --features=rustls,openssl -- --skip=test_reading_deflate_encoding_large_random_rustls
+ cargo test --lib --tests -p=actix-web-codegen --all-features
+ cargo test --lib --tests -p=awc --all-features
+ cargo test --lib --tests -p=actix-http-test --all-features
+ cargo test --lib --tests -p=actix-test --all-features
+ cargo test --lib --tests -p=actix-files
+ cargo test --lib --tests -p=actix-multipart --all-features
+ cargo test --lib --tests -p=actix-web-actors --all-features
+
+ - name: tests (io-uring)
+ if: matrix.target.os == 'ubuntu-latest'
+ timeout-minutes: 60
+ run: >
+ sudo bash -c "ulimit -Sl 512
+ && ulimit -Hl 512
+ && PATH=$PATH:/usr/share/rust/.cargo/bin
+ && RUSTUP_TOOLCHAIN=${{ matrix.version }} cargo test --lib --tests -p=actix-files --all-features"
+
+ - name: Clear the cargo caches
+ run: |
+ cargo install cargo-cache --version 0.6.3 --no-default-features --features ci-autoclean
+ cargo-cache
+
ci_feature_powerset_check:
name: Verify Feature Combinations
runs-on: ubuntu-latest
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 2689804f0..f41aa972f 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -18,7 +18,6 @@ jobs:
version:
- 1.54.0 # MSRV
- stable
- - nightly
name: ${{ matrix.target.name }} / ${{ matrix.version }}
runs-on: ${{ matrix.target.os }}
diff --git a/README.md b/README.md
index c97748c2f..3072ba1c0 100644
--- a/README.md
+++ b/README.md
@@ -11,7 +11,7 @@

[](https://deps.rs/crate/actix-web/4.0.0-beta.18)
-[](https://github.com/actix/actix-web/actions)
+[](https://github.com/actix/actix-web/actions/workflows/ci.yml)
[](https://codecov.io/gh/actix/actix-web)

[](https://discord.gg/NWpN5mmg3x)
diff --git a/src/app.rs b/src/app.rs
index 10868d18d..3fddc055b 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -109,6 +109,7 @@ impl App {
/// .route("/", web::get().to(handler))
/// })
/// ```
+ #[doc(alias = "manage")]
pub fn app_data(mut self, ext: U) -> Self {
self.extensions.insert(ext);
self
diff --git a/src/data.rs b/src/data.rs
index ef077e87c..ce7b1fee6 100644
--- a/src/data.rs
+++ b/src/data.rs
@@ -19,23 +19,32 @@ pub(crate) trait DataFactory {
pub(crate) type FnDataFactory =
Box LocalBoxFuture<'static, Result, ()>>>;
-/// Application data.
+/// Application data wrapper and extractor.
///
-/// Application level data is a piece of arbitrary data attached to the app, scope, or resource.
-/// Application data is available to all routes and can be added during the application
-/// configuration process via `App::data()`.
+/// # Setting Data
+/// Data is set using the `app_data` methods on `App`, `Scope`, and `Resource`. If data is wrapped
+/// in this `Data` type for those calls, it can be used as an extractor.
///
-/// Application data can be accessed by using `Data` extractor where `T` is data type.
+/// Note that `Data` should be constructed _outside_ the `HttpServer::new` closure if shared,
+/// potentially mutable state is desired. `Data` is cheap to clone; internally, it uses an `Arc`.
///
-/// **Note**: HTTP server accepts an application factory rather than an application instance. HTTP
-/// server constructs an application instance for each thread, thus application data must be
-/// constructed multiple times. If you want to share data between different threads, a shareable
-/// object should be used, e.g. `Send + Sync`. Application data does not need to be `Send`
-/// or `Sync`. Internally `Data` contains an `Arc`.
+/// See also [`App::app_data`](crate::App::app_data), [`Scope::app_data`](crate::Scope::app_data),
+/// and [`Resource::app_data`](crate::Resource::app_data).
+///
+/// # Extracting `Data`
+/// Since the Actix Web router layers application data, the returned object will reference the
+/// "closest" instance of the type. For example, if an `App` stores a `u32`, a nested `Scope`
+/// also stores a `u32`, and the delegated request handler falls within that `Scope`, then
+/// extracting a `web::>` for that handler will return the `Scope`'s instance.
+/// However, using the same router set up and a request that does not get captured by the `Scope`,
+/// `web::>` would return the `App`'s instance.
///
/// If route data is not set for a handler, using `Data` extractor would cause a `500 Internal
/// Server Error` response.
///
+/// See also [`HttpRequest::app_data`]
+/// and [`ServiceRequest::app_data`](crate::dev::ServiceRequest::app_data).
+///
/// # Unsized Data
/// For types that are unsized, most commonly `dyn T`, `Data` can wrap these types by first
/// constructing an `Arc` and using the `From` implementation to convert it.
@@ -79,6 +88,7 @@ pub(crate) type FnDataFactory =
/// .route("/index.html", web::get().to(index))
/// .route("/index-alt.html", web::get().to(index_alt));
/// ```
+#[doc(alias = "state")]
#[derive(Debug)]
pub struct Data(Arc);
@@ -90,12 +100,12 @@ impl Data {
}
impl Data {
- /// Get reference to inner app data.
+ /// Returns reference to inner `T`.
pub fn get_ref(&self) -> &T {
self.0.as_ref()
}
- /// Convert to the internal Arc
+ /// Unwraps to the internal `Arc`
pub fn into_inner(self) -> Arc {
self.0
}
@@ -143,13 +153,16 @@ impl FromRequest for Data {
ok(st.clone())
} else {
log::debug!(
- "Failed to construct App-level Data extractor. \
- Request path: {:?} (type: {})",
- req.path(),
+ "Failed to extract `Data<{}>` for `{}` handler. For the Data extractor to work \
+ correctly, wrap the data with `Data::new()` and pass it to `App::app_data()`. \
+ Ensure that types align in both the set and retrieve calls.",
type_name::(),
+ req.match_name().unwrap_or_else(|| req.path())
);
+
err(ErrorInternalServerError(
- "App data is not configured, to configure construct it with web::Data::new() and pass it to App::app_data()",
+ "Requested application data is not configured correctly. \
+ View/enable debug logs for more details.",
))
}
}
diff --git a/src/request.rs b/src/request.rs
index b59369317..cbec70a29 100644
--- a/src/request.rs
+++ b/src/request.rs
@@ -266,14 +266,34 @@ impl HttpRequest {
self.app_state().config()
}
- /// Get an application data object stored with `App::data` or `App::app_data`
- /// methods during application configuration.
+ /// Retrieves a piece of application state.
///
- /// If `App::data` was used to store object, use `Data`:
+ /// Extracts any object stored with [`App::app_data()`](crate::App::app_data) (or the
+ /// counterpart methods on [`Scope`](crate::Scope::app_data) and
+ /// [`Resource`](crate::Resource::app_data)) during application configuration.
///
- /// ```ignore
- /// let opt_t = req.app_data::>();
+ /// Since the Actix Web router layers application data, the returned object will reference the
+ /// "closest" instance of the type. For example, if an `App` stores a `u32`, a nested `Scope`
+ /// also stores a `u32`, and the delegated request handler falls within that `Scope`, then
+ /// calling `.app_data::()` on an `HttpRequest` within that handler will return the
+ /// `Scope`'s instance. However, using the same router set up and a request that does not get
+ /// captured by the `Scope`, `.app_data::()` would return the `App`'s instance.
+ ///
+ /// If the state was stored using the [`Data`] wrapper, then it must also be retrieved using
+ /// this same type.
+ ///
+ /// See also the [`Data`] extractor.
+ ///
+ /// # Examples
+ /// ```no_run
+ /// # use actix_web::{test::TestRequest, web::Data};
+ /// # let req = TestRequest::default().to_http_request();
+ /// # type T = u32;
+ /// let opt_t: Option<&Data> = req.app_data::>();
/// ```
+ ///
+ /// [`Data`]: crate::web::Data
+ #[doc(alias = "state")]
pub fn app_data(&self) -> Option<&T> {
for container in self.inner.app_data.iter().rev() {
if let Some(data) = container.get::() {
diff --git a/src/resource.rs b/src/resource.rs
index 564c4d3ef..8da0a8a85 100644
--- a/src/resource.rs
+++ b/src/resource.rs
@@ -195,6 +195,7 @@ where
/// .route(web::get().to(handler))
/// );
/// ```
+ #[doc(alias = "manage")]
pub fn app_data(mut self, data: U) -> Self {
self.app_data
.get_or_insert_with(Extensions::new)
diff --git a/src/scope.rs b/src/scope.rs
index b4618bb6c..fa9807f42 100644
--- a/src/scope.rs
+++ b/src/scope.rs
@@ -154,6 +154,7 @@ where
/// .route("/", web::get().to(handler))
/// );
/// ```
+ #[doc(alias = "manage")]
pub fn app_data(mut self, data: U) -> Self {
self.app_data
.get_or_insert_with(Extensions::new)