1
0
mirror of https://github.com/fafhrd91/actix-web synced 2025-07-07 19:23:58 +02:00

Compare commits

..

2 Commits

Author SHA1 Message Date
eb10b74751 Merge branch 'master' into error-response-mapping 2024-06-10 01:31:51 +01:00
98c99f3bc2 add methods for mapping error responses 2023-02-13 23:40:23 +00:00
37 changed files with 181 additions and 588 deletions

10
.cargo/config.toml Normal file
View File

@ -0,0 +1,10 @@
[alias]
lint = "clippy --workspace --all-targets -- -Dclippy::todo"
lint-all = "clippy --workspace --all-features --all-targets -- -Dclippy::todo"
# lib checking
ci-check-min = "hack --workspace check --no-default-features"
ci-check-default = "hack --workspace check"
ci-check-default-tests = "check --workspace --tests"
ci-check-all-feature-powerset="hack --workspace --feature-powerset --depth=4 --skip=__compress,experimental-io-uring check"
ci-check-all-feature-powerset-linux="hack --workspace --feature-powerset --depth=4 --skip=__compress check"

View File

@ -44,20 +44,20 @@ jobs:
echo "RUSTFLAGS=-C target-feature=+crt-static" >> $GITHUB_ENV echo "RUSTFLAGS=-C target-feature=+crt-static" >> $GITHUB_ENV
- name: Install Rust (${{ matrix.version.name }}) - name: Install Rust (${{ matrix.version.name }})
uses: actions-rust-lang/setup-rust-toolchain@v1.9.0 uses: actions-rust-lang/setup-rust-toolchain@v1.8.0
with: with:
toolchain: ${{ matrix.version.version }} toolchain: ${{ matrix.version.version }}
- name: Install just, cargo-hack, cargo-nextest, cargo-ci-cache-clean - name: Install just, cargo-hack, cargo-nextest, cargo-ci-cache-clean
uses: taiki-e/install-action@v2.39.1 uses: taiki-e/install-action@v2.34.0
with: with:
tool: just,cargo-hack,cargo-nextest,cargo-ci-cache-clean tool: just,cargo-hack,cargo-nextest,cargo-ci-cache-clean
- name: check minimal - name: check minimal
run: just check-min run: cargo ci-check-min
- name: check default - name: check default
run: just check-default run: cargo ci-check-default
- name: tests - name: tests
timeout-minutes: 60 timeout-minutes: 60
@ -76,16 +76,16 @@ jobs:
- name: Free Disk Space - name: Free Disk Space
run: ./scripts/free-disk-space.sh run: ./scripts/free-disk-space.sh
- name: Setup mold linker
uses: rui314/setup-mold@v1
- name: Install Rust - name: Install Rust
uses: actions-rust-lang/setup-rust-toolchain@v1.9.0 uses: actions-rust-lang/setup-rust-toolchain@v1.8.0
- name: Install just, cargo-hack - name: Install cargo-hack
uses: taiki-e/install-action@v2.39.1 uses: taiki-e/install-action@v2.34.0
with: with:
tool: just,cargo-hack tool: cargo-hack
- name: Check feature combinations - name: check feature combinations
run: just check-feature-combinations run: cargo ci-check-all-feature-powerset
- name: check feature combinations
run: cargo ci-check-all-feature-powerset-linux

View File

@ -59,12 +59,12 @@ jobs:
uses: rui314/setup-mold@v1 uses: rui314/setup-mold@v1
- name: Install Rust (${{ matrix.version.name }}) - name: Install Rust (${{ matrix.version.name }})
uses: actions-rust-lang/setup-rust-toolchain@v1.9.0 uses: actions-rust-lang/setup-rust-toolchain@v1.8.0
with: with:
toolchain: ${{ matrix.version.version }} toolchain: ${{ matrix.version.version }}
- name: Install just, cargo-hack, cargo-nextest, cargo-ci-cache-clean - name: Install just, cargo-hack, cargo-nextest, cargo-ci-cache-clean
uses: taiki-e/install-action@v2.39.1 uses: taiki-e/install-action@v2.34.0
with: with:
tool: just,cargo-hack,cargo-nextest,cargo-ci-cache-clean tool: just,cargo-hack,cargo-nextest,cargo-ci-cache-clean
@ -73,10 +73,10 @@ jobs:
run: just downgrade-for-msrv run: just downgrade-for-msrv
- name: check minimal - name: check minimal
run: just check-min run: cargo ci-check-min
- name: check default - name: check default
run: just check-default run: cargo ci-check-default
- name: tests - name: tests
timeout-minutes: 60 timeout-minutes: 60
@ -92,7 +92,7 @@ jobs:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Install Rust - name: Install Rust
uses: actions-rust-lang/setup-rust-toolchain@v1.9.0 uses: actions-rust-lang/setup-rust-toolchain@v1.8.0
with: with:
toolchain: nightly toolchain: nightly
@ -108,12 +108,12 @@ jobs:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Install Rust (nightly) - name: Install Rust (nightly)
uses: actions-rust-lang/setup-rust-toolchain@v1.9.0 uses: actions-rust-lang/setup-rust-toolchain@v1.8.0
with: with:
toolchain: nightly toolchain: nightly
- name: Install just - name: Install just
uses: taiki-e/install-action@v2.39.1 uses: taiki-e/install-action@v2.34.0
with: with:
tool: just tool: just

View File

@ -17,22 +17,21 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Install Rust (nightly) - name: Install Rust
uses: actions-rust-lang/setup-rust-toolchain@v1.9.0 uses: actions-rust-lang/setup-rust-toolchain@v1.8.0
with: with:
toolchain: nightly components: llvm-tools-preview
components: llvm-tools
- name: Install just, cargo-llvm-cov, cargo-nextest - name: Install just,cargo-llvm-cov
uses: taiki-e/install-action@v2.39.1 uses: taiki-e/install-action@v2.34.0
with: with:
tool: just,cargo-llvm-cov,cargo-nextest tool: just,cargo-llvm-cov
- name: Generate code coverage - name: Generate code coverage
run: just test-coverage-codecov run: cargo llvm-cov --workspace --all-features --codecov --output-path codecov.json
- name: Upload coverage to Codecov - name: Upload coverage to Codecov
uses: codecov/codecov-action@v4.5.0 uses: codecov/codecov-action@v4.4.1
with: with:
files: codecov.json files: codecov.json
fail_ci_if_error: true fail_ci_if_error: true

View File

@ -18,7 +18,7 @@ jobs:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Install Rust (nightly) - name: Install Rust (nightly)
uses: actions-rust-lang/setup-rust-toolchain@v1.9.0 uses: actions-rust-lang/setup-rust-toolchain@v1.8.0
with: with:
toolchain: nightly toolchain: nightly
components: rustfmt components: rustfmt
@ -36,7 +36,7 @@ jobs:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Install Rust - name: Install Rust
uses: actions-rust-lang/setup-rust-toolchain@v1.9.0 uses: actions-rust-lang/setup-rust-toolchain@v1.8.0
with: with:
components: clippy components: clippy
@ -55,7 +55,7 @@ jobs:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Install Rust (nightly) - name: Install Rust (nightly)
uses: actions-rust-lang/setup-rust-toolchain@v1.9.0 uses: actions-rust-lang/setup-rust-toolchain@v1.8.0
with: with:
toolchain: nightly toolchain: nightly
components: rust-docs components: rust-docs
@ -65,29 +65,6 @@ jobs:
RUSTDOCFLAGS: -D warnings RUSTDOCFLAGS: -D warnings
run: cargo +nightly doc --no-deps --workspace --all-features run: cargo +nightly doc --no-deps --workspace --all-features
check-external-types:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust (nightly-2024-05-01)
uses: actions-rust-lang/setup-rust-toolchain@v1.9.0
with:
toolchain: nightly-2024-05-01
- name: Install just
uses: taiki-e/install-action@v2.39.1
with:
tool: just
- name: Install cargo-check-external-types
uses: taiki-e/cache-cargo-install-action@v2.0.1
with:
tool: cargo-check-external-types
- name: check external types
run: just check-external-types-all +nightly-2024-05-01
public-api-diff: public-api-diff:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
@ -99,13 +76,13 @@ jobs:
- name: Checkout PR branch - name: Checkout PR branch
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Install Rust (nightly-2024-06-07) - name: Install Rust
uses: actions-rust-lang/setup-rust-toolchain@v1.9.0 uses: actions-rust-lang/setup-rust-toolchain@v1.8.0
with: with:
toolchain: nightly-2024-06-07 toolchain: nightly-2024-06-07
- name: Install cargo-public-api - name: Install cargo-public-api
uses: taiki-e/install-action@v2.39.1 uses: taiki-e/install-action@v2.34.0
with: with:
tool: cargo-public-api tool: cargo-public-api

View File

@ -13,14 +13,9 @@ categories = ["asynchronous", "web-programming::http-server"]
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
edition = "2021" edition = "2021"
[package.metadata.cargo_check_external_types] [lib]
allowed_external_types = [ name = "actix_files"
"actix_http::*", path = "src/lib.rs"
"actix_service::*",
"actix_web::*",
"http::*",
"mime::*",
]
[features] [features]
experimental-io-uring = ["actix-web/experimental-io-uring", "tokio-uring"] experimental-io-uring = ["actix-web/experimental-io-uring", "tokio-uring"]

View File

@ -18,17 +18,9 @@ edition = "2021"
[package.metadata.docs.rs] [package.metadata.docs.rs]
features = [] features = []
[package.metadata.cargo_check_external_types] [lib]
allowed_external_types = [ name = "actix_http_test"
"actix_codec::*", path = "src/lib.rs"
"actix_http::*",
"actix_server::*",
"awc::*",
"bytes::*",
"futures_core::*",
"http::*",
"tokio::*",
]
[features] [features]
default = [] default = []

View File

@ -1,5 +1,7 @@
# `actix-http-test` # `actix-http-test`
> Various helpers for Actix applications to use during testing.
<!-- prettier-ignore-start --> <!-- prettier-ignore-start -->
[![crates.io](https://img.shields.io/crates/v/actix-http-test?label=latest)](https://crates.io/crates/actix-http-test) [![crates.io](https://img.shields.io/crates/v/actix-http-test?label=latest)](https://crates.io/crates/actix-http-test)
@ -12,9 +14,3 @@
[![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x) [![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x)
<!-- prettier-ignore-end --> <!-- prettier-ignore-end -->
<!-- cargo-rdme start -->
Various helpers for Actix applications to use during testing.
<!-- cargo-rdme end -->

View File

@ -2,8 +2,6 @@
## Unreleased ## Unreleased
## 3.8.0
### Added ### Added
- Add `error::InvalidStatusCode` re-export. - Add `error::InvalidStatusCode` re-export.

View File

@ -1,6 +1,6 @@
[package] [package]
name = "actix-http" name = "actix-http"
version = "3.8.0" version = "3.7.0"
authors = [ authors = [
"Nikolay Kim <fafhrd91@gmail.com>", "Nikolay Kim <fafhrd91@gmail.com>",
"Rob Ede <robjtede@icloud.com>", "Rob Ede <robjtede@icloud.com>",
@ -34,72 +34,51 @@ features = [
"compress-zstd", "compress-zstd",
] ]
[package.metadata.cargo_check_external_types] [lib]
allowed_external_types = [ name = "actix_http"
"actix_codec::*", path = "src/lib.rs"
"actix_service::*",
"actix_tls::*",
"actix_utils::*",
"bytes::*",
"bytestring::*",
"encoding_rs::*",
"futures_core::*",
"h2::*",
"http::*",
"httparse::*",
"language_tags::*",
"mime::*",
"openssl::*",
"rustls::*",
"tokio_util::*",
"tokio::*",
]
[features] [features]
default = [] default = []
# HTTP/2 protocol support # HTTP/2 protocol support
http2 = ["dep:h2"] http2 = ["h2"]
# WebSocket protocol implementation # WebSocket protocol implementation
ws = [ ws = [
"dep:local-channel", "local-channel",
"dep:base64", "base64",
"dep:rand", "rand",
"dep:sha1", "sha1",
] ]
# TLS via OpenSSL # TLS via OpenSSL
openssl = ["__tls", "actix-tls/accept", "actix-tls/openssl"] openssl = ["actix-tls/accept", "actix-tls/openssl"]
# TLS via Rustls v0.20 # TLS via Rustls v0.20
rustls = ["__tls", "rustls-0_20"] rustls = ["rustls-0_20"]
# TLS via Rustls v0.20 # TLS via Rustls v0.20
rustls-0_20 = ["__tls", "actix-tls/accept", "actix-tls/rustls-0_20"] rustls-0_20 = ["actix-tls/accept", "actix-tls/rustls-0_20"]
# TLS via Rustls v0.21 # TLS via Rustls v0.21
rustls-0_21 = ["__tls", "actix-tls/accept", "actix-tls/rustls-0_21"] rustls-0_21 = ["actix-tls/accept", "actix-tls/rustls-0_21"]
# TLS via Rustls v0.22 # TLS via Rustls v0.22
rustls-0_22 = ["__tls", "actix-tls/accept", "actix-tls/rustls-0_22"] rustls-0_22 = ["actix-tls/accept", "actix-tls/rustls-0_22"]
# TLS via Rustls v0.23 # TLS via Rustls v0.23
rustls-0_23 = ["__tls", "actix-tls/accept", "actix-tls/rustls-0_23"] rustls-0_23 = ["actix-tls/accept", "actix-tls/rustls-0_23"]
# Compression codecs # Compression codecs
compress-brotli = ["__compress", "dep:brotli"] compress-brotli = ["__compress", "brotli"]
compress-gzip = ["__compress", "dep:flate2"] compress-gzip = ["__compress", "flate2"]
compress-zstd = ["__compress", "dep:zstd"] compress-zstd = ["__compress", "zstd"]
# Internal (PRIVATE!) features used to aid testing and checking feature status. # Internal (PRIVATE!) features used to aid testing and checking feature status.
# Don't rely on these whatsoever. They are semver-exempt and may disappear at anytime. # Don't rely on these whatsoever. They are semver-exempt and may disappear at anytime.
__compress = [] __compress = []
# Internal (PRIVATE!) features used to aid checking feature status.
# Don't rely on these whatsoever. They may disappear at anytime.
__tls = []
[dependencies] [dependencies]
actix-service = "2" actix-service = "2"
actix-codec = "0.5" actix-codec = "0.5"

View File

@ -5,11 +5,11 @@
<!-- prettier-ignore-start --> <!-- prettier-ignore-start -->
[![crates.io](https://img.shields.io/crates/v/actix-http?label=latest)](https://crates.io/crates/actix-http) [![crates.io](https://img.shields.io/crates/v/actix-http?label=latest)](https://crates.io/crates/actix-http)
[![Documentation](https://docs.rs/actix-http/badge.svg?version=3.8.0)](https://docs.rs/actix-http/3.8.0) [![Documentation](https://docs.rs/actix-http/badge.svg?version=3.7.0)](https://docs.rs/actix-http/3.7.0)
![Version](https://img.shields.io/badge/rustc-1.72+-ab6000.svg) ![Version](https://img.shields.io/badge/rustc-1.72+-ab6000.svg)
![MIT or Apache 2.0 licensed](https://img.shields.io/crates/l/actix-http.svg) ![MIT or Apache 2.0 licensed](https://img.shields.io/crates/l/actix-http.svg)
<br /> <br />
[![dependency status](https://deps.rs/crate/actix-http/3.8.0/status.svg)](https://deps.rs/crate/actix-http/3.8.0) [![dependency status](https://deps.rs/crate/actix-http/3.7.0/status.svg)](https://deps.rs/crate/actix-http/3.7.0)
[![Download](https://img.shields.io/crates/d/actix-http.svg)](https://crates.io/crates/actix-http) [![Download](https://img.shields.io/crates/d/actix-http.svg)](https://crates.io/crates/actix-http)
[![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x) [![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x)

View File

@ -6,10 +6,10 @@
//! | ------------------- | ------------------------------------------- | //! | ------------------- | ------------------------------------------- |
//! | `http2` | HTTP/2 support via [h2]. | //! | `http2` | HTTP/2 support via [h2]. |
//! | `openssl` | TLS support via [OpenSSL]. | //! | `openssl` | TLS support via [OpenSSL]. |
//! | `rustls-0_20` | TLS support via rustls 0.20. | //! | `rustls` | TLS support via [rustls] 0.20. |
//! | `rustls-0_21` | TLS support via rustls 0.21. | //! | `rustls-0_21` | TLS support via [rustls] 0.21. |
//! | `rustls-0_22` | TLS support via rustls 0.22. | //! | `rustls-0_22` | TLS support via [rustls] 0.22. |
//! | `rustls-0_23` | TLS support via [rustls] 0.23. | //! | `rustls-0_23` | TLS support via [rustls] 0.23. |
//! | `compress-brotli` | Payload compression support: Brotli. | //! | `compress-brotli` | Payload compression support: Brotli. |
//! | `compress-gzip` | Payload compression support: Deflate, Gzip. | //! | `compress-gzip` | Payload compression support: Deflate, Gzip. |
//! | `compress-zstd` | Payload compression support: Zstd. | //! | `compress-zstd` | Payload compression support: Zstd. |
@ -61,7 +61,13 @@ pub mod ws;
#[allow(deprecated)] #[allow(deprecated)]
pub use self::payload::PayloadStream; pub use self::payload::PayloadStream;
#[cfg(feature = "__tls")] #[cfg(any(
feature = "openssl",
feature = "rustls-0_20",
feature = "rustls-0_21",
feature = "rustls-0_22",
feature = "rustls-0_23",
))]
pub use self::service::TlsAcceptorConfig; pub use self::service::TlsAcceptorConfig;
pub use self::{ pub use self::{
builder::HttpServiceBuilder, builder::HttpServiceBuilder,

View File

@ -241,13 +241,25 @@ where
} }
/// Configuration options used when accepting TLS connection. /// Configuration options used when accepting TLS connection.
#[cfg(feature = "__tls")] #[cfg(any(
feature = "openssl",
feature = "rustls-0_20",
feature = "rustls-0_21",
feature = "rustls-0_22",
feature = "rustls-0_23",
))]
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct TlsAcceptorConfig { pub struct TlsAcceptorConfig {
pub(crate) handshake_timeout: Option<std::time::Duration>, pub(crate) handshake_timeout: Option<std::time::Duration>,
} }
#[cfg(feature = "__tls")] #[cfg(any(
feature = "openssl",
feature = "rustls-0_20",
feature = "rustls-0_21",
feature = "rustls-0_22",
feature = "rustls-0_23",
))]
impl TlsAcceptorConfig { impl TlsAcceptorConfig {
/// Set TLS handshake timeout duration. /// Set TLS handshake timeout duration.
pub fn handshake_timeout(self, dur: std::time::Duration) -> Self { pub fn handshake_timeout(self, dur: std::time::Duration) -> Self {

View File

@ -16,21 +16,6 @@ edition = "2021"
rustdoc-args = ["--cfg", "docsrs"] rustdoc-args = ["--cfg", "docsrs"]
all-features = true all-features = true
[package.metadata.cargo_check_external_types]
allowed_external_types = [
"actix_http::*",
"actix_multipart_derive::*",
"actix_utils::*",
"actix_web::*",
"bytes::*",
"futures_core::*",
"mime::*",
"serde_json::*",
"serde_plain::*",
"serde::*",
"tempfile::*",
]
[features] [features]
default = ["tempfile", "derive"] default = ["tempfile", "derive"]
derive = ["actix-multipart-derive"] derive = ["actix-multipart-derive"]

View File

@ -1,5 +1,7 @@
# `actix-multipart` # `actix-multipart`
> Multipart form support for Actix Web.
<!-- prettier-ignore-start --> <!-- prettier-ignore-start -->
[![crates.io](https://img.shields.io/crates/v/actix-multipart?label=latest)](https://crates.io/crates/actix-multipart) [![crates.io](https://img.shields.io/crates/v/actix-multipart?label=latest)](https://crates.io/crates/actix-multipart)
@ -13,11 +15,18 @@
<!-- prettier-ignore-end --> <!-- prettier-ignore-end -->
<!-- cargo-rdme start --> ## Example
Multipart form support for Actix Web. Dependencies:
## Examples ```toml
[dependencies]
actix-multipart = "0.6"
actix-web = "4.5"
serde = { version = "1.0", features = ["derive"] }
```
Code:
```rust ```rust
use actix_web::{post, App, HttpServer, Responder}; use actix_web::{post, App, HttpServer, Responder};
@ -54,10 +63,6 @@ async fn main() -> std::io::Result<()> {
} }
``` ```
<!-- cargo-rdme end -->
[More available in the examples repo &rarr;](https://github.com/actix/examples/tree/master/forms/multipart)
Curl request : Curl request :
```bash ```bash
@ -66,3 +71,7 @@ curl -v --request POST \
-F 'json={"name": "Cargo.lock"};type=application/json' \ -F 'json={"name": "Cargo.lock"};type=application/json' \
-F file=@./Cargo.lock -F file=@./Cargo.lock
``` ```
### Examples
https://github.com/actix/examples/tree/master/forms/multipart

View File

@ -33,14 +33,6 @@ pub trait FieldReader<'t>: Sized + Any {
type Future: Future<Output = Result<Self, MultipartError>>; type Future: Future<Output = Result<Self, MultipartError>>;
/// The form will call this function to handle the field. /// The form will call this function to handle the field.
///
/// # Panics
///
/// When reading the `field` payload using its `Stream` implementation, polling (manually or via
/// `next()`/`try_next()`) may panic after the payload is exhausted. If this is a problem for
/// your implementation of this method, you should [`fuse()`] the `Field` first.
///
/// [`fuse()`]: futures_util::stream::StreamExt::fuse()
fn read_field(req: &'t HttpRequest, field: Field, limits: &'t mut Limits) -> Self::Future; fn read_field(req: &'t HttpRequest, field: Field, limits: &'t mut Limits) -> Self::Future;
} }
@ -404,20 +396,11 @@ mod tests {
use actix_http::encoding::Decoder; use actix_http::encoding::Decoder;
use actix_multipart_rfc7578::client::multipart; use actix_multipart_rfc7578::client::multipart;
use actix_test::TestServer; use actix_test::TestServer;
use actix_web::{ use actix_web::{dev::Payload, http::StatusCode, web, App, HttpResponse, Responder};
dev::Payload, http::StatusCode, web, App, HttpRequest, HttpResponse, Resource, Responder,
};
use awc::{Client, ClientResponse}; use awc::{Client, ClientResponse};
use futures_core::future::LocalBoxFuture;
use futures_util::TryStreamExt as _;
use super::MultipartForm; use super::MultipartForm;
use crate::{ use crate::form::{bytes::Bytes, tempfile::TempFile, text::Text, MultipartFormConfig};
form::{
bytes::Bytes, tempfile::TempFile, text::Text, FieldReader, Limits, MultipartFormConfig,
},
Field, MultipartError,
};
pub async fn send_form( pub async fn send_form(
srv: &TestServer, srv: &TestServer,
@ -751,49 +734,4 @@ mod tests {
let response = send_form(&srv, form, "/").await; let response = send_form(&srv, form, "/").await;
assert_eq!(response.status(), StatusCode::BAD_REQUEST); assert_eq!(response.status(), StatusCode::BAD_REQUEST);
} }
#[should_panic(expected = "called `Result::unwrap()` on an `Err` value: Connect(Disconnected)")]
#[actix_web::test]
async fn field_try_next_panic() {
#[derive(Debug)]
struct NullSink;
impl<'t> FieldReader<'t> for NullSink {
type Future = LocalBoxFuture<'t, Result<Self, MultipartError>>;
fn read_field(
_: &'t HttpRequest,
mut field: Field,
_limits: &'t mut Limits,
) -> Self::Future {
Box::pin(async move {
// exhaust field stream
while let Some(_chunk) = field.try_next().await? {}
// poll again, crash
let _post = field.try_next().await;
Ok(Self)
})
}
}
#[allow(dead_code)]
#[derive(MultipartForm)]
struct NullSinkForm {
foo: NullSink,
}
async fn null_sink(_form: MultipartForm<NullSinkForm>) -> impl Responder {
"unreachable"
}
let srv = actix_test::start(|| App::new().service(Resource::new("/").post(null_sink)));
let mut form = multipart::Form::default();
form.add_text("foo", "data is not important to this test");
// panics with Err(Connect(Disconnected)) due to form NullSink panic
let _res = send_form(&srv, form, "/").await;
}
} }

View File

@ -1,7 +1,5 @@
//! Multipart form support for Actix Web. //! Multipart form support for Actix Web.
//!
//! # Examples //! # Examples
//!
//! ```no_run //! ```no_run
//! use actix_web::{post, App, HttpServer, Responder}; //! use actix_web::{post, App, HttpServer, Responder};
//! //!

View File

@ -465,12 +465,7 @@ impl Stream for Field {
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> { fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let this = self.get_mut(); let this = self.get_mut();
let mut inner = this.inner.borrow_mut(); let mut inner = this.inner.borrow_mut();
if let Some(mut buffer) = inner if let Some(mut buffer) = inner.payload.as_ref().unwrap().get_mut(&this.safety) {
.payload
.as_ref()
.expect("Field should not be polled after completion")
.get_mut(&this.safety)
{
// check safety and poll read payload to buffer. // check safety and poll read payload to buffer.
buffer.poll_stream(cx)?; buffer.poll_stream(cx)?;
} else if !this.safety.is_clean() { } else if !this.safety.is_clean() {
@ -501,7 +496,6 @@ impl fmt::Debug for Field {
} }
struct InnerField { struct InnerField {
/// Payload is initialized as Some and is `take`n when the field stream finishes.
payload: Option<PayloadRef>, payload: Option<PayloadRef>,
boundary: String, boundary: String,
eof: bool, eof: bool,
@ -649,12 +643,7 @@ impl InnerField {
return Poll::Ready(None); return Poll::Ready(None);
} }
let result = if let Some(mut payload) = self let result = if let Some(mut payload) = self.payload.as_ref().unwrap().get_mut(s) {
.payload
.as_ref()
.expect("Field should not be polled after completion")
.get_mut(s)
{
if !self.eof { if !self.eof {
let res = if let Some(ref mut len) = self.length { let res = if let Some(ref mut len) = self.length {
InnerField::read_len(&mut payload, len) InnerField::read_len(&mut payload, len)
@ -685,10 +674,8 @@ impl InnerField {
}; };
if let Poll::Ready(None) = result { if let Poll::Ready(None) = result {
// drop payload buffer and make future un-poll-able self.payload.take();
let _ = self.payload.take();
} }
result result
} }
} }

View File

@ -12,11 +12,9 @@ repository = "https://github.com/actix/actix-web"
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
edition = "2021" edition = "2021"
[package.metadata.cargo_check_external_types] [lib]
allowed_external_types = [ name = "actix_router"
"http::*", path = "src/lib.rs"
"serde::*",
]
[features] [features]
default = ["http", "unicode"] default = ["http", "unicode"]

View File

@ -18,22 +18,6 @@ categories = [
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
edition = "2021" edition = "2021"
[package.metadata.cargo_check_external_types]
allowed_external_types = [
"actix_codec::*",
"actix_http_test::*",
"actix_http::*",
"actix_service::*",
"actix_web::*",
"awc::*",
"bytes::*",
"futures_core::*",
"http::*",
"openssl::*",
"rustls::*",
"tokio::*",
]
[features] [features]
default = [] default = []

View File

@ -1,45 +0,0 @@
# `actix-test`
<!-- prettier-ignore-start -->
[![crates.io](https://img.shields.io/crates/v/actix-test?label=latest)](https://crates.io/crates/actix-test)
[![Documentation](https://docs.rs/actix-test/badge.svg?version=0.1.5)](https://docs.rs/actix-test/0.1.5)
![Version](https://img.shields.io/badge/rustc-1.72+-ab6000.svg)
![MIT or Apache 2.0 licensed](https://img.shields.io/crates/l/actix-test.svg)
<br />
[![dependency status](https://deps.rs/crate/actix-test/0.1.5/status.svg)](https://deps.rs/crate/actix-test/0.1.5)
[![Download](https://img.shields.io/crates/d/actix-test.svg)](https://crates.io/crates/actix-test)
[![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x)
<!-- prettier-ignore-end -->
<!-- cargo-rdme start -->
Integration testing tools for Actix Web applications.
The main integration testing tool is [`TestServer`]. It spawns a real HTTP server on an unused port and provides methods that use a real HTTP client. Therefore, it is much closer to real-world cases than using `init_service`, which skips HTTP encoding and decoding.
## Examples
```rust
use actix_web::{get, web, test, App, HttpResponse, Error, Responder};
#[get("/")]
async fn my_handler() -> Result<impl Responder, Error> {
Ok(HttpResponse::Ok())
}
#[actix_rt::test]
async fn test_example() {
let srv = actix_test::start(||
App::new().service(my_handler)
);
let req = srv.get("/");
let res = req.send().await.unwrap();
assert!(res.status().is_success());
}
```
<!-- cargo-rdme end -->

View File

@ -5,7 +5,6 @@
//! real-world cases than using `init_service`, which skips HTTP encoding and decoding. //! real-world cases than using `init_service`, which skips HTTP encoding and decoding.
//! //!
//! # Examples //! # Examples
//!
//! ``` //! ```
//! use actix_web::{get, web, test, App, HttpResponse, Error, Responder}; //! use actix_web::{get, web, test, App, HttpResponse, Error, Responder};
//! //!

View File

@ -9,15 +9,9 @@ repository = "https://github.com/actix/actix-web"
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
edition = "2021" edition = "2021"
[package.metadata.cargo_check_external_types] [lib]
allowed_external_types = [ name = "actix_web_actors"
"actix::*", path = "src/lib.rs"
"actix_http::*",
"actix_web::*",
"bytes::*",
"bytestring::*",
"futures_core::*",
]
[dependencies] [dependencies]
actix = { version = ">=0.12, <0.14", default-features = false } actix = { version = ">=0.12, <0.14", default-features = false }

View File

@ -2,18 +2,9 @@
## Unreleased ## Unreleased
## 4.8.0
### Added
- Add `web::Html` responder.
- Add `HttpRequest::full_url()` method to get the complete URL of the request.
### Fixed ### Fixed
- Always remove port from return value of `ConnectionInfo::realip_remote_addr()` when handling IPv6 addresses. from the `Forwarded` header. - `ConnectionInfo::realip_remote_addr()` now handles IPv6 addresses from `Forwarded` header correctly. Previously, it sometimes returned the forwarded port as well.
- The `UrlencodedError::ContentType` variant (relevant to the `Form` extractor) now uses the 415 (Media Type Unsupported) status code in it's `ResponseError` implementation.
- Apply `HttpServer::max_connection_rate()` setting when using rustls v0.22 or v0.23.
## 4.7.0 ## 4.7.0

View File

@ -1,6 +1,6 @@
[package] [package]
name = "actix-web" name = "actix-web"
version = "4.8.0" version = "4.7.0"
description = "Actix Web is a powerful, pragmatic, and extremely fast web framework for Rust" description = "Actix Web is a powerful, pragmatic, and extremely fast web framework for Rust"
authors = [ authors = [
"Nikolay Kim <fafhrd91@gmail.com>", "Nikolay Kim <fafhrd91@gmail.com>",
@ -35,31 +35,9 @@ features = [
"secure-cookies", "secure-cookies",
] ]
[package.metadata.cargo_check_external_types] [lib]
allowed_external_types = [ name = "actix_web"
"actix_http::*", path = "src/lib.rs"
"actix_router::*",
"actix_rt::*",
"actix_server::*",
"actix_service::*",
"actix_utils::*",
"actix_web_codegen::*",
"bytes::*",
"cookie::*",
"cookie",
"futures_core::*",
"http::*",
"language_tags::*",
"mime::*",
"openssl::*",
"rustls::*",
"serde_json::*",
"serde_urlencoded::*",
"serde::*",
"serde::*",
"tokio::*",
"url::*",
]
[features] [features]
default = [ default = [
@ -93,18 +71,18 @@ secure-cookies = ["cookies", "cookie/secure"]
http2 = ["actix-http/http2"] http2 = ["actix-http/http2"]
# TLS via OpenSSL # TLS via OpenSSL
openssl = ["__tls", "http2", "actix-http/openssl", "actix-tls/accept", "actix-tls/openssl"] openssl = ["http2", "actix-http/openssl", "actix-tls/accept", "actix-tls/openssl"]
# TLS via Rustls v0.20 # TLS via Rustls v0.20
rustls = ["rustls-0_20"] rustls = ["rustls-0_20"]
# TLS via Rustls v0.20 # TLS via Rustls v0.20
rustls-0_20 = ["__tls", "http2", "actix-http/rustls-0_20", "actix-tls/accept", "actix-tls/rustls-0_20"] rustls-0_20 = ["http2", "actix-http/rustls-0_20", "actix-tls/accept", "actix-tls/rustls-0_20"]
# TLS via Rustls v0.21 # TLS via Rustls v0.21
rustls-0_21 = ["__tls", "http2", "actix-http/rustls-0_21", "actix-tls/accept", "actix-tls/rustls-0_21"] rustls-0_21 = ["http2", "actix-http/rustls-0_21", "actix-tls/accept", "actix-tls/rustls-0_21"]
# TLS via Rustls v0.22 # TLS via Rustls v0.22
rustls-0_22 = ["__tls", "http2", "actix-http/rustls-0_22", "actix-tls/accept", "actix-tls/rustls-0_22"] rustls-0_22 = ["http2", "actix-http/rustls-0_22", "actix-tls/accept", "actix-tls/rustls-0_22"]
# TLS via Rustls v0.23 # TLS via Rustls v0.23
rustls-0_23 = ["__tls", "http2", "actix-http/rustls-0_23", "actix-tls/accept", "actix-tls/rustls-0_23"] rustls-0_23 = ["http2", "actix-http/rustls-0_23", "actix-tls/accept", "actix-tls/rustls-0_23"]
# Full unicode support # Full unicode support
unicode = ["dep:regex", "actix-router/unicode"] unicode = ["dep:regex", "actix-router/unicode"]
@ -113,10 +91,6 @@ unicode = ["dep:regex", "actix-router/unicode"]
# Don't rely on these whatsoever. They may disappear at anytime. # Don't rely on these whatsoever. They may disappear at anytime.
__compress = [] __compress = []
# Internal (PRIVATE!) features used to aid checking feature status.
# Don't rely on these whatsoever. They may disappear at anytime.
__tls = []
# io-uring feature only available for Linux OSes. # io-uring feature only available for Linux OSes.
experimental-io-uring = ["actix-server/io-uring"] experimental-io-uring = ["actix-server/io-uring"]

View File

@ -8,10 +8,10 @@
<!-- prettier-ignore-start --> <!-- prettier-ignore-start -->
[![crates.io](https://img.shields.io/crates/v/actix-web?label=latest)](https://crates.io/crates/actix-web) [![crates.io](https://img.shields.io/crates/v/actix-web?label=latest)](https://crates.io/crates/actix-web)
[![Documentation](https://docs.rs/actix-web/badge.svg?version=4.8.0)](https://docs.rs/actix-web/4.8.0) [![Documentation](https://docs.rs/actix-web/badge.svg?version=4.7.0)](https://docs.rs/actix-web/4.7.0)
![MSRV](https://img.shields.io/badge/rustc-1.72+-ab6000.svg) ![MSRV](https://img.shields.io/badge/rustc-1.72+-ab6000.svg)
![MIT or Apache 2.0 licensed](https://img.shields.io/crates/l/actix-web.svg) ![MIT or Apache 2.0 licensed](https://img.shields.io/crates/l/actix-web.svg)
[![Dependency Status](https://deps.rs/crate/actix-web/4.8.0/status.svg)](https://deps.rs/crate/actix-web/4.8.0) [![Dependency Status](https://deps.rs/crate/actix-web/4.7.0/status.svg)](https://deps.rs/crate/actix-web/4.7.0)
<br /> <br />
[![CI](https://github.com/actix/actix-web/actions/workflows/ci.yml/badge.svg)](https://github.com/actix/actix-web/actions/workflows/ci.yml) [![CI](https://github.com/actix/actix-web/actions/workflows/ci.yml/badge.svg)](https://github.com/actix/actix-web/actions/workflows/ci.yml)
[![codecov](https://codecov.io/gh/actix/actix-web/branch/master/graph/badge.svg)](https://codecov.io/gh/actix/actix-web) [![codecov](https://codecov.io/gh/actix/actix-web/branch/master/graph/badge.svg)](https://codecov.io/gh/actix/actix-web)
@ -109,4 +109,4 @@ This project is licensed under either of the following licenses, at your option:
## Code of Conduct ## Code of Conduct
Contribution to the `actix/actix-web` repo is organized under the terms of the Contributor Covenant. The Actix team promises to intervene to uphold that code of conduct. Contribution to the actix-web repo is organized under the terms of the Contributor Covenant. The Actix team promises to intervene to uphold that code of conduct.

View File

@ -234,6 +234,7 @@ where
/// ///
/// * *Resource* is an entry in resource table which corresponds to requested URL. /// * *Resource* is an entry in resource table which corresponds to requested URL.
/// * *Scope* is a set of resources with common root path. /// * *Scope* is a set of resources with common root path.
/// * "StaticFiles" is a service for static files support
pub fn service<F>(mut self, factory: F) -> Self pub fn service<F>(mut self, factory: F) -> Self
where where
F: HttpServiceFactory + 'static, F: HttpServiceFactory + 'static,

View File

@ -6,7 +6,7 @@ use crate::{HttpResponse, ResponseError};
/// General purpose Actix Web error. /// General purpose Actix Web error.
/// ///
/// An Actix Web error is used to carry errors from `std::error` through actix in a convenient way. /// An Actix Web error is used to carry errors from `std::error` through Actix in a convenient way.
/// It can be created through converting errors with `into()`. /// It can be created through converting errors with `into()`.
/// ///
/// Whenever it is created from an external object a response error is created for it that can be /// Whenever it is created from an external object a response error is created for it that can be
@ -14,6 +14,7 @@ use crate::{HttpResponse, ResponseError};
/// you can always get a `ResponseError` reference from it. /// you can always get a `ResponseError` reference from it.
pub struct Error { pub struct Error {
cause: Box<dyn ResponseError>, cause: Box<dyn ResponseError>,
response_mappers: Vec<Box<dyn Fn(HttpResponse) -> HttpResponse>>,
} }
impl Error { impl Error {
@ -29,7 +30,20 @@ impl Error {
/// Shortcut for creating an `HttpResponse`. /// Shortcut for creating an `HttpResponse`.
pub fn error_response(&self) -> HttpResponse { pub fn error_response(&self) -> HttpResponse {
self.cause.error_response() let mut res = self.cause.error_response();
for mapper in &self.response_mappers {
res = (mapper)(res);
}
res
}
pub fn add_mapper<F, B>(&mut self, mapper: F)
where
F: Fn(HttpResponse) -> HttpResponse + 'static,
{
self.response_mappers.push(Box::new(mapper))
} }
} }
@ -56,6 +70,7 @@ impl<T: ResponseError + 'static> From<T> for Error {
fn from(err: T) -> Error { fn from(err: T) -> Error {
Error { Error {
cause: Box::new(err), cause: Box::new(err),
response_mappers: Vec::new(),
} }
} }
} }

View File

@ -100,7 +100,6 @@ impl ResponseError for UrlencodedError {
match self { match self {
Self::Overflow { .. } => StatusCode::PAYLOAD_TOO_LARGE, Self::Overflow { .. } => StatusCode::PAYLOAD_TOO_LARGE,
Self::UnknownLength => StatusCode::LENGTH_REQUIRED, Self::UnknownLength => StatusCode::LENGTH_REQUIRED,
Self::ContentType => StatusCode::UNSUPPORTED_MEDIA_TYPE,
Self::Payload(err) => err.status_code(), Self::Payload(err) => err.status_code(),
_ => StatusCode::BAD_REQUEST, _ => StatusCode::BAD_REQUEST,
} }
@ -233,7 +232,7 @@ mod tests {
let resp = UrlencodedError::UnknownLength.error_response(); let resp = UrlencodedError::UnknownLength.error_response();
assert_eq!(resp.status(), StatusCode::LENGTH_REQUIRED); assert_eq!(resp.status(), StatusCode::LENGTH_REQUIRED);
let resp = UrlencodedError::ContentType.error_response(); let resp = UrlencodedError::ContentType.error_response();
assert_eq!(resp.status(), StatusCode::UNSUPPORTED_MEDIA_TYPE); assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
} }
#[test] #[test]

View File

@ -27,8 +27,7 @@ fn bare_address(val: &str) -> &str {
val.split("]:") val.split("]:")
.next() .next()
.map(|s| s.trim_start_matches('[').trim_end_matches(']')) .map(|s| s.trim_start_matches('[').trim_end_matches(']'))
// this indicates that the IPv6 address is malformed so shouldn't // This shouldn't *actually* ever happen
// usually happen, but if it does, just return the original input
.unwrap_or(val) .unwrap_or(val)
} else { } else {
val.split(':').next().unwrap_or(val) val.split(':').next().unwrap_or(val)

View File

@ -91,35 +91,6 @@ impl HttpRequest {
&self.head().uri &self.head().uri
} }
/// Returns request's original full URL.
///
/// Reconstructed URL is best-effort, using [`connection_info`](HttpRequest::connection_info())
/// to get forwarded scheme & host.
///
/// ```
/// use actix_web::test::TestRequest;
/// let req = TestRequest::with_uri("http://10.1.2.3:8443/api?id=4&name=foo")
/// .insert_header(("host", "example.com"))
/// .to_http_request();
///
/// assert_eq!(
/// req.full_url().as_str(),
/// "http://example.com/api?id=4&name=foo",
/// );
/// ```
pub fn full_url(&self) -> url::Url {
let info = self.connection_info();
let scheme = info.scheme();
let host = info.host();
let path_and_query = self
.uri()
.path_and_query()
.map(|paq| paq.as_str())
.unwrap_or("/");
url::Url::parse(&format!("{scheme}://{host}{path_and_query}")).unwrap()
}
/// Read the Request method. /// Read the Request method.
#[inline] #[inline]
pub fn method(&self) -> &Method { pub fn method(&self) -> &Method {
@ -992,27 +963,4 @@ mod tests {
assert!(format!("{:?}", req).contains(location_header)); assert!(format!("{:?}", req).contains(location_header));
} }
#[test]
fn check_full_url() {
let req = TestRequest::with_uri("/api?id=4&name=foo").to_http_request();
assert_eq!(
req.full_url().as_str(),
"http://localhost:8080/api?id=4&name=foo",
);
let req = TestRequest::with_uri("https://example.com/api?id=4&name=foo").to_http_request();
assert_eq!(
req.full_url().as_str(),
"https://example.com/api?id=4&name=foo",
);
let req = TestRequest::with_uri("http://10.1.2.3:8443/api?id=4&name=foo")
.insert_header(("host", "example.com"))
.to_http_request();
assert_eq!(
req.full_url().as_str(),
"http://example.com/api?id=4&name=foo",
);
}
} }

View File

@ -213,6 +213,7 @@ where
/// ///
/// * *Resource* is an entry in resource table which corresponds to requested URL. /// * *Resource* is an entry in resource table which corresponds to requested URL.
/// * *Scope* is a set of resources with common root path. /// * *Scope* is a set of resources with common root path.
/// * "StaticFiles" is a service for static files support
/// ///
/// ``` /// ```
/// use actix_web::{web, App, HttpRequest}; /// use actix_web::{web, App, HttpRequest};

View File

@ -7,7 +7,13 @@ use std::{
time::Duration, time::Duration,
}; };
#[cfg(feature = "__tls")] #[cfg(any(
feature = "openssl",
feature = "rustls-0_20",
feature = "rustls-0_21",
feature = "rustls-0_22",
feature = "rustls-0_23",
))]
use actix_http::TlsAcceptorConfig; use actix_http::TlsAcceptorConfig;
use actix_http::{body::MessageBody, Extensions, HttpService, KeepAlive, Request, Response}; use actix_http::{body::MessageBody, Extensions, HttpService, KeepAlive, Request, Response};
use actix_server::{Server, ServerBuilder}; use actix_server::{Server, ServerBuilder};
@ -184,7 +190,7 @@ where
/// By default max connections is set to a 256. /// By default max connections is set to a 256.
#[allow(unused_variables)] #[allow(unused_variables)]
pub fn max_connection_rate(self, num: usize) -> Self { pub fn max_connection_rate(self, num: usize) -> Self {
#[cfg(feature = "__tls")] #[cfg(any(feature = "rustls-0_20", feature = "rustls-0_21", feature = "openssl"))]
actix_tls::accept::max_concurrent_tls_connect(num); actix_tls::accept::max_concurrent_tls_connect(num);
self self
} }
@ -237,7 +243,13 @@ where
/// time, the connection is closed. /// time, the connection is closed.
/// ///
/// By default, the handshake timeout is 3 seconds. /// By default, the handshake timeout is 3 seconds.
#[cfg(feature = "__tls")] #[cfg(any(
feature = "openssl",
feature = "rustls-0_20",
feature = "rustls-0_21",
feature = "rustls-0_22",
feature = "rustls-0_23",
))]
pub fn tls_handshake_timeout(self, dur: Duration) -> Self { pub fn tls_handshake_timeout(self, dur: Duration) -> Self {
self.config self.config
.lock() .lock()

View File

@ -1,66 +0,0 @@
//! Semantic HTML responder. See [`Html`].
use crate::{
http::{
header::{self, ContentType, TryIntoHeaderValue},
StatusCode,
},
HttpRequest, HttpResponse, Responder,
};
/// Semantic HTML responder.
///
/// When used as a responder, creates a 200 OK response, sets the correct HTML content type, and
/// uses the string passed to [`Html::new()`] as the body.
///
/// ```
/// # use actix_web::web::Html;
/// Html::new("<p>Hello, World!</p>")
/// # ;
/// ```
#[derive(Debug, Clone, PartialEq, Hash)]
pub struct Html(String);
impl Html {
/// Constructs a new `Html` responder.
pub fn new(html: impl Into<String>) -> Self {
Self(html.into())
}
}
impl Responder for Html {
type Body = String;
fn respond_to(self, _req: &HttpRequest) -> HttpResponse<Self::Body> {
let mut res = HttpResponse::with_body(StatusCode::OK, self.0);
res.headers_mut().insert(
header::CONTENT_TYPE,
ContentType::html().try_into_value().unwrap(),
);
res
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test::TestRequest;
#[test]
fn responder() {
let req = TestRequest::default().to_http_request();
let res = Html::new("<p>Hello, World!</p>");
let res = res.respond_to(&req);
assert!(res.status().is_success());
assert!(res
.headers()
.get(header::CONTENT_TYPE)
.unwrap()
.to_str()
.unwrap()
.starts_with("text/html"));
assert!(res.body().starts_with("<p>"));
}
}

View File

@ -3,7 +3,6 @@
mod either; mod either;
mod form; mod form;
mod header; mod header;
mod html;
mod json; mod json;
mod path; mod path;
mod payload; mod payload;
@ -14,7 +13,6 @@ pub use self::{
either::Either, either::Either,
form::{Form, FormConfig, UrlEncoded}, form::{Form, FormConfig, UrlEncoded},
header::Header, header::Header,
html::Html,
json::{Json, JsonBody, JsonConfig}, json::{Json, JsonBody, JsonConfig},
path::{Path, PathConfig}, path::{Path, PathConfig},
payload::{Payload, PayloadConfig}, payload::{Payload, PayloadConfig},

View File

@ -15,6 +15,10 @@ repository = "https://github.com/actix/actix-web"
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
edition = "2021" edition = "2021"
[lib]
name = "awc"
path = "src/lib.rs"
[package.metadata.docs.rs] [package.metadata.docs.rs]
rustdoc-args = ["--cfg", "docsrs"] rustdoc-args = ["--cfg", "docsrs"]
features = [ features = [
@ -29,27 +33,6 @@ features = [
"compress-zstd", "compress-zstd",
] ]
[package.metadata.cargo_check_external_types]
allowed_external_types = [
"actix_codec::*",
"actix_http::*",
"actix_rt::*",
"actix_service::*",
"actix_tls::*",
"bytes::*",
"cookie::*",
"cookie",
"futures_core::*",
"h2::*",
"http::*",
"openssl::*",
"rustls::*",
"serde_json::*",
"serde_urlencoded::*",
"serde::*",
"tokio::*",
]
[features] [features]
default = ["compress-brotli", "compress-gzip", "compress-zstd", "cookies"] default = ["compress-brotli", "compress-gzip", "compress-zstd", "cookies"]
@ -151,7 +134,7 @@ rcgen = "0.13"
rustls-pemfile = "2" rustls-pemfile = "2"
tokio = { version = "1.24.2", features = ["rt-multi-thread", "macros"] } tokio = { version = "1.24.2", features = ["rt-multi-thread", "macros"] }
zstd = "0.13" zstd = "0.13"
tls-rustls-0_23 = { package = "rustls", version = "0.23" } # add rustls 0.23 with default features to make aws_lc_rs work in tests tls-rustls-0_23 = { package = "rustls", version = "0.23" } # add rustls 0.23 with default features to make aws_lc_rs work in tests
[[example]] [[example]]
name = "client" name = "client"

View File

@ -22,7 +22,7 @@ non_linux_all_features_list := ```
cargo metadata --format-version=1 \ cargo metadata --format-version=1 \
| jq '.packages[] | select(.source == null) | .features | keys' \ | jq '.packages[] | select(.source == null) | .features | keys' \
| jq -r --slurp \ | jq -r --slurp \
--arg exclusions "__tls,__compress,tokio-uring,io-uring,experimental-io-uring" \ --arg exclusions "tokio-uring,io-uring,experimental-io-uring" \
'add | unique | . - ($exclusions | split(",")) | join(",")' 'add | unique | . - ($exclusions | split(",")) | join(",")'
``` ```
@ -32,14 +32,6 @@ all_crate_features := if os() == "linux" {
"--features='" + non_linux_all_features_list + "'" "--features='" + non_linux_all_features_list + "'"
} }
[private]
check-min:
cargo hack --workspace check --no-default-features
[private]
check-default:
cargo hack --workspace check
# Run Clippy over workspace. # Run Clippy over workspace.
clippy toolchain="": clippy toolchain="":
cargo {{ toolchain }} clippy --workspace --all-targets {{ all_crate_features }} cargo {{ toolchain }} clippy --workspace --all-targets {{ all_crate_features }}
@ -61,32 +53,9 @@ test-docs toolchain="": && doc
# Test workspace. # Test workspace.
test-all toolchain="": (test toolchain) (test-docs toolchain) test-all toolchain="": (test toolchain) (test-docs toolchain)
# Test workspace and collect coverage info.
[private]
test-coverage toolchain="":
cargo {{ toolchain }} llvm-cov nextest --no-report {{ all_crate_features }}
cargo {{ toolchain }} llvm-cov --doc --no-report {{ all_crate_features }}
# Test workspace and generate Codecov report.
test-coverage-codecov toolchain="": (test-coverage toolchain)
cargo {{ toolchain }} llvm-cov report --doctests --codecov --output-path=codecov.json
# Test workspace and generate LCOV report.
test-coverage-lcov toolchain="": (test-coverage toolchain)
cargo {{ toolchain }} llvm-cov report --doctests --lcov --output-path=lcov.info
# Document crates in workspace. # Document crates in workspace.
doc *args: && doc-set-workspace-crates doc *args:
RUSTDOCFLAGS="--cfg=docsrs -Dwarnings" cargo +nightly doc --workspace {{ all_crate_features }} {{ args }} RUSTDOCFLAGS="--cfg=docsrs -Dwarnings" cargo +nightly doc --no-deps --workspace {{ all_crate_features }} {{ args }}
[private]
doc-set-workspace-crates:
#!/usr/bin/env bash
(
echo "window.ALL_CRATES ="
cargo metadata --format-version=1 | jq '[.packages[] | select(.source == null) | .name]'
echo ";"
) > "$(cargo metadata --format-version=1 | jq -r '.target_directory')/doc/crates.js"
# Document crates in workspace and watch for changes. # Document crates in workspace and watch for changes.
doc-watch: doc-watch:
@ -96,46 +65,4 @@ doc-watch:
# Update READMEs from crate root documentation. # Update READMEs from crate root documentation.
update-readmes: && fmt update-readmes: && fmt
cd ./actix-files && cargo rdme --force cd ./actix-files && cargo rdme --force
cd ./actix-http-test && cargo rdme --force
cd ./actix-router && cargo rdme --force cd ./actix-router && cargo rdme --force
cd ./actix-multipart && cargo rdme --force
cd ./actix-test && cargo rdme --force
feature_combo_skip_list := if os() == "linux" {
"__tls,__compress"
} else {
"__tls,__compress,experimental-io-uring"
}
# Checks compatibility of feature combinations.
check-feature-combinations:
cargo hack --workspace \
--feature-powerset --depth=4 \
--skip={{ feature_combo_skip_list }} \
check
# 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}}