1
0
mirror of https://github.com/fafhrd91/actix-web synced 2025-07-03 01:34:32 +02:00

Compare commits

...

63 Commits

Author SHA1 Message Date
542200cbc2 update readme 2022-02-25 19:11:46 +00:00
d0c08dbb7d prepare releases: actix-http 3.0.0 and actix-web 4.0.0 (#2663) 2022-02-25 18:46:35 +00:00
d0b5fb18d2 update migration guide on middleware 2022-02-22 17:40:38 +00:00
12fb3412a5 remove concurrency groups 2022-02-22 12:52:07 +00:00
2665357a0c fix ci groups 2022-02-22 12:47:57 +00:00
693271e571 add CI job concurrency groups 2022-02-22 12:41:08 +00:00
10ef9b0751 remove useless doctest main fns 2022-02-22 12:32:06 +00:00
ce00c88963 fix changelog typo 2022-02-22 11:46:51 +00:00
75e6ffb057 prepare actix-router release 0.5.0 (#2658) 2022-02-22 11:38:25 +00:00
ad38973767 move blocking error to web (#2660) 2022-02-22 08:45:28 +00:00
1c1d6477ef remove legacy ws test 2022-02-22 07:11:16 +00:00
53509a5361 ignore all http1 connection headers in h2 2022-02-22 07:07:12 +00:00
a6f27baff1 flesh out Responder docs 2022-02-22 07:07:12 +00:00
218e34ee17 fix http error debug impl 2022-02-22 07:07:12 +00:00
11bfa84926 rename simple_service to status_service (#2659) 2022-02-22 07:06:36 +00:00
5aa6f713c7 update errorhandlers migration guide 2022-02-22 06:23:01 +00:00
151a15da74 prepare actix-http release 3.0.0-rc.4 2022-02-22 00:21:49 +00:00
1ce58ecb30 fix dispatcher panic on pending flush
fixes thread panic in actix-http-3.0.0-rc.3 #2655
2022-02-22 00:19:48 +00:00
f940653981 Edits to the migration notes (#2654) 2022-02-19 17:05:54 +00:00
b291e29882 fix links 2022-02-18 03:41:10 +00:00
f843776f36 Fix links in README (#2653) 2022-02-18 03:34:12 +00:00
52f7d96358 tweak migration document 2022-02-17 19:13:03 +00:00
51e573b888 prepare actix-test release 0.1.0-beta.13 2022-02-16 03:13:41 +00:00
38e015432b prepare actix-http-test release 3.0.0-beta.13 2022-02-16 03:13:22 +00:00
f5895d5eff prepare actix-web-actors release 4.0.0-beta.12 2022-02-16 03:11:22 +00:00
a0c4bf8d1b prepare awc release 3.0.0-beta.21 2022-02-16 03:10:01 +00:00
594e3a6ef1 prepare actix-http release 3.0.0-rc.3 2022-02-16 03:07:12 +00:00
a808a26d8c bump actix-codec to 0.5 2022-02-15 20:49:10 +00:00
de62e8b025 add nextest to post-merge ci 2022-02-15 14:40:26 +00:00
3486edabcf update migrations guide re tokio v1 2022-02-15 00:54:12 +00:00
4c59a34513 Remove clone implementation for Path (#2639) 2022-02-10 10:29:00 +00:00
1b706b3069 update body type migration guide 2022-02-09 16:12:39 +00:00
a9f445875a update migration guide 2022-02-09 12:31:06 +00:00
e0f02c1d9e update migration guide 2022-02-08 16:53:09 +00:00
092dbba5b9 update migration guide 2022-02-08 15:24:35 +00:00
ff4b2d251f fix impl assertions 2022-02-08 14:32:57 +00:00
98faa61afe fix impl assertions 2022-02-08 13:37:01 +00:00
3f2db9e75c fix doc tests 2022-02-08 12:25:13 +00:00
074d18209d better document relationship with tokio 2022-02-08 10:21:47 +00:00
593fbde46a prepare actix-web release 4.0.0-rc.3 2022-02-08 09:31:48 +00:00
161861997c prepare actix-http release 3.0.0-rc.2 2022-02-08 09:31:20 +00:00
3d621677a5 clippy 2022-02-08 08:00:47 +00:00
0c144054cb make Condition generic over body type (#2635)
Co-authored-by: Rob Ede <robjtede@icloud.com>
2022-02-08 07:50:05 +00:00
b0fbe0dfd8 fix workers doc 2022-02-08 06:58:33 +00:00
b653bf557f added note to v4 migration guide about worker thread update (#2634) 2022-02-07 19:04:03 +00:00
1d1a65282f RC refinements (#2625) 2022-02-04 20:37:33 +00:00
b0a363a7ae add migration note about fromrequest::configure 2022-02-04 18:48:22 +00:00
b4d3c2394d clean up migration guide 2022-02-04 18:22:38 +00:00
5ca42df89a fix stuck connection when handler doesn't read payload (#2624) 2022-02-03 07:03:39 +00:00
fc5ecdc30b fix changelog 2022-02-02 03:55:43 +00:00
7fe800c3ff prepare actix-web release 4.0.0-rc.2 2022-02-02 03:54:26 +00:00
075df88a07 update 4.0 migration guide 2022-02-02 03:42:07 +00:00
391d8a744a update 4.0 migratio guide 2022-02-02 03:13:11 +00:00
5b6cb681b9 update 4.0 migration guide 2022-02-02 03:09:33 +00:00
0957ec40b4 split migration file 2022-02-02 02:46:37 +00:00
ccf430d74a disable coverage job 2022-02-01 15:24:35 +00:00
c84c1f0f15 simplify macros feature 2022-02-01 14:39:49 +00:00
e9279dfbb8 Fix deprecated notice about client_shutdown (#2621) 2022-02-01 13:44:56 +00:00
a68239adaa bump zstd to 0.10 2022-02-01 13:35:32 +00:00
40a4b1ccd5 add macro feature (#2619)
Co-authored-by: Ibraheem Ahmed <ibrah1440@gmail.com>
2022-02-01 02:35:05 +00:00
7f5a8c0851 fix vmanifest 2022-02-01 00:33:41 +00:00
bcdde1d4ea move actix-web to own dir 2022-02-01 00:30:41 +00:00
30aa64ea32 update dep graphs 2022-02-01 00:23:58 +00:00
182 changed files with 4388 additions and 2629 deletions

View File

@ -6,9 +6,12 @@ lint-all = "clippy --workspace --all-features --tests --examples --bins -- -Dcli
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 --skip=__compress,io-uring check"
ci-check-all-feature-powerset="hack --workspace --feature-powerset --skip=__compress,experimental-io-uring check"
ci-check-all-feature-powerset-linux="hack --workspace --feature-powerset --skip=__compress check"
# testing
ci-doctest-default = "test --workspace --doc --no-fail-fast -- --nocapture"
ci-doctest = "test --workspace --all-features --doc --no-fail-fast -- --nocapture"
# compile docs as docs.rs would
# RUSTDOCFLAGS="--cfg=docsrs" cargo +nightly doc --no-deps --workspace

View File

@ -1,4 +1,4 @@
name: CI (master only)
name: CI (post-merge)
on:
push:
@ -125,16 +125,44 @@ jobs:
uses: actions-rs/cargo@v1
with: { command: ci-check-all-feature-powerset-linux }
coverage:
name: coverage
# job currently (1st Feb 2022) segfaults
# coverage:
# name: coverage
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v2
# - name: Install stable
# uses: actions-rs/toolchain@v1
# with:
# toolchain: stable-x86_64-unknown-linux-gnu
# 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: Generate coverage file
# run: |
# cargo install cargo-tarpaulin --vers "^0.13"
# cargo tarpaulin --workspace --features=rustls,openssl --out Xml --verbose
# - name: Upload to Codecov
# uses: codecov/codecov-action@v1
# with: { file: cobertura.xml }
nextest:
name: nextest
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install stable
- name: Install Rust
uses: actions-rs/toolchain@v1
with:
toolchain: stable-x86_64-unknown-linux-gnu
toolchain: stable
profile: minimal
override: true
@ -142,12 +170,16 @@ jobs:
uses: actions-rs/cargo@v1
with: { command: generate-lockfile }
- name: Cache Dependencies
uses: Swatinem/rust-cache@v1.2.0
uses: Swatinem/rust-cache@v1.3.0
- name: Generate coverage file
run: |
cargo install cargo-tarpaulin --vers "^0.13"
cargo tarpaulin --workspace --features=rustls,openssl --out Xml --verbose
- name: Upload to Codecov
uses: codecov/codecov-action@v1
with: { file: cobertura.xml }
- name: Install cargo-nextest
uses: actions-rs/cargo@v1
with:
command: install
args: cargo-nextest
- name: Test with cargo-nextest
uses: actions-rs/cargo@v1
with:
command: nextest
args: run

3
.prettierrc.json Normal file
View File

@ -0,0 +1,3 @@
{
"proseWrap": "never"
}

1033
CHANGES.md

File diff suppressed because it is too large Load Diff

View File

@ -1,36 +1,6 @@
[package]
name = "actix-web"
version = "4.0.0-rc.1"
authors = [
"Nikolay Kim <fafhrd91@gmail.com>",
"Rob Ede <robjtede@icloud.com>",
]
description = "Actix Web is a powerful, pragmatic, and extremely fast web framework for Rust"
keywords = ["actix", "http", "web", "framework", "async"]
categories = [
"network-programming",
"asynchronous",
"web-programming::http-server",
"web-programming::websocket"
]
homepage = "https://actix.rs"
repository = "https://github.com/actix/actix-web.git"
license = "MIT OR Apache-2.0"
edition = "2018"
[package.metadata.docs.rs]
# features that docs.rs will build with
features = ["openssl", "rustls", "compress-brotli", "compress-gzip", "compress-zstd", "cookies", "secure-cookies"]
rustdoc-args = ["--cfg", "docsrs"]
[lib]
name = "actix_web"
path = "src/lib.rs"
[workspace]
resolver = "2"
members = [
".",
"actix-files",
"actix-http-test",
"actix-http",
@ -39,93 +9,10 @@ members = [
"actix-test",
"actix-web-actors",
"actix-web-codegen",
"actix-web",
"awc",
]
[features]
default = ["compress-brotli", "compress-gzip", "compress-zstd", "cookies"]
# Brotli algorithm content-encoding support
compress-brotli = ["actix-http/compress-brotli", "__compress"]
# Gzip and deflate algorithms content-encoding support
compress-gzip = ["actix-http/compress-gzip", "__compress"]
# Zstd algorithm content-encoding support
compress-zstd = ["actix-http/compress-zstd", "__compress"]
# support for cookies
cookies = ["cookie"]
# secure cookies feature
secure-cookies = ["cookie/secure"]
# openssl
openssl = ["actix-http/openssl", "actix-tls/accept", "actix-tls/openssl"]
# rustls
rustls = ["actix-http/rustls", "actix-tls/accept", "actix-tls/rustls"]
# Internal (PRIVATE!) features used to aid testing and checking feature status.
# Don't rely on these whatsoever. They may disappear at anytime.
__compress = []
# io-uring feature only avaiable for Linux OSes.
experimental-io-uring = ["actix-server/io-uring"]
[dependencies]
actix-codec = "0.4.1"
actix-macros = "0.2.3"
actix-rt = "2.6"
actix-server = "2"
actix-service = "2.0.0"
actix-utils = "3.0.0"
actix-tls = { version = "3.0.0", default-features = false, optional = true }
actix-http = { version = "3.0.0-rc.1", features = ["http2", "ws"] }
actix-router = "0.5.0-rc.3"
actix-web-codegen = "0.5.0-rc.2"
ahash = "0.7"
bytes = "1"
cfg-if = "1"
cookie = { version = "0.16", features = ["percent-encode"], optional = true }
derive_more = "0.99.5"
encoding_rs = "0.8"
futures-core = { version = "0.3.7", default-features = false }
futures-util = { version = "0.3.7", default-features = false }
itoa = "1"
language-tags = "0.3"
once_cell = "1.5"
log = "0.4"
mime = "0.3"
pin-project-lite = "0.2.7"
regex = "1.4"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
serde_urlencoded = "0.7"
smallvec = "1.6.1"
socket2 = "0.4.0"
time = { version = "0.3", default-features = false, features = ["formatting"] }
url = "2.1"
[dev-dependencies]
actix-files = "0.6.0-beta.16"
actix-test = { version = "0.1.0-beta.12", features = ["openssl", "rustls"] }
awc = { version = "3.0.0-beta.20", features = ["openssl"] }
brotli = "3.3.3"
const-str = "0.3"
criterion = { version = "0.3", features = ["html_reports"] }
env_logger = "0.9"
flate2 = "1.0.13"
futures-util = { version = "0.3.7", default-features = false, features = ["std"] }
rand = "0.8"
rcgen = "0.8"
rustls-pemfile = "0.2"
static_assertions = "1"
tls-openssl = { package = "openssl", version = "0.10.9" }
tls-rustls = { package = "rustls", version = "0.20.0" }
zstd = "0.9"
[profile.dev]
# Disabling debug info speeds up builds a bunch and we don't rely on it for debugging that much.
debug = 0
@ -142,7 +29,7 @@ actix-http-test = { path = "actix-http-test" }
actix-multipart = { path = "actix-multipart" }
actix-router = { path = "actix-router" }
actix-test = { path = "actix-test" }
actix-web = { path = "." }
actix-web = { path = "actix-web" }
actix-web-actors = { path = "actix-web-actors" }
actix-web-codegen = { path = "actix-web-codegen" }
awc = { path = "awc" }
@ -155,35 +42,3 @@ awc = { path = "awc" }
# actix-utils = { path = "../actix-net/actix-utils" }
# actix-tls = { path = "../actix-net/actix-tls" }
# actix-server = { path = "../actix-net/actix-server" }
[[test]]
name = "test_server"
required-features = ["compress-brotli", "compress-gzip", "compress-zstd", "cookies"]
[[test]]
name = "compression"
required-features = ["compress-brotli", "compress-gzip", "compress-zstd"]
[[example]]
name = "basic"
required-features = ["compress-gzip"]
[[example]]
name = "uds"
required-features = ["compress-gzip"]
[[example]]
name = "on-connect"
required-features = []
[[bench]]
name = "server"
harness = false
[[bench]]
name = "service"
harness = false
[[bench]]
name = "responder"
harness = false

View File

@ -1,677 +0,0 @@
## Unreleased
- The default `NormalizePath` behavior now strips trailing slashes by default. This was
previously documented to be the case in v3 but the behavior now matches. The effect is that
routes defined with trailing slashes will become inaccessible when
using `NormalizePath::default()`. As such, calling `NormalizePath::default()` will log a warning.
It is advised that the `new` method be used instead.
Before: `#[get("/test/")]`
After: `#[get("/test")]`
Alternatively, explicitly require trailing slashes: `NormalizePath::new(TrailingSlash::Always)`.
- The `type Config` of `FromRequest` was removed.
- Feature flag `compress` has been split into its supported algorithm (brotli, gzip, zstd).
By default all compression algorithms are enabled.
To select algorithm you want to include with `middleware::Compress` use following flags:
- `compress-brotli`
- `compress-gzip`
- `compress-zstd`
If you have set in your `Cargo.toml` dedicated `actix-web` features and you still want
to have compression enabled. Please change features selection like bellow:
Before: `"compress"`
After: `"compress-brotli", "compress-gzip", "compress-zstd"`
## 3.0.0
- The return type for `ServiceRequest::app_data::<T>()` was changed from returning a `Data<T>` to
simply a `T`. To access a `Data<T>` use `ServiceRequest::app_data::<Data<T>>()`.
- Cookie handling has been offloaded to the `cookie` crate:
* `USERINFO_ENCODE_SET` is no longer exposed. Percent-encoding is still supported; check docs.
* Some types now require lifetime parameters.
- The time crate was updated to `v0.2`, a major breaking change to the time crate, which affects
any `actix-web` method previously expecting a time v0.1 input.
- Setting a cookie's SameSite property, explicitly, to `SameSite::None` will now
result in `SameSite=None` being sent with the response Set-Cookie header.
To create a cookie without a SameSite attribute, remove any calls setting same_site.
- actix-http support for Actors messages was moved to actix-http crate and is enabled
with feature `actors`
- content_length function is removed from actix-http.
You can set Content-Length by normally setting the response body or calling no_chunking function.
- `BodySize::Sized64` variant has been removed. `BodySize::Sized` now receives a
`u64` instead of a `usize`.
- Code that was using `path.<index>` to access a `web::Path<(A, B, C)>`s elements now needs to use
destructuring or `.into_inner()`. For example:
```rust
// Previously:
async fn some_route(path: web::Path<(String, String)>) -> String {
format!("Hello, {} {}", path.0, path.1)
}
// Now (this also worked before):
async fn some_route(path: web::Path<(String, String)>) -> String {
let (first_name, last_name) = path.into_inner();
format!("Hello, {} {}", first_name, last_name)
}
// Or (this wasn't previously supported):
async fn some_route(web::Path((first_name, last_name)): web::Path<(String, String)>) -> String {
format!("Hello, {} {}", first_name, last_name)
}
```
- `middleware::NormalizePath` can now also be configured to trim trailing slashes instead of always keeping one.
It will need `middleware::normalize::TrailingSlash` when being constructed with `NormalizePath::new(...)`,
or for an easier migration you can replace `wrap(middleware::NormalizePath)` with `wrap(middleware::NormalizePath::new(TrailingSlash::MergeOnly))`.
- `HttpServer::maxconn` is renamed to the more expressive `HttpServer::max_connections`.
- `HttpServer::maxconnrate` is renamed to the more expressive `HttpServer::max_connection_rate`.
## 2.0.0
- `HttpServer::start()` renamed to `HttpServer::run()`. It also possible to
`.await` on `run` method result, in that case it awaits server exit.
- `App::register_data()` renamed to `App::app_data()` and accepts any type `T: 'static`.
Stored data is available via `HttpRequest::app_data()` method at runtime.
- Extractor configuration must be registered with `App::app_data()` instead of `App::data()`
- Sync handlers has been removed. `.to_async()` method has been renamed to `.to()`
replace `fn` with `async fn` to convert sync handler to async
- `actix_http_test::TestServer` moved to `actix_web::test` module. To start
test server use `test::start()` or `test_start_with_config()` methods
- `ResponseError` trait has been reafctored. `ResponseError::error_response()` renders
http response.
- Feature `rust-tls` renamed to `rustls`
instead of
```rust
actix-web = { version = "2.0.0", features = ["rust-tls"] }
```
use
```rust
actix-web = { version = "2.0.0", features = ["rustls"] }
```
- Feature `ssl` renamed to `openssl`
instead of
```rust
actix-web = { version = "2.0.0", features = ["ssl"] }
```
use
```rust
actix-web = { version = "2.0.0", features = ["openssl"] }
```
- `Cors` builder now requires that you call `.finish()` to construct the middleware
## 1.0.1
- Cors middleware has been moved to `actix-cors` crate
instead of
```rust
use actix_web::middleware::cors::Cors;
```
use
```rust
use actix_cors::Cors;
```
- Identity middleware has been moved to `actix-identity` crate
instead of
```rust
use actix_web::middleware::identity::{Identity, CookieIdentityPolicy, IdentityService};
```
use
```rust
use actix_identity::{Identity, CookieIdentityPolicy, IdentityService};
```
## 1.0.0
- Extractor configuration. In version 1.0 this is handled with the new `Data` mechanism for both setting and retrieving the configuration
instead of
```rust
#[derive(Default)]
struct ExtractorConfig {
config: String,
}
impl FromRequest for YourExtractor {
type Config = ExtractorConfig;
type Result = Result<YourExtractor, Error>;
fn from_request(req: &HttpRequest, cfg: &Self::Config) -> Self::Result {
println!("use the config: {:?}", cfg.config);
...
}
}
App::new().resource("/route_with_config", |r| {
r.post().with_config(handler_fn, |cfg| {
cfg.0.config = "test".to_string();
})
})
```
use the HttpRequest to get the configuration like any other `Data` with `req.app_data::<C>()` and set it with the `data()` method on the `resource`
```rust
#[derive(Default)]
struct ExtractorConfig {
config: String,
}
impl FromRequest for YourExtractor {
type Error = Error;
type Future = Result<Self, Self::Error>;
type Config = ExtractorConfig;
fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {
let cfg = req.app_data::<ExtractorConfig>();
println!("config data?: {:?}", cfg.unwrap().role);
...
}
}
App::new().service(
resource("/route_with_config")
.data(ExtractorConfig {
config: "test".to_string(),
})
.route(post().to(handler_fn)),
)
```
- Resource registration. 1.0 version uses generalized resource
registration via `.service()` method.
instead of
```rust
App.new().resource("/welcome", |r| r.f(welcome))
```
use App's or Scope's `.service()` method. `.service()` method accepts
object that implements `HttpServiceFactory` trait. By default
actix-web provides `Resource` and `Scope` services.
```rust
App.new().service(
web::resource("/welcome")
.route(web::get().to(welcome))
.route(web::post().to(post_handler))
```
- Scope registration.
instead of
```rust
let app = App::new().scope("/{project_id}", |scope| {
scope
.resource("/path1", |r| r.f(|_| HttpResponse::Ok()))
.resource("/path2", |r| r.f(|_| HttpResponse::Ok()))
.resource("/path3", |r| r.f(|_| HttpResponse::MethodNotAllowed()))
});
```
use `.service()` for registration and `web::scope()` as scope object factory.
```rust
let app = App::new().service(
web::scope("/{project_id}")
.service(web::resource("/path1").to(|| HttpResponse::Ok()))
.service(web::resource("/path2").to(|| HttpResponse::Ok()))
.service(web::resource("/path3").to(|| HttpResponse::MethodNotAllowed()))
);
```
- `.with()`, `.with_async()` registration methods have been renamed to `.to()` and `.to_async()`.
instead of
```rust
App.new().resource("/welcome", |r| r.with(welcome))
```
use `.to()` or `.to_async()` methods
```rust
App.new().service(web::resource("/welcome").to(welcome))
```
- Passing arguments to handler with extractors, multiple arguments are allowed
instead of
```rust
fn welcome((body, req): (Bytes, HttpRequest)) -> ... {
...
}
```
use multiple arguments
```rust
fn welcome(body: Bytes, req: HttpRequest) -> ... {
...
}
```
- `.f()`, `.a()` and `.h()` handler registration methods have been removed.
Use `.to()` for handlers and `.to_async()` for async handlers. Handler function
must use extractors.
instead of
```rust
App.new().resource("/welcome", |r| r.f(welcome))
```
use App's `to()` or `to_async()` methods
```rust
App.new().service(web::resource("/welcome").to(welcome))
```
- `HttpRequest` does not provide access to request's payload stream.
instead of
```rust
fn index(req: &HttpRequest) -> Box<Future<Item=HttpResponse, Error=Error>> {
req
.payload()
.from_err()
.fold((), |_, chunk| {
...
})
.map(|_| HttpResponse::Ok().finish())
.responder()
}
```
use `Payload` extractor
```rust
fn index(stream: web::Payload) -> impl Future<Item=HttpResponse, Error=Error> {
stream
.from_err()
.fold((), |_, chunk| {
...
})
.map(|_| HttpResponse::Ok().finish())
}
```
- `State` is now `Data`. You register Data during the App initialization process
and then access it from handlers either using a Data extractor or using
HttpRequest's api.
instead of
```rust
App.with_state(T)
```
use App's `data` method
```rust
App.new()
.data(T)
```
and either use the Data extractor within your handler
```rust
use actix_web::web::Data;
fn endpoint_handler(Data<T>)){
...
}
```
.. or access your Data element from the HttpRequest
```rust
fn endpoint_handler(req: HttpRequest) {
let data: Option<Data<T>> = req.app_data::<T>();
}
```
- AsyncResponder is removed, use `.to_async()` registration method and `impl Future<>` as result type.
instead of
```rust
use actix_web::AsyncResponder;
fn endpoint_handler(...) -> impl Future<Item=HttpResponse, Error=Error>{
...
.responder()
}
```
.. simply omit AsyncResponder and the corresponding responder() finish method
- Middleware
instead of
```rust
let app = App::new()
.middleware(middleware::Logger::default())
```
use `.wrap()` method
```rust
let app = App::new()
.wrap(middleware::Logger::default())
.route("/index.html", web::get().to(index));
```
- `HttpRequest::body()`, `HttpRequest::urlencoded()`, `HttpRequest::json()`, `HttpRequest::multipart()`
method have been removed. Use `Bytes`, `String`, `Form`, `Json`, `Multipart` extractors instead.
instead of
```rust
fn index(req: &HttpRequest) -> Responder {
req.body()
.and_then(|body| {
...
})
}
```
use
```rust
fn index(body: Bytes) -> Responder {
...
}
```
- `actix_web::server` module has been removed. To start http server use `actix_web::HttpServer` type
- StaticFiles and NamedFile have been moved to a separate crate.
instead of `use actix_web::fs::StaticFile`
use `use actix_files::Files`
instead of `use actix_web::fs::Namedfile`
use `use actix_files::NamedFile`
- Multipart has been moved to a separate crate.
instead of `use actix_web::multipart::Multipart`
use `use actix_multipart::Multipart`
- Response compression is not enabled by default.
To enable, use `Compress` middleware, `App::new().wrap(Compress::default())`.
- Session middleware moved to actix-session crate
- Actors support have been moved to `actix-web-actors` crate
- Custom Error
Instead of error_response method alone, ResponseError now provides two methods: error_response and render_response respectively. Where, error_response creates the error response and render_response returns the error response to the caller.
Simplest migration from 0.7 to 1.0 shall include below method to the custom implementation of ResponseError:
```rust
fn render_response(&self) -> HttpResponse {
self.error_response()
}
```
## 0.7.15
- The `' '` character is not percent decoded anymore before matching routes. If you need to use it in
your routes, you should use `%20`.
instead of
```rust
fn main() {
let app = App::new().resource("/my index", |r| {
r.method(http::Method::GET)
.with(index);
});
}
```
use
```rust
fn main() {
let app = App::new().resource("/my%20index", |r| {
r.method(http::Method::GET)
.with(index);
});
}
```
- If you used `AsyncResult::async` you need to replace it with `AsyncResult::future`
## 0.7.4
- `Route::with_config()`/`Route::with_async_config()` always passes configuration objects as tuple
even for handler with one parameter.
## 0.7
- `HttpRequest` does not implement `Stream` anymore. If you need to read request payload
use `HttpMessage::payload()` method.
instead of
```rust
fn index(req: HttpRequest) -> impl Responder {
req
.from_err()
.fold(...)
....
}
```
use `.payload()`
```rust
fn index(req: HttpRequest) -> impl Responder {
req
.payload() // <- get request payload stream
.from_err()
.fold(...)
....
}
```
- [Middleware](https://actix.rs/actix-web/actix_web/middleware/trait.Middleware.html)
trait uses `&HttpRequest` instead of `&mut HttpRequest`.
- Removed `Route::with2()` and `Route::with3()` use tuple of extractors instead.
instead of
```rust
fn index(query: Query<..>, info: Json<MyStruct) -> impl Responder {}
```
use tuple of extractors and use `.with()` for registration:
```rust
fn index((query, json): (Query<..>, Json<MyStruct)) -> impl Responder {}
```
- `Handler::handle()` uses `&self` instead of `&mut self`
- `Handler::handle()` accepts reference to `HttpRequest<_>` instead of value
- Removed deprecated `HttpServer::threads()`, use
[HttpServer::workers()](https://actix.rs/actix-web/actix_web/server/struct.HttpServer.html#method.workers) instead.
- Renamed `client::ClientConnectorError::Connector` to
`client::ClientConnectorError::Resolver`
- `Route::with()` does not return `ExtractorConfig`, to configure
extractor use `Route::with_config()`
instead of
```rust
fn main() {
let app = App::new().resource("/index.html", |r| {
r.method(http::Method::GET)
.with(index)
.limit(4096); // <- limit size of the payload
});
}
```
use
```rust
fn main() {
let app = App::new().resource("/index.html", |r| {
r.method(http::Method::GET)
.with_config(index, |cfg| { // <- register handler
cfg.limit(4096); // <- limit size of the payload
})
});
}
```
- `Route::with_async()` does not return `ExtractorConfig`, to configure
extractor use `Route::with_async_config()`
## 0.6
- `Path<T>` extractor return `ErrorNotFound` on failure instead of `ErrorBadRequest`
- `ws::Message::Close` now includes optional close reason.
`ws::CloseCode::Status` and `ws::CloseCode::Empty` have been removed.
- `HttpServer::threads()` renamed to `HttpServer::workers()`.
- `HttpServer::start_ssl()` and `HttpServer::start_tls()` deprecated.
Use `HttpServer::bind_ssl()` and `HttpServer::bind_tls()` instead.
- `HttpRequest::extensions()` returns read only reference to the request's Extension
`HttpRequest::extensions_mut()` returns mutable reference.
- Instead of
`use actix_web::middleware::{
CookieSessionBackend, CookieSessionError, RequestSession,
Session, SessionBackend, SessionImpl, SessionStorage};`
use `actix_web::middleware::session`
`use actix_web::middleware::session{CookieSessionBackend, CookieSessionError,
RequestSession, Session, SessionBackend, SessionImpl, SessionStorage};`
- `FromRequest::from_request()` accepts mutable reference to a request
- `FromRequest::Result` has to implement `Into<Reply<Self>>`
- [`Responder::respond_to()`](
https://actix.rs/actix-web/actix_web/trait.Responder.html#tymethod.respond_to)
is generic over `S`
- Use `Query` extractor instead of HttpRequest::query()`.
```rust
fn index(q: Query<HashMap<String, String>>) -> Result<..> {
...
}
```
or
```rust
let q = Query::<HashMap<String, String>>::extract(req);
```
- Websocket operations are implemented as `WsWriter` trait.
you need to use `use actix_web::ws::WsWriter`
## 0.5
- `HttpResponseBuilder::body()`, `.finish()`, `.json()`
methods return `HttpResponse` instead of `Result<HttpResponse>`
- `actix_web::Method`, `actix_web::StatusCode`, `actix_web::Version`
moved to `actix_web::http` module
- `actix_web::header` moved to `actix_web::http::header`
- `NormalizePath` moved to `actix_web::http` module
- `HttpServer` moved to `actix_web::server`, added new `actix_web::server::new()` function,
shortcut for `actix_web::server::HttpServer::new()`
- `DefaultHeaders` middleware does not use separate builder, all builder methods moved to type itself
- `StaticFiles::new()`'s show_index parameter removed, use `show_files_listing()` method instead.
- `CookieSessionBackendBuilder` removed, all methods moved to `CookieSessionBackend` type
- `actix_web::httpcodes` module is deprecated, `HttpResponse::Ok()`, `HttpResponse::Found()` and other `HttpResponse::XXX()`
functions should be used instead
- `ClientRequestBuilder::body()` returns `Result<_, actix_web::Error>`
instead of `Result<_, http::Error>`
- `Application` renamed to a `App`
- `actix_web::Reply`, `actix_web::Resource` moved to `actix_web::dev`

105
README.md
View File

@ -1,105 +0,0 @@
<div align="center">
<h1>Actix Web</h1>
<p>
<strong>Actix Web is a powerful, pragmatic, and extremely fast web framework for Rust</strong>
</p>
<p>
[![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.0.0-rc.1)](https://docs.rs/actix-web/4.0.0-rc.1)
![MSRV](https://img.shields.io/badge/rustc-1.54+-ab6000.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.0.0-rc.1/status.svg)](https://deps.rs/crate/actix-web/4.0.0-rc.1)
<br />
[![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)
![downloads](https://img.shields.io/crates/d/actix-web.svg)
[![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x)
</p>
</div>
## Features
- Supports _HTTP/1.x_ and _HTTP/2_
- Streaming and pipelining
- Powerful [request routing](https://actix.rs/docs/url-dispatch/) with optional macros
- Full [Tokio](https://tokio.rs) compatibility
- Keep-alive and slow requests handling
- Client/server [WebSockets](https://actix.rs/docs/websockets/) support
- Transparent content compression/decompression (br, gzip, deflate, zstd)
- Multipart streams
- Static assets
- SSL support using OpenSSL or Rustls
- Middlewares ([Logger, Session, CORS, etc](https://actix.rs/docs/middleware/))
- Includes an async [HTTP client](https://docs.rs/awc/)
- Runs on stable Rust 1.54+
## Documentation
- [Website & User Guide](https://actix.rs)
- [Examples Repository](https://github.com/actix/examples)
- [API Documentation](https://docs.rs/actix-web)
- [API Documentation (master branch)](https://actix.rs/actix-web/actix_web)
## Example
Dependencies:
```toml
[dependencies]
actix-web = "4.0.0-rc.1"
```
Code:
```rust
use actix_web::{get, web, App, HttpServer, Responder};
#[get("/{id}/{name}/index.html")]
async fn index(params: web::Path<(u32, String)>) -> impl Responder {
let (id, name) = params.into_inner();
format!("Hello {}! id:{}", name, id)
}
#[actix_web::main] // or #[tokio::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| App::new().service(index))
.bind(("127.0.0.1", 8080))?
.run()
.await
}
```
### More examples
- [Basic Setup](https://github.com/actix/examples/tree/master/basics/basics/)
- [Application State](https://github.com/actix/examples/tree/master/basics/state/)
- [JSON Handling](https://github.com/actix/examples/tree/master/json/json/)
- [Multipart Streams](https://github.com/actix/examples/tree/master/forms/multipart/)
- [Diesel Integration](https://github.com/actix/examples/tree/master/database_interactions/diesel/)
- [r2d2 Integration](https://github.com/actix/examples/tree/master/database_interactions/r2d2/)
- [Simple WebSocket](https://github.com/actix/examples/tree/master/websockets/websocket/)
- [Tera Templates](https://github.com/actix/examples/tree/master/template_engines/tera/)
- [Askama Templates](https://github.com/actix/examples/tree/master/template_engines/askama/)
- [HTTPS using Rustls](https://github.com/actix/examples/tree/master/security/rustls/)
- [HTTPS using OpenSSL](https://github.com/actix/examples/tree/master/security/openssl/)
- [WebSocket Chat](https://github.com/actix/examples/tree/master/websockets/chat/)
You may consider checking out [this directory](https://github.com/actix/examples/tree/master/) for more examples.
## Benchmarks
One of the fastest web frameworks available according to the [TechEmpower Framework Benchmark](https://www.techempower.com/benchmarks/#section=data-r20&test=composite).
## License
This project is licensed under either of the following licenses, at your option:
- Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or [http://www.apache.org/licenses/LICENSE-2.0])
- MIT license ([LICENSE-MIT](LICENSE-MIT) or [http://opensource.org/licenses/MIT])
## 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.

1
README.md Symbolic link
View File

@ -0,0 +1 @@
actix-web/README.md

View File

@ -22,10 +22,10 @@ path = "src/lib.rs"
experimental-io-uring = ["actix-web/experimental-io-uring", "tokio-uring"]
[dependencies]
actix-http = "3.0.0-rc.1"
actix-http = "3.0.0"
actix-service = "2"
actix-utils = "3"
actix-web = { version = "4.0.0-rc.1", default-features = false }
actix-web = { version = "4.0.0", default-features = false }
askama_escape = "0.10"
bitflags = "1"
@ -43,6 +43,6 @@ tokio-uring = { version = "0.2", optional = true, features = ["bytes"] }
[dev-dependencies]
actix-rt = "2.2"
actix-test = "0.1.0-beta.12"
actix-web = "4.0.0-rc.1"
actix-test = "0.1.0-beta.13"
actix-web = "4.0.0"
tempfile = "3.2"

View File

@ -13,6 +13,6 @@
## Documentation & Resources
- [API Documentation](https://docs.rs/actix-files/)
- [Example Project](https://github.com/actix/examples/tree/master/basics/static_index)
- [API Documentation](https://docs.rs/actix-files)
- [Example Project](https://github.com/actix/examples/tree/master/basics/static-files)
- Minimum Supported Rust Version (MSRV): 1.54

View File

@ -81,7 +81,7 @@ async fn chunked_read_file_callback(
) -> Result<(File, Bytes), Error> {
use io::{Read as _, Seek as _};
let res = actix_web::rt::task::spawn_blocking(move || {
let res = actix_web::web::block(move || {
let mut buf = Vec::with_capacity(max_bytes);
file.seek(io::SeekFrom::Start(offset))?;
@ -94,8 +94,7 @@ async fn chunked_read_file_callback(
Ok((file, Bytes::from(buf)))
}
})
.await
.map_err(|_| actix_web::error::BlockingError)??;
.await??;
Ok(res)
}

View File

@ -75,7 +75,7 @@ pub(crate) fn directory_listing(
if dir.is_visible(&entry) {
let entry = entry.unwrap();
let p = match entry.path().strip_prefix(&dir.path) {
Ok(p) if cfg!(windows) => base.join(p).to_string_lossy().replace("\\", "/"),
Ok(p) if cfg!(windows) => base.join(p).to_string_lossy().replace('\\', "/"),
Ok(p) => base.join(p).to_string_lossy().into_owned(),
Err(_) => continue,
};

View File

@ -96,18 +96,18 @@ impl NamedFile {
///
/// # Examples
/// ```ignore
/// use std::{
/// io::{self, Write as _},
/// env,
/// fs::File
/// };
/// use actix_files::NamedFile;
/// use std::io::{self, Write};
/// use std::env;
/// use std::fs::File;
///
/// fn main() -> io::Result<()> {
/// let mut file = File::create("foo.txt")?;
/// file.write_all(b"Hello, world!")?;
/// let named_file = NamedFile::from_file(file, "bar.txt")?;
/// # std::fs::remove_file("foo.txt");
/// Ok(())
/// }
/// let mut file = File::create("foo.txt")?;
/// file.write_all(b"Hello, world!")?;
/// let named_file = NamedFile::from_file(file, "bar.txt")?;
/// # std::fs::remove_file("foo.txt");
/// Ok(())
/// ```
pub fn from_file<P: AsRef<Path>>(file: File, path: P) -> io::Result<NamedFile> {
let path = path.as_ref().to_path_buf();

View File

@ -3,6 +3,10 @@
## Unreleased - 2021-xx-xx
## 3.0.0-beta.13 - 2022-02-16
- No significant changes since `3.0.0-beta.12`.
## 3.0.0-beta.12 - 2022-01-31
- No significant changes since `3.0.0-beta.11`.

View File

@ -1,6 +1,6 @@
[package]
name = "actix-http-test"
version = "3.0.0-beta.12"
version = "3.0.0-beta.13"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Various helpers for Actix applications to use during testing"
keywords = ["http", "web", "framework", "async", "futures"]
@ -30,12 +30,12 @@ openssl = ["tls-openssl", "awc/openssl"]
[dependencies]
actix-service = "2.0.0"
actix-codec = "0.4.1"
actix-tls = "3.0.0"
actix-codec = "0.5"
actix-tls = "3"
actix-utils = "3.0.0"
actix-rt = "2.2"
actix-server = "2"
awc = { version = "3.0.0-beta.20", default-features = false }
awc = { version = "3.0.0-beta.21", default-features = false }
base64 = "0.13"
bytes = "1"
@ -51,5 +51,5 @@ tls-openssl = { version = "0.10.9", package = "openssl", optional = true }
tokio = { version = "1.8.4", features = ["sync"] }
[dev-dependencies]
actix-web = { version = "4.0.0-rc.1", default-features = false, features = ["cookies"] }
actix-http = "3.0.0-rc.1"
actix-web = { version = "4.0.0", default-features = false, features = ["cookies"] }
actix-http = "3.0.0"

View File

@ -3,11 +3,11 @@
> Various helpers for Actix applications to use during testing.
[![crates.io](https://img.shields.io/crates/v/actix-http-test?label=latest)](https://crates.io/crates/actix-http-test)
[![Documentation](https://docs.rs/actix-http-test/badge.svg?version=3.0.0-beta.12)](https://docs.rs/actix-http-test/3.0.0-beta.12)
[![Documentation](https://docs.rs/actix-http-test/badge.svg?version=3.0.0-beta.13)](https://docs.rs/actix-http-test/3.0.0-beta.13)
[![Version](https://img.shields.io/badge/rustc-1.54+-ab6000.svg)](https://blog.rust-lang.org/2021/05/06/Rust-1.54.0.html)
![MIT or Apache 2.0 licensed](https://img.shields.io/crates/l/actix-http-test)
<br>
[![Dependency Status](https://deps.rs/crate/actix-http-test/3.0.0-beta.12/status.svg)](https://deps.rs/crate/actix-http-test/3.0.0-beta.12)
[![Dependency Status](https://deps.rs/crate/actix-http-test/3.0.0-beta.13/status.svg)](https://deps.rs/crate/actix-http-test/3.0.0-beta.13)
[![Download](https://img.shields.io/crates/d/actix-http-test.svg)](https://crates.io/crates/actix-http-test)
[![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x)

View File

@ -3,6 +3,315 @@
## Unreleased - 2021-xx-xx
## 3.0.0 - 2022-02-25
### Dependencies
- Updated `actix-*` to Tokio v1-based versions. [#1813]
- Updated `bytes` to `1.0`. [#1813]
- Updated `h2` to `0.3`. [#1813]
- Updated `rustls` to `0.20.0`. [#2414]
- Updated `language-tags` to `0.3`.
- Updated `tokio` to `1`.
### Added
- Crate Features:
- `ws`; disabled by default. [#2618]
- `http2`; disabled by default. [#2618]
- `compress-brotli`; disabled by default. [#2618]
- `compress-gzip`; disabled by default. [#2618]
- `compress-zstd`; disabled by default. [#2618]
- Functions:
- `body::to_bytes` for async collecting message body into Bytes. [#2158]
- Traits:
- `TryIntoHeaderPair`; allows using typed and untyped headers in the same methods. [#1869]
- Types:
- `body::BoxBody`; a boxed message body with boxed errors. [#2183]
- `body::EitherBody` enum. [#2468]
- `body::None` struct. [#2468]
- Re-export `http` crate's `Error` type as `error::HttpError`. [#2171]
- Variants:
- `ContentEncoding::Zstd` along with . [#2244]
- `Protocol::Http3` for future compatibility and also mark `#[non_exhaustive]`. [00ba8d55]
- Methods:
- `ContentEncoding::to_header_value()`. [#2501]
- `header::QualityItem::{max, min}()`. [#2486]
- `header::QualityItem::zero()` that uses `Quality::ZERO`. [#2501]
- `HeaderMap::drain()` as an efficient draining iterator. [#1964]
- `HeaderMap::len_keys()` has the behavior of the old `len` method. [#1964]
- `MessageBody::boxed` trait method for wrapping boxing types efficiently. [#2520]
- `MessageBody::try_into_bytes` trait method, with default implementation, for optimizations on body types that complete in exactly one poll. [#2522]
- `Request::conn_data()`. [#2491]
- `Request::take_conn_data()`. [#2491]
- `Request::take_req_data()`. [#2487]
- `Response::{ok, bad_request, not_found, internal_server_error}()`. [#2159]
- `Response::into_body()` that consumes response and returns body type. [#2201]
- `Response::map_into_boxed_body()`. [#2468]
- `ResponseBuilder::append_header()` method which allows using typed and untyped headers. [#1869]
- `ResponseBuilder::insert_header()` method which allows using typed and untyped headers. [#1869]
- `ResponseHead::set_camel_case_headers()`. [#2587]
- `TestRequest::insert_header()` method which allows using typed and untyped headers. [#1869]
- Implementations:
- Implement `Clone for ws::HandshakeError`. [#2468]
- Implement `Clone` for `body::AnyBody<S> where S: Clone`. [#2448]
- Implement `Clone` for `RequestHead`. [#2487]
- Implement `Clone` for `ResponseHead`. [#2585]
- Implement `Copy` for `QualityItem<T> where T: Copy`. [#2501]
- Implement `Default` for `ContentEncoding`. [#1912]
- Implement `Default` for `HttpServiceBuilder`. [#2611]
- Implement `Default` for `KeepAlive`. [#2611]
- Implement `Default` for `Response`. [#2201]
- Implement `Default` for `ws::Codec`. [#1920]
- Implement `Display` for `header::Quality`. [#2486]
- Implement `Eq` for `header::ContentEncoding`. [#2501]
- Implement `ExactSizeIterator` and `FusedIterator` for all `HeaderMap` iterators. [#2470]
- Implement `From<Duration>` for `KeepAlive`. [#2611]
- Implement `From<Option<Duration>>` for `KeepAlive`. [#2611]
- Implement `From<Vec<u8>>` for `Response<Vec<u8>>`. [#2625]
- Implement `FromStr` for `ContentEncoding`. [#1912]
- Implement `Header` for `ContentEncoding`. [#1912]
- Implement `IntoHeaderValue` for `ContentEncoding`. [#1912]
- Implement `IntoIterator` for `HeaderMap`. [#1964]
- Implement `MessageBody` for `bytestring::ByteString`. [#2468]
- Implement `MessageBody` for `Pin<Box<T>> where T: MessageBody`. [#2152]
- Misc:
- Re-export `StatusCode`, `Method`, `Version` and `Uri` at the crate root. [#2171]
- Re-export `ContentEncoding` and `ConnectionType` at the crate root. [#2171]
- `Quality::ZERO` associated constant equivalent to `q=0`. [#2501]
- `header::Quality::{MAX, MIN}` associated constants equivalent to `q=1` and `q=0.001`, respectively. [#2486]
- Timeout for canceling HTTP/2 server side connection handshake. Configurable with `ServiceConfig::client_timeout`; defaults to 5 seconds. [#2483]
- `#[must_use]` for `ws::Codec` to prevent subtle bugs. [#1920]
### Changed
- Traits:
- Rename `IntoHeaderValue => TryIntoHeaderValue`. [#2510]
- `MessageBody` now has an associated `Error` type. [#2183]
- Types:
- `Protocol` enum is now marked `#[non_exhaustive]`.
- `error::DispatcherError` enum is now marked `#[non_exhaustive]`. [#2624]
- `ContentEncoding` is now marked `#[non_exhaustive]`. [#2377]
- Error enums are marked `#[non_exhaustive]`. [#2161]
- Rename `PayloadStream` to `BoxedPayloadStream`. [#2545]
- The body type parameter of `Response` no longer has a default. [#2152]
- Enum Variants:
- Rename `ContentEncoding::{Br => Brotli}`. [#2501]
- `Payload` inner fields are now named. [#2545]
- `ws::Message::Text` now contains a `bytestring::ByteString`. [#1864]
- Methods:
- Rename `ServiceConfig::{client_timer_expire => client_request_deadline}`. [#2611]
- Rename `ServiceConfig::{client_disconnect_timer => client_disconnect_deadline}`. [#2611]
- Rename `h1::Codec::{keepalive => keep_alive}`. [#2611]
- Rename `h1::Codec::{keepalive_enabled => keep_alive_enabled}`. [#2611]
- Rename `h1::ClientCodec::{keepalive => keep_alive}`. [#2611]
- Rename `h1::ClientPayloadCodec::{keepalive => keep_alive}`. [#2611]
- Rename `header::EntityTag::{weak => new_weak, strong => new_strong}`. [#2565]
- Rename `TryIntoHeaderValue::{try_into => try_into_value}` to avoid ambiguity with std `TryInto` trait. [#1894]
- Deadline methods in `ServiceConfig` now return `std::time::Instant`s instead of Tokio's wrapper type. [#2611]
- Places in `Response` where `ResponseBody<B>` was received or returned now simply use `B`. [#2201]
- `encoding::Encoder::response` now returns `AnyBody<Encoder<B>>`. [#2448]
- `Extensions::insert` returns replaced item. [#1904]
- `HeaderMap::get_all` now returns a `std::slice::Iter`. [#2527]
- `HeaderMap::insert` now returns iterator of removed values. [#1964]
- `HeaderMap::len` now returns number of values instead of number of keys. [#1964]
- `HeaderMap::remove` now returns iterator of removed values. [#1964]
- `ResponseBuilder::body(B)` now returns `Response<EitherBody<B>>`. [#2468]
- `ResponseBuilder::content_type` now takes an `impl TryIntoHeaderValue` to support using typed `mime` types. [#1894]
- `ResponseBuilder::finish()` now returns `Response<EitherBody<()>>`. [#2468]
- `ResponseBuilder::json` now takes `impl Serialize`. [#2052]
- `ResponseBuilder::message_body` now returns a `Result`. [#2201]∑
- `ServiceConfig::keep_alive` now returns a `KeepAlive`. [#2611]
- `ws::hash_key` now returns array. [#2035]
- Trait Implementations:
- Implementation of `Stream` for `Payload` no longer requires the `Stream` variant be `Unpin`. [#2545]
- Implementation of `Future` for `h1::SendResponse` no longer requires the body type be `Unpin`. [#2545]
- Implementation of `Stream` for `encoding::Decoder` no longer requires the stream type be `Unpin`. [#2545]
- Implementation of `From` for error types now return a `Response<BoxBody>`. [#2468]
- Misc:
- `header` module is now public. [#2171]
- `uri` module is now public. [#2171]
- Request-local data container is no longer part of a `RequestHead`. Instead it is a distinct part of a `Request`. [#2487]
- All error trait bounds in server service builders have changed from `Into<Error>` to `Into<Response<BoxBody>>`. [#2253]
- All error trait bounds in message body and stream impls changed from `Into<Error>` to `Into<Box<dyn std::error::Error>>`. [#2253]
- Guarantee ordering of `header::GetAll` iterator to be same as insertion order. [#2467]
- Connection data set through the `on_connect_ext` callbacks is now accessible only from the new `Request::conn_data()` method. [#2491]
- Brotli (de)compression support is now provided by the `brotli` crate. [#2538]
- Minimum supported Rust version (MSRV) is now 1.54.
### Fixed
- A `Vary` header is now correctly sent along with compressed content. [#2501]
- HTTP/1.1 dispatcher correctly uses client request timeout. [#2611]
- Fixed issue where handlers that took payload but then dropped without reading it to EOF it would cause keep-alive connections to become stuck. [#2624]
- `ContentEncoding`'s `Identity` variant can now be parsed from a string. [#2501]
- `HttpServer::{listen_rustls(), bind_rustls()}` now honor the ALPN protocols in the configuration parameter. [#2226]
- Remove unnecessary `Into<Error>` bound on `Encoder` body types. [#2375]
- Remove unnecessary `Unpin` bound on `ResponseBuilder::streaming`. [#2253]
- `BodyStream` and `SizedStream` are no longer restricted to `Unpin` types. [#2152]
- Fixed slice creation pointing to potential uninitialized data on h1 encoder. [#2364]
- Fixed quality parse error in Accept-Encoding header. [#2344]
### Removed
- Crate Features:
- `compress` feature. [#2065]
- `cookies` feature. [#2065]
- `trust-dns` feature. [#2425]
- `actors` optional feature and trait implementation for `actix` types. [#1969]
- Functions:
- `header::qitem` helper. Replaced with `header::QualityItem::max`. [#2486]
- Types:
- `body::Body`; replaced with `EitherBody` and `BoxBody`. [#2468]
- `body::ResponseBody`. [#2446]
- `ConnectError::SslHandshakeError` and re-export of `HandshakeError`. Due to the removal of this type from `tokio-openssl` crate. OpenSSL handshake error now returns `ConnectError::SslError`. [#1813]
- `error::Canceled` re-export. [#1994]
- `error::Result` type alias. [#2201]
- `error::BlockingError` [#2660]
- `InternalError` and all the error types it constructed were moved up to `actix-web`. [#2215]
- Typed HTTP headers; they have moved up to `actix-web`. [2094]
- Re-export of `http` crate's `HeaderMap` types in addition to ours. [#2171]
- Enum Variants:
- `body::BodySize::Empty`; an empty body can now only be represented as a `Sized(0)` variant. [#2446]
- `ContentEncoding::Auto`. [#2501]
- `EncoderError::Boxed`. [#2446]
- Methods:
- `ContentEncoding::is_compression()`. [#2501]
- `h1::Payload::readany()`. [#2545]
- `HttpMessage::cookie[s]()` trait methods. [#2065]
- `HttpServiceBuilder::new()`; use `default` instead. [#2611]
- `on_connect` (previously deprecated) methods have been removed; use `on_connect_ext`. [#1857]
- `Response::build_from()`. [#2159]
- `Response::error()` [#2205]
- `Response::take_body()` and old `Response::into_body()` method that casted body type. [#2201]
- `Response`'s status code builders. [#2159]
- `ResponseBuilder::{if_true, if_some}()` (previously deprecated). [#2148]
- `ResponseBuilder::{set, set_header}()`; use `ResponseBuilder::insert_header()`. [#1869]
- `ResponseBuilder::extensions[_mut]()`. [#2585]
- `ResponseBuilder::header()`; use `ResponseBuilder::append_header()`. [#1869]
- `ResponseBuilder::json()`. [#2148]
- `ResponseBuilder::json2()`. [#1903]
- `ResponseBuilder::streaming()`. [#2468]
- `ResponseHead::extensions[_mut]()`. [#2585]
- `ServiceConfig::{client_timer, keep_alive_timer}()`. [#2611]
- `TestRequest::with_hdr()`; use `TestRequest::default().insert_header()`. [#1869]
- `TestRequest::with_header()`; use `TestRequest::default().insert_header()`. [#1869]
- Trait implementations:
- Implementation of `Copy` for `ws::Codec`. [#1920]
- Implementation of `From<Option<usize>> for KeepAlive`; use `Duration`s instead. [#2611]
- Implementation of `From<serde_json::Value>` for `Body`. [#2148]
- Implementation of `From<usize> for KeepAlive`; use `Duration`s instead. [#2611]
- Implementation of `Future` for `Response`. [#2201]
- Implementation of `Future` for `ResponseBuilder`. [#2468]
- Implementation of `Into<Error>` for `Response<Body>`. [#2215]
- Implementation of `Into<Error>` for `ResponseBuilder`. [#2215]
- Implementation of `ResponseError` for `actix_utils::timeout::TimeoutError`. [#2127]
- Implementation of `ResponseError` for `CookieParseError`. [#2065]
- Implementation of `TryFrom<u16>` for `header::Quality`. [#2486]
- Misc:
- `http` module; most everything it contained is exported at the crate root. [#2488]
- `cookies` module (re-export). [#2065]
- `client` module. Connector types now live in `awc`. [#2425]
- `error` field from `Response`. [#2205]
- `downcast` and `downcast_get_type_id` macros. [#2291]
- Down-casting for `MessageBody` types; use standard `Any` trait. [#2183]
[#1813]: https://github.com/actix/actix-web/pull/1813
[#1845]: https://github.com/actix/actix-web/pull/1845
[#1857]: https://github.com/actix/actix-web/pull/1857
[#1864]: https://github.com/actix/actix-web/pull/1864
[#1869]: https://github.com/actix/actix-web/pull/1869
[#1878]: https://github.com/actix/actix-web/pull/1878
[#1894]: https://github.com/actix/actix-web/pull/1894
[#1903]: https://github.com/actix/actix-web/pull/1903
[#1904]: https://github.com/actix/actix-web/pull/1904
[#1912]: https://github.com/actix/actix-web/pull/1912
[#1920]: https://github.com/actix/actix-web/pull/1920
[#1964]: https://github.com/actix/actix-web/pull/1964
[#1969]: https://github.com/actix/actix-web/pull/1969
[#1981]: https://github.com/actix/actix-web/pull/1981
[#1994]: https://github.com/actix/actix-web/pull/1994
[#2035]: https://github.com/actix/actix-web/pull/2035
[#2052]: https://github.com/actix/actix-web/pull/2052
[#2065]: https://github.com/actix/actix-web/pull/2065
[#2094]: https://github.com/actix/actix-web/pull/2094
[#2127]: https://github.com/actix/actix-web/pull/2127
[#2148]: https://github.com/actix/actix-web/pull/2148
[#2152]: https://github.com/actix/actix-web/pull/2152
[#2158]: https://github.com/actix/actix-web/pull/2158
[#2159]: https://github.com/actix/actix-web/pull/2159
[#2161]: https://github.com/actix/actix-web/pull/2161
[#2171]: https://github.com/actix/actix-web/pull/2171
[#2183]: https://github.com/actix/actix-web/pull/2183
[#2196]: https://github.com/actix/actix-web/pull/2196
[#2201]: https://github.com/actix/actix-web/pull/2201
[#2205]: https://github.com/actix/actix-web/pull/2205
[#2215]: https://github.com/actix/actix-web/pull/2215
[#2244]: https://github.com/actix/actix-web/pull/2244
[#2250]: https://github.com/actix/actix-web/pull/2250
[#2253]: https://github.com/actix/actix-web/pull/2253
[#2291]: https://github.com/actix/actix-web/pull/2291
[#2344]: https://github.com/actix/actix-web/pull/2344
[#2364]: https://github.com/actix/actix-web/pull/2364
[#2375]: https://github.com/actix/actix-web/pull/2375
[#2377]: https://github.com/actix/actix-web/pull/2377
[#2414]: https://github.com/actix/actix-web/pull/2414
[#2425]: https://github.com/actix/actix-web/pull/2425
[#2442]: https://github.com/actix/actix-web/pull/2442
[#2446]: https://github.com/actix/actix-web/pull/2446
[#2448]: https://github.com/actix/actix-web/pull/2448
[#2456]: https://github.com/actix/actix-web/pull/2456
[#2467]: https://github.com/actix/actix-web/pull/2467
[#2468]: https://github.com/actix/actix-web/pull/2468
[#2470]: https://github.com/actix/actix-web/pull/2470
[#2474]: https://github.com/actix/actix-web/pull/2474
[#2483]: https://github.com/actix/actix-web/pull/2483
[#2486]: https://github.com/actix/actix-web/pull/2486
[#2487]: https://github.com/actix/actix-web/pull/2487
[#2488]: https://github.com/actix/actix-web/pull/2488
[#2491]: https://github.com/actix/actix-web/pull/2491
[#2497]: https://github.com/actix/actix-web/pull/2497
[#2501]: https://github.com/actix/actix-web/pull/2501
[#2510]: https://github.com/actix/actix-web/pull/2510
[#2520]: https://github.com/actix/actix-web/pull/2520
[#2522]: https://github.com/actix/actix-web/pull/2522
[#2527]: https://github.com/actix/actix-web/pull/2527
[#2538]: https://github.com/actix/actix-web/pull/2538
[#2545]: https://github.com/actix/actix-web/pull/2545
[#2565]: https://github.com/actix/actix-web/pull/2565
[#2585]: https://github.com/actix/actix-web/pull/2585
[#2587]: https://github.com/actix/actix-web/pull/2587
[#2611]: https://github.com/actix/actix-web/pull/2611
[#2618]: https://github.com/actix/actix-web/pull/2618
[#2624]: https://github.com/actix/actix-web/pull/2624
[#2625]: https://github.com/actix/actix-web/pull/2625
[#2660]: https://github.com/actix/actix-web/pull/2660
[00ba8d55]: https://github.com/actix/actix-web/commit/00ba8d55492284581695d824648590715a8bd386
<details>
<summary>3.0.0 Pre-Releases</summary>
## 3.0.0-rc.4 - 2022-02-22
### Fixed
- Fix h1 dispatcher panic. [1ce58ecb]
[1ce58ecb]: https://github.com/actix/actix-web/commit/1ce58ecb305c60e51db06e6c913b7a1344e229ca
## 3.0.0-rc.3 - 2022-02-16
- No significant changes since `3.0.0-rc.2`.
## 3.0.0-rc.2 - 2022-02-08
### Added
- Implement `From<Vec<u8>>` for `Response<Vec<u8>>`. [#2625]
### Changed
- `error::DispatcherError` enum is now marked `#[non_exhaustive]`. [#2624]
### Fixed
- Issue where handlers that took payload but then dropped without reading it to EOF it would cause keep-alive connections to become stuck. [#2624]
[#2624]: https://github.com/actix/actix-web/pull/2624
[#2625]: https://github.com/actix/actix-web/pull/2625
## 3.0.0-rc.1 - 2022-01-31
### Added
- Implement `Default` for `KeepAlive`. [#2611]
@ -80,7 +389,7 @@
## 3.0.0-beta.17 - 2021-12-27
### Changes
### Changed
- `HeaderMap::get_all` now returns a `std::slice::Iter`. [#2527]
- `Payload` inner fields are now named. [#2545]
- `impl Stream` for `Payload` no longer requires the `Stream` variant be `Unpin`. [#2545]
@ -303,7 +612,7 @@
- `Response::{ok, bad_request, not_found, internal_server_error}`. [#2159]
- Helper `body::to_bytes` for async collecting message body into Bytes. [#2158]
### Changes
### Changed
- The type parameter of `Response` no longer has a default. [#2152]
- The `Message` variant of `body::Body` is now `Pin<Box<dyn MessageBody>>`. [#2152]
- `BodyStream` and `SizedStream` are no longer restricted to Unpin types. [#2152]
@ -447,6 +756,8 @@
[#1864]: https://github.com/actix/actix-web/pull/1864
[#1878]: https://github.com/actix/actix-web/pull/1878
</details>
## 2.2.2 - 2022-01-21
### Changed

View File

@ -1,6 +1,6 @@
[package]
name = "actix-http"
version = "3.0.0-rc.1"
version = "3.0.0"
authors = [
"Nikolay Kim <fafhrd91@gmail.com>",
"Rob Ede <robjtede@icloud.com>",
@ -20,7 +20,7 @@ edition = "2018"
[package.metadata.docs.rs]
# features that docs.rs will build with
features = ["openssl", "rustls", "compress-brotli", "compress-gzip", "compress-zstd"]
features = ["http2", "openssl", "rustls", "compress-brotli", "compress-gzip", "compress-zstd"]
[lib]
name = "actix_http"
@ -57,7 +57,7 @@ __compress = []
[dependencies]
actix-service = "2"
actix-codec = "0.4.1"
actix-codec = "0.5"
actix-utils = "3"
actix-rt = { version = "2.2", default-features = false }
@ -89,18 +89,18 @@ rand = { version = "0.8", optional = true }
sha-1 = { version = "0.10", optional = true }
# openssl/rustls
actix-tls = { version = "3.0.0", default-features = false, optional = true }
actix-tls = { version = "3", default-features = false, optional = true }
# compress-*
brotli = { version = "3.3.3", optional = true }
flate2 = { version = "1.0.13", optional = true }
zstd = { version = "0.9", optional = true }
zstd = { version = "0.10", optional = true }
[dev-dependencies]
actix-http-test = { version = "3.0.0-beta.12", features = ["openssl"] }
actix-http-test = { version = "3.0.0-beta.13", features = ["openssl"] }
actix-server = "2"
actix-tls = { version = "3.0.0", features = ["openssl"] }
actix-web = "4.0.0-rc.1"
actix-tls = { version = "3", features = ["openssl"] }
actix-web = "4.0.0"
async-stream = "0.3"
criterion = { version = "0.3", features = ["html_reports"] }

View File

@ -3,11 +3,11 @@
> HTTP primitives for the Actix ecosystem.
[![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.0.0-rc.1)](https://docs.rs/actix-http/3.0.0-rc.1)
[![Documentation](https://docs.rs/actix-http/badge.svg?version=3.0.0)](https://docs.rs/actix-http/3.0.0)
[![Version](https://img.shields.io/badge/rustc-1.54+-ab6000.svg)](https://blog.rust-lang.org/2021/05/06/Rust-1.54.0.html)
![MIT or Apache 2.0 licensed](https://img.shields.io/crates/l/actix-http.svg)
<br />
[![dependency status](https://deps.rs/crate/actix-http/3.0.0-rc.1/status.svg)](https://deps.rs/crate/actix-http/3.0.0-rc.1)
[![dependency status](https://deps.rs/crate/actix-http/3.0.0/status.svg)](https://deps.rs/crate/actix-http/3.0.0)
[![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)

View File

@ -80,7 +80,7 @@ mod tests {
use futures_core::ready;
use futures_util::{stream, FutureExt as _};
use pin_project_lite::pin_project;
use static_assertions::{assert_impl_all, assert_not_impl_all};
use static_assertions::{assert_impl_all, assert_not_impl_any};
use super::*;
use crate::body::to_bytes;
@ -91,10 +91,10 @@ mod tests {
assert_impl_all!(BodyStream<stream::Empty<Result<Bytes, Infallible>>>: MessageBody);
assert_impl_all!(BodyStream<stream::Repeat<Result<Bytes, Infallible>>>: MessageBody);
assert_not_impl_all!(BodyStream<stream::Empty<Bytes>>: MessageBody);
assert_not_impl_all!(BodyStream<stream::Repeat<Bytes>>: MessageBody);
assert_not_impl_any!(BodyStream<stream::Empty<Bytes>>: MessageBody);
assert_not_impl_any!(BodyStream<stream::Repeat<Bytes>>: MessageBody);
// crate::Error is not Clone
assert_not_impl_all!(BodyStream<stream::Repeat<Result<Bytes, crate::Error>>>: MessageBody);
assert_not_impl_any!(BodyStream<stream::Repeat<Result<Bytes, crate::Error>>>: MessageBody);
#[actix_rt::test]
async fn skips_empty_chunks() {

View File

@ -105,14 +105,13 @@ impl MessageBody for BoxBody {
#[cfg(test)]
mod tests {
use static_assertions::{assert_impl_all, assert_not_impl_all};
use static_assertions::{assert_impl_all, assert_not_impl_any};
use super::*;
use crate::body::to_bytes;
assert_impl_all!(BoxBody: MessageBody, fmt::Debug, Unpin);
assert_not_impl_all!(BoxBody: Send, Sync, Unpin);
assert_impl_all!(BoxBody: fmt::Debug, MessageBody, Unpin);
assert_not_impl_any!(BoxBody: Send, Sync);
#[actix_rt::test]
async fn nested_boxed_body() {

View File

@ -10,6 +10,17 @@ use super::{BodySize, BoxBody, MessageBody};
use crate::Error;
pin_project! {
/// An "either" type specialized for body types.
///
/// It is common, in middleware especially, to conditionally return an inner service's unknown/
/// generic body `B` type or return early with a new response. This type's "right" variant
/// defaults to `BoxBody` since error responses are the common case.
///
/// For example, middleware will often have `type Response = ServiceResponse<EitherBody<B>>`.
/// This means that the inner service's response body type maps to the `Left` variant and the
/// middleware's own error responses use the default `Right` variant of `BoxBody`. Of course,
/// there's no reason it couldn't use `EitherBody<B, String>` instead if its alternative
/// responses have a known type.
#[project = EitherBodyProj]
#[derive(Debug, Clone)]
pub enum EitherBody<L, R = BoxBody> {
@ -22,7 +33,10 @@ pin_project! {
}
impl<L> EitherBody<L, BoxBody> {
/// Creates new `EitherBody` using left variant and boxed right variant.
/// Creates new `EitherBody` left variant with a boxed right variant.
///
/// If the expected `R` type will be inferred and is not `BoxBody` then use the
/// [`left`](Self::left) constructor instead.
#[inline]
pub fn new(body: L) -> Self {
Self::Left { body }

View File

@ -19,7 +19,7 @@ use super::{BodySize, BoxBody};
/// It is not usually necessary to create custom body types, this trait is already [implemented for
/// a large number of sensible body types](#foreign-impls) including:
/// - Empty body: `()`
/// - Text-based: `String`, `&'static str`, `ByteString`.
/// - Text-based: `String`, `&'static str`, [`ByteString`](https://docs.rs/bytestring/1).
/// - Byte-based: `Bytes`, `BytesMut`, `Vec<u8>`, `&'static [u8]`;
/// - Streams: [`BodyStream`](super::BodyStream), [`SizedStream`](super::SizedStream)
///

View File

@ -76,7 +76,7 @@ mod tests {
use actix_rt::pin;
use actix_utils::future::poll_fn;
use futures_util::stream;
use static_assertions::{assert_impl_all, assert_not_impl_all};
use static_assertions::{assert_impl_all, assert_not_impl_any};
use super::*;
use crate::body::to_bytes;
@ -87,10 +87,10 @@ mod tests {
assert_impl_all!(SizedStream<stream::Empty<Result<Bytes, Infallible>>>: MessageBody);
assert_impl_all!(SizedStream<stream::Repeat<Result<Bytes, Infallible>>>: MessageBody);
assert_not_impl_all!(SizedStream<stream::Empty<Bytes>>: MessageBody);
assert_not_impl_all!(SizedStream<stream::Repeat<Bytes>>: MessageBody);
assert_not_impl_any!(SizedStream<stream::Empty<Bytes>>: MessageBody);
assert_not_impl_any!(SizedStream<stream::Repeat<Bytes>>: MessageBody);
// crate::Error is not Clone
assert_not_impl_all!(SizedStream<stream::Repeat<Result<Bytes, crate::Error>>>: MessageBody);
assert_not_impl_any!(SizedStream<stream::Repeat<Result<Bytes, crate::Error>>>: MessageBody);
#[actix_rt::test]
async fn skips_empty_chunks() {

View File

@ -104,8 +104,13 @@ impl ServiceConfig {
self.0.date_service.now()
}
pub(crate) fn write_date_header(&self, dst: &mut BytesMut, camel_case: bool) {
let mut buf: [u8; 39] = [0; 39];
/// Writes date header to `dst` buffer.
///
/// Low-level method that utilizes the built-in efficient date service, requiring fewer syscalls
/// than normal. Note that a CRLF (`\r\n`) is included in what is written.
#[doc(hidden)]
pub fn write_date_header(&self, dst: &mut BytesMut, camel_case: bool) {
let mut buf: [u8; 37] = [0; 37];
buf[..6].copy_from_slice(if camel_case { b"Date: " } else { b"date: " });
@ -113,7 +118,7 @@ impl ServiceConfig {
.date_service
.with_date(|date| buf[6..35].copy_from_slice(&date.bytes));
buf[35..].copy_from_slice(b"\r\n\r\n");
buf[35..].copy_from_slice(b"\r\n");
dst.extend_from_slice(&buf);
}

View File

@ -19,7 +19,7 @@ use zstd::stream::write::Decoder as ZstdDecoder;
use crate::{
encoding::Writer,
error::{BlockingError, PayloadError},
error::PayloadError,
header::{ContentEncoding, HeaderMap, CONTENT_ENCODING},
};
@ -47,14 +47,17 @@ where
ContentEncoding::Brotli => Some(ContentDecoder::Brotli(Box::new(
brotli::DecompressorWriter::new(Writer::new(), 8_096),
))),
#[cfg(feature = "compress-gzip")]
ContentEncoding::Deflate => Some(ContentDecoder::Deflate(Box::new(
ZlibDecoder::new(Writer::new()),
))),
#[cfg(feature = "compress-gzip")]
ContentEncoding::Gzip => Some(ContentDecoder::Gzip(Box::new(GzDecoder::new(
Writer::new(),
)))),
#[cfg(feature = "compress-zstd")]
ContentEncoding::Zstd => Some(ContentDecoder::Zstd(Box::new(
ZstdDecoder::new(Writer::new()).expect(
@ -98,8 +101,12 @@ where
loop {
if let Some(ref mut fut) = this.fut {
let (chunk, decoder) =
ready!(Pin::new(fut).poll(cx)).map_err(|_| BlockingError)??;
let (chunk, decoder) = ready!(Pin::new(fut).poll(cx)).map_err(|_| {
PayloadError::Io(io::Error::new(
io::ErrorKind::Other,
"Blocking task was cancelled unexpectedly",
))
})??;
*this.decoder = Some(decoder);
this.fut.take();
@ -159,10 +166,13 @@ where
enum ContentDecoder {
#[cfg(feature = "compress-gzip")]
Deflate(Box<ZlibDecoder<Writer>>),
#[cfg(feature = "compress-gzip")]
Gzip(Box<GzDecoder<Writer>>),
#[cfg(feature = "compress-brotli")]
Brotli(Box<brotli::DecompressorWriter<Writer>>),
// We need explicit 'static lifetime here because ZstdDecoder need lifetime
// argument, and we use `spawn_blocking` in `Decoder::poll_next` that require `FnOnce() -> R + Send + 'static`
#[cfg(feature = "compress-zstd")]

View File

@ -23,7 +23,6 @@ use zstd::stream::write::Encoder as ZstdEncoder;
use super::Writer;
use crate::{
body::{self, BodySize, MessageBody},
error::BlockingError,
header::{self, ContentEncoding, HeaderValue, CONTENT_ENCODING},
ResponseHead, StatusCode,
};
@ -173,7 +172,12 @@ where
if let Some(ref mut fut) = this.fut {
let mut encoder = ready!(Pin::new(fut).poll(cx))
.map_err(|_| EncoderError::Blocking(BlockingError))?
.map_err(|_| {
EncoderError::Io(io::Error::new(
io::ErrorKind::Other,
"Blocking task was cancelled unexpectedly",
))
})?
.map_err(EncoderError::Io)?;
let chunk = encoder.take();
@ -400,12 +404,11 @@ fn new_brotli_compressor() -> Box<brotli::CompressorWriter<Writer>> {
#[derive(Debug, Display)]
#[non_exhaustive]
pub enum EncoderError {
/// Wrapped body stream error.
#[display(fmt = "body")]
Body(Box<dyn StdError>),
#[display(fmt = "blocking")]
Blocking(BlockingError),
/// Generic I/O error.
#[display(fmt = "io")]
Io(io::Error),
}
@ -414,7 +417,6 @@ impl StdError for EncoderError {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
match self {
EncoderError::Body(err) => Some(&**err),
EncoderError::Blocking(err) => Some(err),
EncoderError::Io(err) => Some(err),
}
}

View File

@ -51,7 +51,7 @@ impl Error {
Self::new(Kind::SendResponse)
}
#[allow(unused)] // reserved for future use (TODO: remove allow when being used)
#[allow(unused)] // available for future use
pub(crate) fn new_io() -> Self {
Self::new(Kind::Io)
}
@ -108,8 +108,10 @@ pub(crate) enum Kind {
impl fmt::Debug for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// TODO: more detail
f.write_str("actix_http::Error")
f.debug_struct("actix_http::Error")
.field("kind", &self.inner.kind)
.field("cause", &self.inner.cause)
.finish()
}
}
@ -250,12 +252,6 @@ impl From<ParseError> for Response<BoxBody> {
}
}
/// A set of errors that can occur running blocking tasks in thread pool.
#[derive(Debug, Display, Error)]
#[display(fmt = "Blocking thread pool is gone")]
// TODO: non-exhaustive
pub struct BlockingError;
/// A set of errors that can occur during payload parsing.
#[derive(Debug, Display)]
#[non_exhaustive]
@ -293,13 +289,13 @@ impl std::error::Error for PayloadError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
PayloadError::Incomplete(None) => None,
PayloadError::Incomplete(Some(err)) => Some(err as &dyn std::error::Error),
PayloadError::Incomplete(Some(err)) => Some(err),
PayloadError::EncodingCorrupted => None,
PayloadError::Overflow => None,
PayloadError::UnknownLength => None,
#[cfg(feature = "http2")]
PayloadError::Http2Payload(err) => Some(err as &dyn std::error::Error),
PayloadError::Io(err) => Some(err as &dyn std::error::Error),
PayloadError::Http2Payload(err) => Some(err),
PayloadError::Io(err) => Some(err),
}
}
}
@ -323,15 +319,6 @@ impl From<io::Error> for PayloadError {
}
}
impl From<BlockingError> for PayloadError {
fn from(_: BlockingError) -> Self {
PayloadError::Io(io::Error::new(
io::ErrorKind::Other,
"Operation is canceled",
))
}
}
impl From<PayloadError> for Error {
fn from(err: PayloadError) -> Self {
Self::new_payload().with_cause(err)
@ -340,6 +327,7 @@ impl From<PayloadError> for Error {
/// A set of errors that can occur during dispatching HTTP requests.
#[derive(Debug, Display, From)]
#[non_exhaustive]
pub enum DispatchError {
/// Service error.
#[display(fmt = "Service Error")]
@ -373,6 +361,10 @@ pub enum DispatchError {
#[display(fmt = "Connection shutdown timeout")]
DisconnectTimeout,
/// Handler dropped payload before reading EOF.
#[display(fmt = "Handler dropped payload before reading EOF")]
HandlerDroppedPayload,
/// Internal error.
#[display(fmt = "Internal error")]
InternalError,
@ -381,7 +373,6 @@ pub enum DispatchError {
impl StdError for DispatchError {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
match self {
// TODO: error source extraction?
DispatchError::Service(_res) => None,
DispatchError::Body(err) => Some(&**err),
DispatchError::Io(err) => Some(err),

View File

@ -128,7 +128,10 @@ impl Decoder for ClientCodec {
type Error = ParseError;
fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
debug_assert!(!self.inner.payload.is_some(), "Payload decoder is set");
debug_assert!(
self.inner.payload.is_none(),
"Payload decoder should not be set"
);
if let Some((req, payload)) = self.inner.decoder.decode(src)? {
if let Some(conn_type) = req.conn_type() {

View File

@ -125,11 +125,13 @@ impl Decoder for Codec {
self.flags.set(Flags::HEAD, head.method == Method::HEAD);
self.version = head.version;
self.conn_type = head.connection_type();
if self.conn_type == ConnectionType::KeepAlive
&& !self.flags.contains(Flags::KEEP_ALIVE_ENABLED)
{
self.conn_type = ConnectionType::Close
}
match payload {
PayloadType::None => self.payload = None,
PayloadType::Payload(pl) => self.payload = Some(pl),

View File

@ -209,15 +209,16 @@ impl MessageType for Request {
let (len, method, uri, ver, h_len) = {
// SAFETY:
// Create an uninitialized array of `MaybeUninit`. The `assume_init` is
// safe because the type we are claiming to have initialized here is a
// bunch of `MaybeUninit`s, which do not require initialization.
// Create an uninitialized array of `MaybeUninit`. The `assume_init` is safe because the
// type we are claiming to have initialized here is a bunch of `MaybeUninit`s, which
// do not require initialization.
let mut parsed = unsafe {
MaybeUninit::<[MaybeUninit<httparse::Header<'_>>; MAX_HEADERS]>::uninit()
.assume_init()
};
let mut req = httparse::Request::new(&mut []);
match req.parse_with_uninit_headers(src, &mut parsed)? {
httparse::Status::Complete(len) => {
let method = Method::from_bytes(req.method.unwrap().as_bytes())
@ -232,6 +233,7 @@ impl MessageType for Request {
(len, method, uri, version, req.headers.len())
}
httparse::Status::Partial => {
return if src.len() >= MAX_BUFFER_SIZE {
trace!("MAX_BUFFER_SIZE unprocessed data reached, closing");

View File

@ -21,7 +21,7 @@ use crate::{
config::ServiceConfig,
error::{DispatchError, ParseError, PayloadError},
service::HttpFlow,
Error, Extensions, OnConnectData, Request, Response, StatusCode,
ConnectionType, Error, Extensions, OnConnectData, Request, Response, StatusCode,
};
use super::{
@ -151,7 +151,8 @@ pin_project! {
error: Option<DispatchError>,
#[pin]
state: State<S, B, X>,
pub(super) state: State<S, B, X>,
// when Some(_) dispatcher is in state of receiving request payload
payload: Option<PayloadSender>,
messages: VecDeque<DispatcherMessage>,
@ -174,7 +175,7 @@ enum DispatcherMessage {
pin_project! {
#[project = StateProj]
enum State<S, B, X>
pub(super) enum State<S, B, X>
where
S: Service<Request>,
X: Service<Request, Response = Request>,
@ -194,7 +195,7 @@ where
X: Service<Request, Response = Request>,
B: MessageBody,
{
fn is_none(&self) -> bool {
pub(super) fn is_none(&self) -> bool {
matches!(self, State::None)
}
}
@ -686,12 +687,74 @@ where
let can_not_read = !self.can_read(cx);
// limit amount of non-processed requests
if pipeline_queue_full || can_not_read {
if pipeline_queue_full {
return Ok(false);
}
let mut this = self.as_mut().project();
if can_not_read {
log::debug!("cannot read request payload");
if let Some(sender) = &this.payload {
// ...maybe handler does not want to read any more payload...
if let PayloadStatus::Dropped = sender.need_read(cx) {
log::debug!("handler dropped payload early; attempt to clean connection");
// ...in which case poll request payload a few times
loop {
match this.codec.decode(this.read_buf)? {
Some(msg) => {
match msg {
// payload decoded did not yield EOF yet
Message::Chunk(Some(_)) => {
// if non-clean connection, next loop iter will detect empty
// read buffer and close connection
}
// connection is in clean state for next request
Message::Chunk(None) => {
log::debug!("connection successfully cleaned");
// reset dispatcher state
let _ = this.payload.take();
this.state.set(State::None);
// break out of payload decode loop
break;
}
// Either whole payload is read and loop is broken or more data
// was expected in which case connection is closed. In both
// situations dispatcher cannot get here.
Message::Item(_) => {
unreachable!("dispatcher is in payload receive state")
}
}
}
// not enough info to decide if connection is going to be clean or not
None => {
log::error!(
"handler did not read whole payload and dispatcher could not \
drain read buf; return 500 and close connection"
);
this.flags.insert(Flags::SHUTDOWN);
let mut res = Response::internal_server_error().drop_body();
res.head_mut().set_connection_type(ConnectionType::Close);
this.messages.push_back(DispatcherMessage::Error(res));
*this.error = Some(DispatchError::HandlerDroppedPayload);
return Ok(true);
}
}
}
}
} else {
// can_not_read and no request payload
return Ok(false);
}
}
let mut updated = false;
loop {
@ -868,10 +931,16 @@ where
"dispatcher should not be in keep-alive phase if state is not none: {:?}",
this.state,
);
debug_assert!(
this.write_buf.is_empty(),
"dispatcher should not be in keep-alive phase if write_buf is not empty",
);
// Assert removed by @robjtede on account of issue #2655. There are cases where an I/O
// flush can be pending after entering the keep-alive state causing the subsequent flush
// wake up to panic here. This appears to be a Linux-only problem. Leaving original code
// below for posterity because a simple and reliable test could not be found to trigger
// the behavior.
// debug_assert!(
// this.write_buf.is_empty(),
// "dispatcher should not be in keep-alive phase if write_buf is not empty",
// );
// keep-alive timer has timed out
if timer.as_mut().poll(cx).is_ready() {

View File

@ -1,6 +1,6 @@
use std::{future::Future, str, task::Poll, time::Duration};
use actix_rt::time::sleep;
use actix_rt::{pin, time::sleep};
use actix_service::fn_service;
use actix_utils::future::{ready, Ready};
use bytes::Bytes;
@ -53,6 +53,14 @@ fn echo_path_service(
})
}
fn drop_payload_service(
) -> impl Service<Request, Response = Response<&'static str>, Error = Error> {
fn_service(|mut req: Request| async move {
let _ = req.take_payload();
Ok::<_, Error>(Response::with_body(StatusCode::OK, "payload dropped"))
})
}
fn echo_payload_service() -> impl Service<Request, Response = Response<Bytes>, Error = Error> {
fn_service(|mut req: Request| {
Box::pin(async move {
@ -89,7 +97,7 @@ async fn late_request() {
None,
OnConnectData::default(),
);
actix_rt::pin!(h1);
pin!(h1);
lazy(|cx| {
assert!(matches!(&h1.inner, DispatcherState::Normal { .. }));
@ -156,7 +164,7 @@ async fn oneshot_connection() {
None,
OnConnectData::default(),
);
actix_rt::pin!(h1);
pin!(h1);
lazy(|cx| {
assert!(matches!(&h1.inner, DispatcherState::Normal { .. }));
@ -173,13 +181,16 @@ async fn oneshot_connection() {
stabilize_date_header(&mut res);
let res = &res[..];
let exp = b"\
HTTP/1.1 200 OK\r\n\
content-length: 5\r\n\
connection: close\r\n\
date: Thu, 01 Jan 1970 12:34:56 UTC\r\n\r\n\
/abcd\
";
let exp = http_msg(
r"
HTTP/1.1 200 OK
content-length: 5
connection: close
date: Thu, 01 Jan 1970 12:34:56 UTC
/abcd
",
);
assert_eq!(
res,
@ -188,7 +199,7 @@ async fn oneshot_connection() {
response: {:?}\n\
expected: {:?}",
String::from_utf8_lossy(res),
String::from_utf8_lossy(exp)
String::from_utf8_lossy(&exp)
);
})
.await;
@ -214,7 +225,7 @@ async fn keep_alive_timeout() {
None,
OnConnectData::default(),
);
actix_rt::pin!(h1);
pin!(h1);
lazy(|cx| {
assert!(matches!(&h1.inner, DispatcherState::Normal { .. }));
@ -293,7 +304,7 @@ async fn keep_alive_follow_up_req() {
None,
OnConnectData::default(),
);
actix_rt::pin!(h1);
pin!(h1);
lazy(|cx| {
assert!(matches!(&h1.inner, DispatcherState::Normal { .. }));
@ -413,7 +424,7 @@ async fn req_parse_err() {
OnConnectData::default(),
);
actix_rt::pin!(h1);
pin!(h1);
match h1.as_mut().poll(cx) {
Poll::Pending => panic!(),
@ -459,7 +470,7 @@ async fn pipelining_ok_then_ok() {
OnConnectData::default(),
);
actix_rt::pin!(h1);
pin!(h1);
assert!(matches!(&h1.inner, DispatcherState::Normal { .. }));
@ -529,7 +540,7 @@ async fn pipelining_ok_then_bad() {
OnConnectData::default(),
);
actix_rt::pin!(h1);
pin!(h1);
assert!(matches!(&h1.inner, DispatcherState::Normal { .. }));
@ -601,7 +612,7 @@ async fn expect_handling() {
",
);
actix_rt::pin!(h1);
pin!(h1);
assert!(h1.as_mut().poll(cx).is_pending());
assert!(matches!(&h1.inner, DispatcherState::Normal { .. }));
@ -678,7 +689,7 @@ async fn expect_eager() {
",
);
actix_rt::pin!(h1);
pin!(h1);
assert!(h1.as_mut().poll(cx).is_ready());
assert!(matches!(&h1.inner, DispatcherState::Normal { .. }));
@ -761,7 +772,7 @@ async fn upgrade_handling() {
",
);
actix_rt::pin!(h1);
pin!(h1);
assert!(h1.as_mut().poll(cx).is_ready());
assert!(matches!(&h1.inner, DispatcherState::Upgrade { .. }));
@ -771,3 +782,192 @@ async fn upgrade_handling() {
})
.await;
}
#[actix_rt::test]
async fn handler_drop_payload() {
let _ = env_logger::try_init();
let mut buf = TestBuffer::new(http_msg(
r"
POST /drop-payload HTTP/1.1
Content-Length: 3
abc
",
));
let services = HttpFlow::new(
drop_payload_service(),
ExpectHandler,
None::<UpgradeHandler>,
);
let h1 = Dispatcher::new(
buf.clone(),
services,
ServiceConfig::default(),
None,
OnConnectData::default(),
);
pin!(h1);
lazy(|cx| {
assert!(h1.as_mut().poll(cx).is_pending());
// polls: manual
assert_eq!(h1.poll_count, 1);
let mut res = BytesMut::from(buf.take_write_buf().as_ref());
stabilize_date_header(&mut res);
let res = &res[..];
let exp = http_msg(
r"
HTTP/1.1 200 OK
content-length: 15
date: Thu, 01 Jan 1970 12:34:56 UTC
payload dropped
",
);
assert_eq!(
res,
exp,
"\nexpected response not in write buffer:\n\
response: {:?}\n\
expected: {:?}",
String::from_utf8_lossy(res),
String::from_utf8_lossy(&exp)
);
if let DispatcherStateProj::Normal { inner } = h1.as_mut().project().inner.project() {
assert!(inner.state.is_none());
}
})
.await;
lazy(|cx| {
// add message that claims to have payload longer than provided
buf.extend_read_buf(http_msg(
r"
POST /drop-payload HTTP/1.1
Content-Length: 200
abc
",
));
assert!(h1.as_mut().poll(cx).is_pending());
// polls: manual => manual
assert_eq!(h1.poll_count, 2);
let mut res = BytesMut::from(buf.take_write_buf().as_ref());
stabilize_date_header(&mut res);
let res = &res[..];
// expect response immediately even though request side has not finished reading payload
let exp = http_msg(
r"
HTTP/1.1 200 OK
content-length: 15
date: Thu, 01 Jan 1970 12:34:56 UTC
payload dropped
",
);
assert_eq!(
res,
exp,
"\nexpected response not in write buffer:\n\
response: {:?}\n\
expected: {:?}",
String::from_utf8_lossy(res),
String::from_utf8_lossy(&exp)
);
})
.await;
lazy(|cx| {
assert!(h1.as_mut().poll(cx).is_ready());
// polls: manual => manual => manual
assert_eq!(h1.poll_count, 3);
let mut res = BytesMut::from(buf.take_write_buf().as_ref());
stabilize_date_header(&mut res);
let res = &res[..];
// expect that unrequested error response is sent back since connection could not be cleaned
let exp = http_msg(
r"
HTTP/1.1 500 Internal Server Error
content-length: 0
connection: close
date: Thu, 01 Jan 1970 12:34:56 UTC
",
);
assert_eq!(
res,
exp,
"\nexpected response not in write buffer:\n\
response: {:?}\n\
expected: {:?}",
String::from_utf8_lossy(res),
String::from_utf8_lossy(&exp)
);
})
.await;
}
fn http_msg(msg: impl AsRef<str>) -> BytesMut {
let mut msg = msg
.as_ref()
.trim()
.split('\n')
.into_iter()
.map(|line| [line.trim_start(), "\r"].concat())
.collect::<Vec<_>>()
.join("\n");
// remove trailing \r
msg.pop();
if !msg.is_empty() && !msg.contains("\r\n\r\n") {
msg.push_str("\r\n\r\n");
}
BytesMut::from(msg.as_bytes())
}
#[test]
fn http_msg_creates_msg() {
assert_eq!(http_msg(r""), "");
assert_eq!(
http_msg(
r"
POST / HTTP/1.1
Content-Length: 3
abc
"
),
"POST / HTTP/1.1\r\nContent-Length: 3\r\n\r\nabc"
);
assert_eq!(
http_msg(
r"
GET / HTTP/1.1
Content-Length: 3
"
),
"GET / HTTP/1.1\r\nContent-Length: 3\r\n\r\n"
);
}

View File

@ -210,14 +210,14 @@ pub(crate) trait MessageType: Sized {
dst.advance_mut(pos);
}
// optimized date header, set_date writes \r\n
if !has_date {
// optimized date header, write_date_header writes its own \r\n
config.write_date_header(dst, camel_case);
} else {
// msg eof
dst.extend_from_slice(b"\r\n");
}
// end-of-headers marker
dst.extend_from_slice(b"\r\n");
Ok(())
}

View File

@ -25,7 +25,9 @@ use pin_project_lite::pin_project;
use crate::{
body::{BodySize, BoxBody, MessageBody},
config::ServiceConfig,
header::{HeaderValue, CONNECTION, CONTENT_LENGTH, DATE, TRANSFER_ENCODING},
header::{
HeaderName, HeaderValue, CONNECTION, CONTENT_LENGTH, DATE, TRANSFER_ENCODING, UPGRADE,
},
service::HttpFlow,
Extensions, OnConnectData, Payload, Request, Response, ResponseHead,
};
@ -306,13 +308,22 @@ fn prepare_response(
// copy headers
for (key, value) in head.headers.iter() {
match *key {
// TODO: consider skipping other headers according to:
// https://datatracker.ietf.org/doc/html/rfc7540#section-8.1.2.2
// omit HTTP/1.x only headers
CONNECTION | TRANSFER_ENCODING => continue,
CONTENT_LENGTH if skip_len => continue,
DATE => has_date = true,
match key {
// omit HTTP/1.x only headers according to:
// https://datatracker.ietf.org/doc/html/rfc7540#section-8.1.2.2
&CONNECTION | &TRANSFER_ENCODING | &UPGRADE => continue,
&CONTENT_LENGTH if skip_len => continue,
&DATE => has_date = true,
// omit HTTP/1.x only headers according to:
// https://datatracker.ietf.org/doc/html/rfc7540#section-8.1.2.2
hdr if hdr == HeaderName::from_static("keep-alive")
|| hdr == HeaderName::from_static("proxy-connection") =>
{
continue
}
_ => {}
}

View File

@ -3,6 +3,7 @@
//! ## Crate Features
//! | Feature | Functionality |
//! | ------------------- | ------------------------------------------- |
//! | `http2` | HTTP/2 support via [h2]. |
//! | `openssl` | TLS support via [OpenSSL]. |
//! | `rustls` | TLS support via [rustls]. |
//! | `compress-brotli` | Payload compression support: Brotli. |
@ -10,6 +11,7 @@
//! | `compress-zstd` | Payload compression support: Zstd. |
//! | `trust-dns` | Use [trust-dns] as the client DNS resolver. |
//!
//! [h2]: https://crates.io/crates/h2
//! [OpenSSL]: https://crates.io/crates/openssl
//! [rustls]: https://crates.io/crates/rustls
//! [trust-dns]: https://crates.io/crates/trust-dns

View File

@ -285,6 +285,24 @@ impl From<&'static [u8]> for Response<&'static [u8]> {
}
}
impl From<Vec<u8>> for Response<Vec<u8>> {
fn from(val: Vec<u8>) -> Self {
let mut res = Response::with_body(StatusCode::OK, val);
let mime = mime::APPLICATION_OCTET_STREAM.try_into_value().unwrap();
res.headers_mut().insert(header::CONTENT_TYPE, mime);
res
}
}
impl From<&Vec<u8>> for Response<Vec<u8>> {
fn from(val: &Vec<u8>) -> Self {
let mut res = Response::with_body(StatusCode::OK, val.clone());
let mime = mime::APPLICATION_OCTET_STREAM.try_into_value().unwrap();
res.headers_mut().insert(header::CONTENT_TYPE, mime);
res
}
}
impl From<String> for Response<String> {
fn from(val: String) -> Self {
let mut res = Response::with_body(StatusCode::OK, val);

View File

@ -47,40 +47,6 @@ pub fn apply_mask_fast32(buf: &mut [u8], mask: [u8; 4]) {
mod tests {
use super::*;
// legacy test from old apply mask test. kept for now for back compat test.
// TODO: remove it and favor the other test.
#[test]
fn test_apply_mask_legacy() {
let mask = [0x6d, 0xb6, 0xb2, 0x80];
let unmasked = vec![
0xf3, 0x00, 0x01, 0x02, 0x03, 0x80, 0x81, 0x82, 0xff, 0xfe, 0x00, 0x17, 0x74, 0xf9,
0x12, 0x03,
];
// Check masking with proper alignment.
{
let mut masked = unmasked.clone();
apply_mask_fallback(&mut masked, mask);
let mut masked_fast = unmasked.clone();
apply_mask(&mut masked_fast, mask);
assert_eq!(masked, masked_fast);
}
// Check masking without alignment.
{
let mut masked = unmasked.clone();
apply_mask_fallback(&mut masked[1..], mask);
let mut masked_fast = unmasked;
apply_mask(&mut masked_fast[1..], mask);
assert_eq!(masked, masked_fast);
}
}
#[test]
fn test_apply_mask() {
let mask = [0x6d, 0xb6, 0xb2, 0x80];

View File

@ -66,7 +66,7 @@ fn tls_config() -> SslAcceptor {
}
#[actix_rt::test]
async fn test_h2() -> io::Result<()> {
async fn h2() -> io::Result<()> {
let srv = test_server(move || {
HttpService::build()
.h2(|_| ok::<_, Error>(Response::ok()))
@ -81,7 +81,7 @@ async fn test_h2() -> io::Result<()> {
}
#[actix_rt::test]
async fn test_h2_1() -> io::Result<()> {
async fn h2_1() -> io::Result<()> {
let srv = test_server(move || {
HttpService::build()
.finish(|req: Request| {
@ -100,7 +100,7 @@ async fn test_h2_1() -> io::Result<()> {
}
#[actix_rt::test]
async fn test_h2_body() -> io::Result<()> {
async fn h2_body() -> io::Result<()> {
let data = "HELLOWORLD".to_owned().repeat(64 * 1024); // 640 KiB
let mut srv = test_server(move || {
HttpService::build()
@ -122,7 +122,7 @@ async fn test_h2_body() -> io::Result<()> {
}
#[actix_rt::test]
async fn test_h2_content_length() {
async fn h2_content_length() {
let srv = test_server(move || {
HttpService::build()
.h2(|req: Request| {
@ -164,7 +164,7 @@ async fn test_h2_content_length() {
}
#[actix_rt::test]
async fn test_h2_headers() {
async fn h2_headers() {
let data = STR.repeat(10);
let data2 = data.clone();
@ -229,7 +229,7 @@ const STR: &str = "Hello World Hello World Hello World Hello World Hello World \
Hello World Hello World Hello World Hello World Hello World";
#[actix_rt::test]
async fn test_h2_body2() {
async fn h2_body2() {
let mut srv = test_server(move || {
HttpService::build()
.h2(|_| ok::<_, Infallible>(Response::ok().set_body(STR)))
@ -247,7 +247,7 @@ async fn test_h2_body2() {
}
#[actix_rt::test]
async fn test_h2_head_empty() {
async fn h2_head_empty() {
let mut srv = test_server(move || {
HttpService::build()
.finish(|_| ok::<_, Infallible>(Response::ok().set_body(STR)))
@ -271,7 +271,7 @@ async fn test_h2_head_empty() {
}
#[actix_rt::test]
async fn test_h2_head_binary() {
async fn h2_head_binary() {
let mut srv = test_server(move || {
HttpService::build()
.h2(|_| ok::<_, Infallible>(Response::ok().set_body(STR)))
@ -294,7 +294,7 @@ async fn test_h2_head_binary() {
}
#[actix_rt::test]
async fn test_h2_head_binary2() {
async fn h2_head_binary2() {
let srv = test_server(move || {
HttpService::build()
.h2(|_| ok::<_, Infallible>(Response::ok().set_body(STR)))
@ -313,7 +313,7 @@ async fn test_h2_head_binary2() {
}
#[actix_rt::test]
async fn test_h2_body_length() {
async fn h2_body_length() {
let mut srv = test_server(move || {
HttpService::build()
.h2(|_| async {
@ -338,7 +338,7 @@ async fn test_h2_body_length() {
}
#[actix_rt::test]
async fn test_h2_body_chunked_explicit() {
async fn h2_body_chunked_explicit() {
let mut srv = test_server(move || {
HttpService::build()
.h2(|_| {
@ -366,7 +366,7 @@ async fn test_h2_body_chunked_explicit() {
}
#[actix_rt::test]
async fn test_h2_response_http_error_handling() {
async fn h2_response_http_error_handling() {
let mut srv = test_server(move || {
HttpService::build()
.h2(fn_service(|_| {
@ -406,7 +406,7 @@ impl From<BadRequest> for Response<BoxBody> {
}
#[actix_rt::test]
async fn test_h2_service_error() {
async fn h2_service_error() {
let mut srv = test_server(move || {
HttpService::build()
.h2(|_| err::<Response<BoxBody>, _>(BadRequest))
@ -424,7 +424,7 @@ async fn test_h2_service_error() {
}
#[actix_rt::test]
async fn test_h2_on_connect() {
async fn h2_on_connect() {
let srv = test_server(move || {
HttpService::build()
.on_connect_ext(|_, data| {

View File

@ -106,7 +106,7 @@ pub fn get_negotiated_alpn_protocol(
}
#[actix_rt::test]
async fn test_h1() -> io::Result<()> {
async fn h1() -> io::Result<()> {
let srv = test_server(move || {
HttpService::build()
.h1(|_| ok::<_, Error>(Response::ok()))
@ -120,7 +120,7 @@ async fn test_h1() -> io::Result<()> {
}
#[actix_rt::test]
async fn test_h2() -> io::Result<()> {
async fn h2() -> io::Result<()> {
let srv = test_server(move || {
HttpService::build()
.h2(|_| ok::<_, Error>(Response::ok()))
@ -134,7 +134,7 @@ async fn test_h2() -> io::Result<()> {
}
#[actix_rt::test]
async fn test_h1_1() -> io::Result<()> {
async fn h1_1() -> io::Result<()> {
let srv = test_server(move || {
HttpService::build()
.h1(|req: Request| {
@ -152,7 +152,7 @@ async fn test_h1_1() -> io::Result<()> {
}
#[actix_rt::test]
async fn test_h2_1() -> io::Result<()> {
async fn h2_1() -> io::Result<()> {
let srv = test_server(move || {
HttpService::build()
.finish(|req: Request| {
@ -170,7 +170,7 @@ async fn test_h2_1() -> io::Result<()> {
}
#[actix_rt::test]
async fn test_h2_body1() -> io::Result<()> {
async fn h2_body1() -> io::Result<()> {
let data = "HELLOWORLD".to_owned().repeat(64 * 1024);
let mut srv = test_server(move || {
HttpService::build()
@ -191,7 +191,7 @@ async fn test_h2_body1() -> io::Result<()> {
}
#[actix_rt::test]
async fn test_h2_content_length() {
async fn h2_content_length() {
let srv = test_server(move || {
HttpService::build()
.h2(|req: Request| {
@ -245,7 +245,7 @@ async fn test_h2_content_length() {
}
#[actix_rt::test]
async fn test_h2_headers() {
async fn h2_headers() {
let data = STR.repeat(10);
let data2 = data.clone();
@ -309,7 +309,7 @@ const STR: &str = "Hello World Hello World Hello World Hello World Hello World \
Hello World Hello World Hello World Hello World Hello World";
#[actix_rt::test]
async fn test_h2_body2() {
async fn h2_body2() {
let mut srv = test_server(move || {
HttpService::build()
.h2(|_| ok::<_, Infallible>(Response::ok().set_body(STR)))
@ -326,7 +326,7 @@ async fn test_h2_body2() {
}
#[actix_rt::test]
async fn test_h2_head_empty() {
async fn h2_head_empty() {
let mut srv = test_server(move || {
HttpService::build()
.finish(|_| ok::<_, Infallible>(Response::ok().set_body(STR)))
@ -352,7 +352,7 @@ async fn test_h2_head_empty() {
}
#[actix_rt::test]
async fn test_h2_head_binary() {
async fn h2_head_binary() {
let mut srv = test_server(move || {
HttpService::build()
.h2(|_| ok::<_, Infallible>(Response::ok().set_body(STR)))
@ -377,7 +377,7 @@ async fn test_h2_head_binary() {
}
#[actix_rt::test]
async fn test_h2_head_binary2() {
async fn h2_head_binary2() {
let srv = test_server(move || {
HttpService::build()
.h2(|_| ok::<_, Infallible>(Response::ok().set_body(STR)))
@ -398,7 +398,7 @@ async fn test_h2_head_binary2() {
}
#[actix_rt::test]
async fn test_h2_body_length() {
async fn h2_body_length() {
let mut srv = test_server(move || {
HttpService::build()
.h2(|_| {
@ -420,7 +420,7 @@ async fn test_h2_body_length() {
}
#[actix_rt::test]
async fn test_h2_body_chunked_explicit() {
async fn h2_body_chunked_explicit() {
let mut srv = test_server(move || {
HttpService::build()
.h2(|_| {
@ -447,7 +447,7 @@ async fn test_h2_body_chunked_explicit() {
}
#[actix_rt::test]
async fn test_h2_response_http_error_handling() {
async fn h2_response_http_error_handling() {
let mut srv = test_server(move || {
HttpService::build()
.h2(fn_factory_with_config(|_: ()| {
@ -486,7 +486,7 @@ impl From<BadRequest> for Response<BoxBody> {
}
#[actix_rt::test]
async fn test_h2_service_error() {
async fn h2_service_error() {
let mut srv = test_server(move || {
HttpService::build()
.h2(|_| err::<Response<BoxBody>, _>(BadRequest))
@ -503,7 +503,7 @@ async fn test_h2_service_error() {
}
#[actix_rt::test]
async fn test_h1_service_error() {
async fn h1_service_error() {
let mut srv = test_server(move || {
HttpService::build()
.h1(|_| err::<Response<BoxBody>, _>(BadRequest))
@ -524,7 +524,7 @@ const HTTP1_1_ALPN_PROTOCOL: &[u8] = b"http/1.1";
const CUSTOM_ALPN_PROTOCOL: &[u8] = b"custom";
#[actix_rt::test]
async fn test_alpn_h1() -> io::Result<()> {
async fn alpn_h1() -> io::Result<()> {
let srv = test_server(move || {
let mut config = tls_config();
config.alpn_protocols.push(CUSTOM_ALPN_PROTOCOL.to_vec());
@ -546,7 +546,7 @@ async fn test_alpn_h1() -> io::Result<()> {
}
#[actix_rt::test]
async fn test_alpn_h2() -> io::Result<()> {
async fn alpn_h2() -> io::Result<()> {
let srv = test_server(move || {
let mut config = tls_config();
config.alpn_protocols.push(CUSTOM_ALPN_PROTOCOL.to_vec());
@ -572,7 +572,7 @@ async fn test_alpn_h2() -> io::Result<()> {
}
#[actix_rt::test]
async fn test_alpn_h2_1() -> io::Result<()> {
async fn alpn_h2_1() -> io::Result<()> {
let srv = test_server(move || {
let mut config = tls_config();
config.alpn_protocols.push(CUSTOM_ALPN_PROTOCOL.to_vec());

View File

@ -850,7 +850,8 @@ async fn not_modified_spec_h1() {
Some(&header::HeaderValue::from_static("4")),
);
// server does not prevent payload from being sent but clients may choose not to read it
// TODO: this is probably a bug, especially since CL header can differ in length from the body
// TODO: this is probably a bug in the client, especially since CL header can differ in length
// from the body
assert!(!srv.load_body(res).await.unwrap().is_empty());
// TODO: add stream response tests

View File

@ -15,7 +15,7 @@ path = "src/lib.rs"
[dependencies]
actix-utils = "3.0.0"
actix-web = { version = "4.0.0-rc.1", default-features = false }
actix-web = { version = "4.0.0", default-features = false }
bytes = "1"
derive_more = "0.99.5"
@ -28,7 +28,7 @@ twoway = "0.2"
[dev-dependencies]
actix-rt = "2.2"
actix-http = "3.0.0-rc.1"
actix-http = "3.0.0"
futures-util = { version = "0.3.7", default-features = false, features = ["alloc"] }
tokio = { version = "1.8.4", features = ["sync"] }
tokio-stream = "0.1"

View File

@ -3,6 +3,77 @@
## Unreleased - 2021-xx-xx
## 0.5.0 - 2022-02-22
### Added
- Add `Path::as_str`. [#2590]
- Add `ResourceDef::set_name`. [#373][net#373]
- Add `RouterBuilder::push`. [#2612]
- Implement `IntoPatterns` for `bytestring::ByteString`. [#372][net#372]
- Introduce `ResourceDef::join`. [#380][net#380]
- Introduce `ResourceDef::pattern_iter` to get an iterator over all patterns in a multi-pattern resource. [#373][net#373]
- `Resource` is now implemented for `&mut Path<_>` and `RefMut<Path<_>>`. [#2568]
- Support `build_resource_path` on multi-pattern resources. [#2356]
- Support multi-pattern prefixes and joins. [#2356]
### Changed
- Change signature of `ResourceDef::capture_match_info_fn` to remove `user_data` parameter. [#2612]
- Deprecate `Path::path`. [#2590]
- Disallow prefix routes with tail segments. [#379][net#379]
- Enforce path separators on dynamic prefixes. [#378][net#378]
- Minimum supported Rust version (MSRV) is now 1.54.
- Prefix segments now always end with with a segment delimiter or end-of-input. [#2355]
- Prefix segments with trailing slashes define a trailing empty segment. [#2355]
- `Quoter::requote` now returns `Option<Vec<u8>>`. [#2613]
- Re-work `IntoPatterns` trait, adding a `Patterns` enum. [#372][net#372]
- Rename `Path::{len => segment_count}` to be more descriptive of its purpose. [#370][net#370]
- Rename `ResourceDef::{is_prefix_match => find_match}`. [#373][net#373]
- Rename `ResourceDef::{match_path => capture_match_info}`. [#373][net#373]
- Rename `ResourceDef::{match_path_checked => capture_match_info_fn}`. [#373][net#373]
- Rename `ResourceDef::{resource_path => resource_path_from_iter}`. [#371][net#371]
- Rename `ResourceDef::{resource_path_named => resource_path_from_map}`. [#371][net#371]
- Rename `Router::{*_checked => *_fn}`. [#373][net#373]
- Replace `Option<U>` with `U` in `Router` API. [#2612]
- `Resource` trait now uses an associated type, `Path`, instead of a generic parameter. [#2568]
- `ResourceDef::pattern` now returns the first pattern in multi-pattern resources. [#2356]
- `ResourceDef::resource_path_from_iter` now takes an `IntoIterator`. [#373][net#373]
- Return type of `ResourceDef::name` is now `Option<&str>`. [#373][net#373]
- Return type of `ResourceDef::pattern` is now `Option<&str>`. [#373][net#373]
### Fixed
- Fix `ResourceDef`'s `PartialEq` implementation. [#373][net#373]
- Fix segment interpolation leaving `Path` in unintended state after matching. [#368][net#368]
- Improve malformed path error message. [#384][net#384]
- `PathDeserializer` now decodes all percent encoded characters in dynamic segments. [#2566]
- Relax bounds on `Router::recognize*` and `ResourceDef::capture_match_info`. [#2612]
- Static patterns in multi-patterns are no longer interpreted as regex. [#366][net#366]
### Removed
- `ResourceDef::name_mut`. [#373][net#373]
- Unused `ResourceInfo`. [#2612]
[#2355]: https://github.com/actix/actix-web/pull/2355
[#2356]: https://github.com/actix/actix-web/pull/2356
[#2566]: https://github.com/actix/actix-net/pull/2566
[#2568]: https://github.com/actix/actix-web/pull/2568
[#2590]: https://github.com/actix/actix-web/pull/2590
[#2612]: https://github.com/actix/actix-web/pull/2612
[#2613]: https://github.com/actix/actix-web/pull/2613
[net#366]: https://github.com/actix/actix-net/pull/366
[net#368]: https://github.com/actix/actix-net/pull/368
[net#368]: https://github.com/actix/actix-net/pull/368
[net#370]: https://github.com/actix/actix-net/pull/370
[net#371]: https://github.com/actix/actix-net/pull/371
[net#372]: https://github.com/actix/actix-net/pull/372
[net#373]: https://github.com/actix/actix-net/pull/373
[net#378]: https://github.com/actix/actix-net/pull/378
[net#379]: https://github.com/actix/actix-net/pull/379
[net#380]: https://github.com/actix/actix-net/pull/380
[net#384]: https://github.com/actix/actix-net/pull/384
<details>
<summary>0.5.0 Pre-Releases</summary>
## 0.5.0-rc.3 - 2022-01-31
- Remove unused `ResourceInfo`. [#2612]
- Add `RouterBuilder::push`. [#2612]
@ -41,10 +112,10 @@
## 0.5.0-beta.2 - 2021-09-09
- Introduce `ResourceDef::join`. [#380]
- Disallow prefix routes with tail segments. [#379]
- Enforce path separators on dynamic prefixes. [#378]
- Improve malformed path error message. [#384]
- Introduce `ResourceDef::join`. [#380][net#380]
- Disallow prefix routes with tail segments. [#379][net#379]
- Enforce path separators on dynamic prefixes. [#378][net#378]
- Improve malformed path error message. [#384][net#384]
- Prefix segments now always end with with a segment delimiter or end-of-input. [#2355]
- Prefix segments with trailing slashes define a trailing empty segment. [#2355]
- Support multi-pattern prefixes and joins. [#2356]
@ -52,52 +123,54 @@
- Support `build_resource_path` on multi-pattern resources. [#2356]
- Minimum supported Rust version (MSRV) is now 1.51.
[#378]: https://github.com/actix/actix-net/pull/378
[#379]: https://github.com/actix/actix-net/pull/379
[#380]: https://github.com/actix/actix-net/pull/380
[#384]: https://github.com/actix/actix-net/pull/384
[net#378]: https://github.com/actix/actix-net/pull/378
[net#379]: https://github.com/actix/actix-net/pull/379
[net#380]: https://github.com/actix/actix-net/pull/380
[net#384]: https://github.com/actix/actix-net/pull/384
[#2355]: https://github.com/actix/actix-web/pull/2355
[#2356]: https://github.com/actix/actix-web/pull/2356
## 0.5.0-beta.1 - 2021-07-20
- Fix a bug in multi-patterns where static patterns are interpreted as regex. [#366]
- Introduce `ResourceDef::pattern_iter` to get an iterator over all patterns in a multi-pattern resource. [#373]
- Fix segment interpolation leaving `Path` in unintended state after matching. [#368]
- Fix `ResourceDef` `PartialEq` implementation. [#373]
- Re-work `IntoPatterns` trait, adding a `Patterns` enum. [#372]
- Implement `IntoPatterns` for `bytestring::ByteString`. [#372]
- Rename `Path::{len => segment_count}` to be more descriptive of it's purpose. [#370]
- Rename `ResourceDef::{resource_path => resource_path_from_iter}`. [#371]
- `ResourceDef::resource_path_from_iter` now takes an `IntoIterator`. [#373]
- Rename `ResourceDef::{resource_path_named => resource_path_from_map}`. [#371]
- Rename `ResourceDef::{is_prefix_match => find_match}`. [#373]
- Rename `ResourceDef::{match_path => capture_match_info}`. [#373]
- Rename `ResourceDef::{match_path_checked => capture_match_info_fn}`. [#373]
- Remove `ResourceDef::name_mut` and introduce `ResourceDef::set_name`. [#373]
- Rename `Router::{*_checked => *_fn}`. [#373]
- Return type of `ResourceDef::name` is now `Option<&str>`. [#373]
- Return type of `ResourceDef::pattern` is now `Option<&str>`. [#373]
- Fix a bug in multi-patterns where static patterns are interpreted as regex. [#366][net#366]
- Introduce `ResourceDef::pattern_iter` to get an iterator over all patterns in a multi-pattern resource. [#373][net#373]
- Fix segment interpolation leaving `Path` in unintended state after matching. [#368][net#368]
- Fix `ResourceDef` `PartialEq` implementation. [#373][net#373]
- Re-work `IntoPatterns` trait, adding a `Patterns` enum. [#372][net#372]
- Implement `IntoPatterns` for `bytestring::ByteString`. [#372][net#372]
- Rename `Path::{len => segment_count}` to be more descriptive of it's purpose. [#370][net#370]
- Rename `ResourceDef::{resource_path => resource_path_from_iter}`. [#371][net#371]
- `ResourceDef::resource_path_from_iter` now takes an `IntoIterator`. [#373][net#373]
- Rename `ResourceDef::{resource_path_named => resource_path_from_map}`. [#371][net#371]
- Rename `ResourceDef::{is_prefix_match => find_match}`. [#373][net#373]
- Rename `ResourceDef::{match_path => capture_match_info}`. [#373][net#373]
- Rename `ResourceDef::{match_path_checked => capture_match_info_fn}`. [#373][net#373]
- Remove `ResourceDef::name_mut` and introduce `ResourceDef::set_name`. [#373][net#373]
- Rename `Router::{*_checked => *_fn}`. [#373][net#373]
- Return type of `ResourceDef::name` is now `Option<&str>`. [#373][net#373]
- Return type of `ResourceDef::pattern` is now `Option<&str>`. [#373][net#373]
[#368]: https://github.com/actix/actix-net/pull/368
[#366]: https://github.com/actix/actix-net/pull/366
[#368]: https://github.com/actix/actix-net/pull/368
[#370]: https://github.com/actix/actix-net/pull/370
[#371]: https://github.com/actix/actix-net/pull/371
[#372]: https://github.com/actix/actix-net/pull/372
[#373]: https://github.com/actix/actix-net/pull/373
[net#368]: https://github.com/actix/actix-net/pull/368
[net#366]: https://github.com/actix/actix-net/pull/366
[net#368]: https://github.com/actix/actix-net/pull/368
[net#370]: https://github.com/actix/actix-net/pull/370
[net#371]: https://github.com/actix/actix-net/pull/371
[net#372]: https://github.com/actix/actix-net/pull/372
[net#373]: https://github.com/actix/actix-net/pull/373
</details>
## 0.4.0 - 2021-06-06
- When matching path parameters, `%25` is now kept in the percent-encoded form; no longer decoded to `%`. [#357]
- Path tail patterns now match new lines (`\n`) in request URL. [#360]
- Fixed a safety bug where `Path` could return a malformed string after percent decoding. [#359]
- Methods `Path::{add, add_static}` now take `impl Into<Cow<'static, str>>`. [#345]
- When matching path parameters, `%25` is now kept in the percent-encoded form; no longer decoded to `%`. [#357][net#357]
- Path tail patterns now match new lines (`\n`) in request URL. [#360][net#360]
- Fixed a safety bug where `Path` could return a malformed string after percent decoding. [#359][net#359]
- Methods `Path::{add, add_static}` now take `impl Into<Cow<'static, str>>`. [#345][net#345]
[#345]: https://github.com/actix/actix-net/pull/345
[#357]: https://github.com/actix/actix-net/pull/357
[#359]: https://github.com/actix/actix-net/pull/359
[#360]: https://github.com/actix/actix-net/pull/360
[net#345]: https://github.com/actix/actix-net/pull/345
[net#357]: https://github.com/actix/actix-net/pull/357
[net#359]: https://github.com/actix/actix-net/pull/359
[net#360]: https://github.com/actix/actix-net/pull/360
## 0.3.0 - 2019-12-31
@ -105,15 +178,15 @@
## 0.2.7 - 2021-02-06
- Add `Router::recognize_checked` [#247]
- Add `Router::recognize_checked` [#247][net#247]
[#247]: https://github.com/actix/actix-net/pull/247
[net#247]: https://github.com/actix/actix-net/pull/247
## 0.2.6 - 2021-01-09
- Use `bytestring` version range compatible with Bytes v1.0. [#246]
- Use `bytestring` version range compatible with Bytes v1.0. [#246][net#246]
[#246]: https://github.com/actix/actix-net/pull/246
[net#246]: https://github.com/actix/actix-net/pull/246
## 0.2.5 - 2020-09-20

View File

@ -1,6 +1,6 @@
[package]
name = "actix-router"
version = "0.5.0-rc.3"
version = "0.5.0"
authors = [
"Nikolay Kim <fafhrd91@gmail.com>",
"Ali MJ Al-Nasrawy <alimjalnasrawy@gmail.com>",

View File

@ -145,7 +145,8 @@ macro_rules! register {
concat!("/user/keys"),
concat!("/user/keys/", $p1),
];
std::array::IntoIter::new(arr)
IntoIterator::into_iter(arr)
}};
}
@ -158,7 +159,7 @@ fn call() -> impl Iterator<Item = &'static str> {
"/repos/rust-lang/rust/releases/1.51.0",
];
std::array::IntoIter::new(arr)
IntoIterator::into_iter(arr)
}
fn compare_routers(c: &mut Criterion) {

View File

@ -898,7 +898,7 @@ impl ResourceDef {
}
let pattern_re_set = RegexSet::new(re_set).unwrap();
let segments = segments.unwrap_or_else(Vec::new);
let segments = segments.unwrap_or_default();
(
PatternType::DynamicSet(pattern_re_set, pattern_data),

View File

@ -3,6 +3,10 @@
## Unreleased - 2021-xx-xx
## 0.1.0-beta.13 - 2022-02-16
- No significant changes since `0.1.0-beta.12`.
## 0.1.0-beta.12 - 2022-01-31
- Rename `TestServerConfig::{client_timeout => client_request_timeout}`. [#2611]

View File

@ -1,6 +1,6 @@
[package]
name = "actix-test"
version = "0.1.0-beta.12"
version = "0.1.0-beta.13"
authors = [
"Nikolay Kim <fafhrd91@gmail.com>",
"Rob Ede <robjtede@icloud.com>",
@ -28,14 +28,14 @@ rustls = ["tls-rustls", "actix-http/rustls", "awc/rustls"]
openssl = ["tls-openssl", "actix-http/openssl", "awc/openssl"]
[dependencies]
actix-codec = "0.4.1"
actix-http = "3.0.0-rc.1"
actix-http-test = "3.0.0-beta.12"
actix-codec = "0.5"
actix-http = "3.0.0"
actix-http-test = "3.0.0-beta.13"
actix-rt = "2.1"
actix-service = "2.0.0"
actix-utils = "3.0.0"
actix-web = { version = "4.0.0-rc.1", default-features = false, features = ["cookies"] }
awc = { version = "3.0.0-beta.20", default-features = false, features = ["cookies"] }
actix-web = { version = "4.0.0", default-features = false, features = ["cookies"] }
awc = { version = "3.0.0-beta.21", default-features = false, features = ["cookies"] }
futures-core = { version = "0.3.7", default-features = false, features = ["std"] }
futures-util = { version = "0.3.7", default-features = false, features = [] }

View File

@ -43,7 +43,7 @@ pub use actix_http_test::unused_addr;
use actix_service::{map_config, IntoServiceFactory, ServiceFactory, ServiceFactoryExt as _};
pub use actix_web::test::{
call_and_read_body, call_and_read_body_json, call_service, init_service, ok_service,
read_body, read_body_json, simple_service, TestRequest,
read_body, read_body_json, status_service, TestRequest,
};
use actix_web::{
body::MessageBody,

View File

@ -3,6 +3,10 @@
## Unreleased - 2021-xx-xx
## 4.0.0-beta.12 - 2022-02-16
- No significant changes since `4.0.0-beta.11`.
## 4.0.0-beta.11 - 2022-01-31
- No significant changes since `4.0.0-beta.10`.

View File

@ -1,6 +1,6 @@
[package]
name = "actix-web-actors"
version = "4.0.0-beta.11"
version = "4.0.0-beta.12"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Actix actors support for Actix Web"
keywords = ["actix", "http", "web", "framework", "async"]
@ -15,9 +15,9 @@ path = "src/lib.rs"
[dependencies]
actix = { version = "0.12.0", default-features = false }
actix-codec = "0.4.1"
actix-http = "3.0.0-rc.1"
actix-web = { version = "4.0.0-rc.1", default-features = false }
actix-codec = "0.5"
actix-http = "3.0.0"
actix-web = { version = "4.0.0", default-features = false }
bytes = "1"
bytestring = "1"
@ -27,8 +27,8 @@ tokio = { version = "1.8.4", features = ["sync"] }
[dev-dependencies]
actix-rt = "2.2"
actix-test = "0.1.0-beta.12"
awc = { version = "3.0.0-beta.20", default-features = false }
actix-test = "0.1.0-beta.13"
awc = { version = "3.0.0-beta.21", default-features = false }
env_logger = "0.9"
futures-util = { version = "0.3.7", default-features = false }

View File

@ -3,11 +3,11 @@
> Actix actors support for Actix Web.
[![crates.io](https://img.shields.io/crates/v/actix-web-actors?label=latest)](https://crates.io/crates/actix-web-actors)
[![Documentation](https://docs.rs/actix-web-actors/badge.svg?version=4.0.0-beta.11)](https://docs.rs/actix-web-actors/4.0.0-beta.11)
[![Documentation](https://docs.rs/actix-web-actors/badge.svg?version=4.0.0-beta.12)](https://docs.rs/actix-web-actors/4.0.0-beta.12)
[![Version](https://img.shields.io/badge/rustc-1.54+-ab6000.svg)](https://blog.rust-lang.org/2021/05/06/Rust-1.54.0.html)
![License](https://img.shields.io/crates/l/actix-web-actors.svg)
<br />
[![dependency status](https://deps.rs/crate/actix-web-actors/4.0.0-beta.11/status.svg)](https://deps.rs/crate/actix-web-actors/4.0.0-beta.11)
[![dependency status](https://deps.rs/crate/actix-web-actors/4.0.0-beta.12/status.svg)](https://deps.rs/crate/actix-web-actors/4.0.0-beta.12)
[![Download](https://img.shields.io/crates/d/actix-web-actors.svg)](https://crates.io/crates/actix-web-actors)
[![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x)

View File

@ -3,6 +3,15 @@
## Unreleased - 2021-xx-xx
## 4.0.0 - 2022-02-24
- Version aligned with `actix-web` and will remain in sync going forward.
- No significant changes since `0.5.0`.
## 0.5.0 - 2022-02-24
- No significant changes since `0.5.0-rc.2`.
## 0.5.0-rc.2 - 2022-02-01
- No significant changes since `0.5.0-rc.1`.

View File

@ -1,6 +1,6 @@
[package]
name = "actix-web-codegen"
version = "0.5.0-rc.2"
version = "4.0.0"
description = "Routing and runtime macros for Actix Web"
homepage = "https://actix.rs"
repository = "https://github.com/actix/actix-web.git"
@ -15,7 +15,7 @@ edition = "2018"
proc-macro = true
[dependencies]
actix-router = "0.5.0-rc.3"
actix-router = "0.5.0"
proc-macro2 = "1"
quote = "1"
syn = { version = "1", features = ["full", "parsing"] }
@ -23,9 +23,9 @@ syn = { version = "1", features = ["full", "parsing"] }
[dev-dependencies]
actix-macros = "0.2.3"
actix-rt = "2.2"
actix-test = "0.1.0-beta.12"
actix-test = "0.1.0-beta.13"
actix-utils = "3.0.0"
actix-web = "4.0.0-rc.1"
actix-web = "4.0.0"
futures-core = { version = "0.3.7", default-features = false, features = ["alloc"] }
trybuild = "1"

View File

@ -3,11 +3,11 @@
> Routing and runtime macros for Actix Web.
[![crates.io](https://img.shields.io/crates/v/actix-web-codegen?label=latest)](https://crates.io/crates/actix-web-codegen)
[![Documentation](https://docs.rs/actix-web-codegen/badge.svg?version=0.5.0-rc.2)](https://docs.rs/actix-web-codegen/0.5.0-rc.2)
[![Documentation](https://docs.rs/actix-web-codegen/badge.svg?version=4.0.0)](https://docs.rs/actix-web-codegen/4.0.0)
[![Version](https://img.shields.io/badge/rustc-1.54+-ab6000.svg)](https://blog.rust-lang.org/2021/05/06/Rust-1.54.0.html)
![License](https://img.shields.io/crates/l/actix-web-codegen.svg)
<br />
[![dependency status](https://deps.rs/crate/actix-web-codegen/0.5.0-rc.2/status.svg)](https://deps.rs/crate/actix-web-codegen/0.5.0-rc.2)
[![dependency status](https://deps.rs/crate/actix-web-codegen/4.0.0/status.svg)](https://deps.rs/crate/actix-web-codegen/4.0.0)
[![Download](https://img.shields.io/crates/d/actix-web-codegen.svg)](https://crates.io/crates/actix-web-codegen)
[![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x)

View File

@ -152,6 +152,10 @@ method_macro!(Patch, patch);
/// Marks async main function as the Actix Web system entry-point.
///
/// Note that Actix Web also works under `#[tokio::main]` since version 4.0. However, this macro is
/// still necessary for actor support (since actors use a `System`). Read more in the
/// [`actix_web::rt`](https://docs.rs/actix-web/4/actix_web/rt) module docs.
///
/// # Examples
/// ```
/// #[actix_web::main]

1327
actix-web/CHANGES.md Normal file

File diff suppressed because it is too large Load Diff

153
actix-web/Cargo.toml Normal file
View File

@ -0,0 +1,153 @@
[package]
name = "actix-web"
version = "4.0.0"
authors = [
"Nikolay Kim <fafhrd91@gmail.com>",
"Rob Ede <robjtede@icloud.com>",
]
description = "Actix Web is a powerful, pragmatic, and extremely fast web framework for Rust"
keywords = ["actix", "http", "web", "framework", "async"]
categories = [
"network-programming",
"asynchronous",
"web-programming::http-server",
"web-programming::websocket"
]
homepage = "https://actix.rs"
repository = "https://github.com/actix/actix-web.git"
license = "MIT OR Apache-2.0"
edition = "2018"
[package.metadata.docs.rs]
# features that docs.rs will build with
features = ["macros", "openssl", "rustls", "compress-brotli", "compress-gzip", "compress-zstd", "cookies", "secure-cookies"]
rustdoc-args = ["--cfg", "docsrs"]
[lib]
name = "actix_web"
path = "src/lib.rs"
[features]
default = ["macros", "compress-brotli", "compress-gzip", "compress-zstd", "cookies"]
# Brotli algorithm content-encoding support
compress-brotli = ["actix-http/compress-brotli", "__compress"]
# Gzip and deflate algorithms content-encoding support
compress-gzip = ["actix-http/compress-gzip", "__compress"]
# Zstd algorithm content-encoding support
compress-zstd = ["actix-http/compress-zstd", "__compress"]
# Routing and runtime proc macros
macros = [
"actix-macros",
"actix-web-codegen",
]
# Cookies support
cookies = ["cookie"]
# Secure & signed cookies
secure-cookies = ["cookies", "cookie/secure"]
# TLS via OpenSSL
openssl = ["actix-http/openssl", "actix-tls/accept", "actix-tls/openssl"]
# TLS via Rustls
rustls = ["actix-http/rustls", "actix-tls/accept", "actix-tls/rustls"]
# Internal (PRIVATE!) features used to aid testing and checking feature status.
# Don't rely on these whatsoever. They may disappear at anytime.
__compress = []
# io-uring feature only avaiable for Linux OSes.
experimental-io-uring = ["actix-server/io-uring"]
[dependencies]
actix-codec = "0.5"
actix-macros = { version = "0.2.3", optional = true }
actix-rt = { version = "2.6", default-features = false }
actix-server = "2"
actix-service = "2"
actix-utils = "3"
actix-tls = { version = "3", default-features = false, optional = true }
actix-http = { version = "3.0.0", features = ["http2", "ws"] }
actix-router = "0.5.0"
actix-web-codegen = { version = "4.0.0", optional = true }
ahash = "0.7"
bytes = "1"
bytestring = "1"
cfg-if = "1"
cookie = { version = "0.16", features = ["percent-encode"], optional = true }
derive_more = "0.99.5"
encoding_rs = "0.8"
futures-core = { version = "0.3.7", default-features = false }
futures-util = { version = "0.3.7", default-features = false }
itoa = "1"
language-tags = "0.3"
once_cell = "1.5"
log = "0.4"
mime = "0.3"
pin-project-lite = "0.2.7"
regex = "1.4"
serde = "1.0"
serde_json = "1.0"
serde_urlencoded = "0.7"
smallvec = "1.6.1"
socket2 = "0.4.0"
time = { version = "0.3", default-features = false, features = ["formatting"] }
url = "2.1"
[dev-dependencies]
actix-files = "0.6.0-beta.16"
actix-test = { version = "0.1.0-beta.13", features = ["openssl", "rustls"] }
awc = { version = "3.0.0-beta.21", features = ["openssl"] }
brotli = "3.3.3"
const-str = "0.3"
criterion = { version = "0.3", features = ["html_reports"] }
env_logger = "0.9"
flate2 = "1.0.13"
futures-util = { version = "0.3.7", default-features = false, features = ["std"] }
rand = "0.8"
rcgen = "0.8"
rustls-pemfile = "0.2"
serde = { version = "1.0", features = ["derive"] }
static_assertions = "1"
tls-openssl = { package = "openssl", version = "0.10.9" }
tls-rustls = { package = "rustls", version = "0.20.0" }
tokio = { version = "1.13.1", features = ["rt-multi-thread", "macros"] }
zstd = "0.10"
[[test]]
name = "test_server"
required-features = ["compress-brotli", "compress-gzip", "compress-zstd", "cookies"]
[[test]]
name = "compression"
required-features = ["compress-brotli", "compress-gzip", "compress-zstd"]
[[example]]
name = "basic"
required-features = ["compress-gzip"]
[[example]]
name = "uds"
required-features = ["compress-gzip"]
[[example]]
name = "on-connect"
required-features = []
[[bench]]
name = "server"
harness = false
[[bench]]
name = "service"
harness = false
[[bench]]
name = "responder"
harness = false

1
actix-web/LICENSE-APACHE Symbolic link
View File

@ -0,0 +1 @@
../LICENSE-APACHE

1
actix-web/LICENSE-MIT Symbolic link
View File

@ -0,0 +1 @@
../LICENSE-MIT

198
actix-web/MIGRATION-0.x.md Normal file
View File

@ -0,0 +1,198 @@
# 0.7.15
- The `' '` character is not percent decoded anymore before matching routes. If you need to use it in
your routes, you should use `%20`.
instead of
```rust
fn main() {
let app = App::new().resource("/my index", |r| {
r.method(http::Method::GET)
.with(index);
});
}
```
use
```rust
fn main() {
let app = App::new().resource("/my%20index", |r| {
r.method(http::Method::GET)
.with(index);
});
}
```
- If you used `AsyncResult::async` you need to replace it with `AsyncResult::future`
# 0.7.4
- `Route::with_config()`/`Route::with_async_config()` always passes configuration objects as tuple
even for handler with one parameter.
# 0.7
- `HttpRequest` does not implement `Stream` anymore. If you need to read request payload
use `HttpMessage::payload()` method.
instead of
```rust
fn index(req: HttpRequest) -> impl Responder {
req
.from_err()
.fold(...)
....
}
```
use `.payload()`
```rust
fn index(req: HttpRequest) -> impl Responder {
req
.payload() // <- get request payload stream
.from_err()
.fold(...)
....
}
```
- [Middleware](https://actix.rs/actix-web/actix_web/middleware/trait.Middleware.html)
trait uses `&HttpRequest` instead of `&mut HttpRequest`.
- Removed `Route::with2()` and `Route::with3()` use tuple of extractors instead.
instead of
```rust
fn index(query: Query<..>, info: Json<MyStruct) -> impl Responder {}
```
use tuple of extractors and use `.with()` for registration:
```rust
fn index((query, json): (Query<..>, Json<MyStruct)) -> impl Responder {}
```
- `Handler::handle()` uses `&self` instead of `&mut self`
- `Handler::handle()` accepts reference to `HttpRequest<_>` instead of value
- Removed deprecated `HttpServer::threads()`, use
[HttpServer::workers()](https://actix.rs/actix-web/actix_web/server/struct.HttpServer.html#method.workers) instead.
- Renamed `client::ClientConnectorError::Connector` to
`client::ClientConnectorError::Resolver`
- `Route::with()` does not return `ExtractorConfig`, to configure
extractor use `Route::with_config()`
instead of
```rust
fn main() {
let app = App::new().resource("/index.html", |r| {
r.method(http::Method::GET)
.with(index)
.limit(4096); // <- limit size of the payload
});
}
```
use
```rust
fn main() {
let app = App::new().resource("/index.html", |r| {
r.method(http::Method::GET)
.with_config(index, |cfg| { // <- register handler
cfg.limit(4096); // <- limit size of the payload
})
});
}
```
- `Route::with_async()` does not return `ExtractorConfig`, to configure
extractor use `Route::with_async_config()`
# 0.6
- `Path<T>` extractor return `ErrorNotFound` on failure instead of `ErrorBadRequest`
- `ws::Message::Close` now includes optional close reason.
`ws::CloseCode::Status` and `ws::CloseCode::Empty` have been removed.
- `HttpServer::threads()` renamed to `HttpServer::workers()`.
- `HttpServer::start_ssl()` and `HttpServer::start_tls()` deprecated.
Use `HttpServer::bind_ssl()` and `HttpServer::bind_tls()` instead.
- `HttpRequest::extensions()` returns read only reference to the request's Extension
`HttpRequest::extensions_mut()` returns mutable reference.
- Instead of
`use actix_web::middleware::{ CookieSessionBackend, CookieSessionError, RequestSession, Session, SessionBackend, SessionImpl, SessionStorage};`
use `actix_web::middleware::session`
`use actix_web::middleware::session{CookieSessionBackend, CookieSessionError, RequestSession, Session, SessionBackend, SessionImpl, SessionStorage};`
- `FromRequest::from_request()` accepts mutable reference to a request
- `FromRequest::Result` has to implement `Into<Reply<Self>>`
- [`Responder::respond_to()`](https://actix.rs/actix-web/actix_web/trait.Responder.html#tymethod.respond_to)
is generic over `S`
- Use `Query` extractor instead of HttpRequest::query()`.
```rust
fn index(q: Query<HashMap<String, String>>) -> Result<..> {
...
}
```
or
```rust
let q = Query::<HashMap<String, String>>::extract(req);
```
- Websocket operations are implemented as `WsWriter` trait.
you need to use `use actix_web::ws::WsWriter`
# 0.5
- `HttpResponseBuilder::body()`, `.finish()`, `.json()`
methods return `HttpResponse` instead of `Result<HttpResponse>`
- `actix_web::Method`, `actix_web::StatusCode`, `actix_web::Version`
moved to `actix_web::http` module
- `actix_web::header` moved to `actix_web::http::header`
- `NormalizePath` moved to `actix_web::http` module
- `HttpServer` moved to `actix_web::server`, added new `actix_web::server::new()` function,
shortcut for `actix_web::server::HttpServer::new()`
- `DefaultHeaders` middleware does not use separate builder, all builder methods moved to type itself
- `StaticFiles::new()`'s show_index parameter removed, use `show_files_listing()` method instead.
- `CookieSessionBackendBuilder` removed, all methods moved to `CookieSessionBackend` type
- `actix_web::httpcodes` module is deprecated, `HttpResponse::Ok()`, `HttpResponse::Found()` and other `HttpResponse::XXX()`
functions should be used instead
- `ClientRequestBuilder::body()` returns `Result<_, actix_web::Error>`
instead of `Result<_, http::Error>`
- `Application` renamed to a `App`
- `actix_web::Reply`, `actix_web::Resource` moved to `actix_web::dev`

337
actix-web/MIGRATION-1.0.md Normal file
View File

@ -0,0 +1,337 @@
## 1.0.1
- Cors middleware has been moved to `actix-cors` crate
instead of
```rust
use actix_web::middleware::cors::Cors;
```
use
```rust
use actix_cors::Cors;
```
- Identity middleware has been moved to `actix-identity` crate
instead of
```rust
use actix_web::middleware::identity::{Identity, CookieIdentityPolicy, IdentityService};
```
use
```rust
use actix_identity::{Identity, CookieIdentityPolicy, IdentityService};
```
## 1.0.0
- Extractor configuration. In version 1.0 this is handled with the new `Data` mechanism for both setting and retrieving the configuration
instead of
```rust
#[derive(Default)]
struct ExtractorConfig {
config: String,
}
impl FromRequest for YourExtractor {
type Config = ExtractorConfig;
type Result = Result<YourExtractor, Error>;
fn from_request(req: &HttpRequest, cfg: &Self::Config) -> Self::Result {
println!("use the config: {:?}", cfg.config);
...
}
}
App::new().resource("/route_with_config", |r| {
r.post().with_config(handler_fn, |cfg| {
cfg.0.config = "test".to_string();
})
})
```
use the HttpRequest to get the configuration like any other `Data` with `req.app_data::<C>()` and set it with the `data()` method on the `resource`
```rust
#[derive(Default)]
struct ExtractorConfig {
config: String,
}
impl FromRequest for YourExtractor {
type Error = Error;
type Future = Result<Self, Self::Error>;
type Config = ExtractorConfig;
fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {
let cfg = req.app_data::<ExtractorConfig>();
println!("config data?: {:?}", cfg.unwrap().role);
...
}
}
App::new().service(
resource("/route_with_config")
.data(ExtractorConfig {
config: "test".to_string(),
})
.route(post().to(handler_fn)),
)
```
- Resource registration. 1.0 version uses generalized resource
registration via `.service()` method.
instead of
```rust
App.new().resource("/welcome", |r| r.f(welcome))
```
use App's or Scope's `.service()` method. `.service()` method accepts
object that implements `HttpServiceFactory` trait. By default
actix-web provides `Resource` and `Scope` services.
```rust
App.new().service(
web::resource("/welcome")
.route(web::get().to(welcome))
.route(web::post().to(post_handler))
```
- Scope registration.
instead of
```rust
let app = App::new().scope("/{project_id}", |scope| {
scope
.resource("/path1", |r| r.f(|_| HttpResponse::Ok()))
.resource("/path2", |r| r.f(|_| HttpResponse::Ok()))
.resource("/path3", |r| r.f(|_| HttpResponse::MethodNotAllowed()))
});
```
use `.service()` for registration and `web::scope()` as scope object factory.
```rust
let app = App::new().service(
web::scope("/{project_id}")
.service(web::resource("/path1").to(|| HttpResponse::Ok()))
.service(web::resource("/path2").to(|| HttpResponse::Ok()))
.service(web::resource("/path3").to(|| HttpResponse::MethodNotAllowed()))
);
```
- `.with()`, `.with_async()` registration methods have been renamed to `.to()` and `.to_async()`.
instead of
```rust
App.new().resource("/welcome", |r| r.with(welcome))
```
use `.to()` or `.to_async()` methods
```rust
App.new().service(web::resource("/welcome").to(welcome))
```
- Passing arguments to handler with extractors, multiple arguments are allowed
instead of
```rust
fn welcome((body, req): (Bytes, HttpRequest)) -> ... {
...
}
```
use multiple arguments
```rust
fn welcome(body: Bytes, req: HttpRequest) -> ... {
...
}
```
- `.f()`, `.a()` and `.h()` handler registration methods have been removed.
Use `.to()` for handlers and `.to_async()` for async handlers. Handler function
must use extractors.
instead of
```rust
App.new().resource("/welcome", |r| r.f(welcome))
```
use App's `to()` or `to_async()` methods
```rust
App.new().service(web::resource("/welcome").to(welcome))
```
- `HttpRequest` does not provide access to request's payload stream.
instead of
```rust
fn index(req: &HttpRequest) -> Box<Future<Item=HttpResponse, Error=Error>> {
req
.payload()
.from_err()
.fold((), |_, chunk| {
...
})
.map(|_| HttpResponse::Ok().finish())
.responder()
}
```
use `Payload` extractor
```rust
fn index(stream: web::Payload) -> impl Future<Item=HttpResponse, Error=Error> {
stream
.from_err()
.fold((), |_, chunk| {
...
})
.map(|_| HttpResponse::Ok().finish())
}
```
- `State` is now `Data`. You register Data during the App initialization process
and then access it from handlers either using a Data extractor or using
HttpRequest's api.
instead of
```rust
App.with_state(T)
```
use App's `data` method
```rust
App.new()
.data(T)
```
and either use the Data extractor within your handler
```rust
use actix_web::web::Data;
fn endpoint_handler(Data<T>)){
...
}
```
.. or access your Data element from the HttpRequest
```rust
fn endpoint_handler(req: HttpRequest) {
let data: Option<Data<T>> = req.app_data::<T>();
}
```
- AsyncResponder is removed, use `.to_async()` registration method and `impl Future<>` as result type.
instead of
```rust
use actix_web::AsyncResponder;
fn endpoint_handler(...) -> impl Future<Item=HttpResponse, Error=Error>{
...
.responder()
}
```
.. simply omit AsyncResponder and the corresponding responder() finish method
- Middleware
instead of
```rust
let app = App::new()
.middleware(middleware::Logger::default())
```
use `.wrap()` method
```rust
let app = App::new()
.wrap(middleware::Logger::default())
.route("/index.html", web::get().to(index));
```
- `HttpRequest::body()`, `HttpRequest::urlencoded()`, `HttpRequest::json()`, `HttpRequest::multipart()`
method have been removed. Use `Bytes`, `String`, `Form`, `Json`, `Multipart` extractors instead.
instead of
```rust
fn index(req: &HttpRequest) -> Responder {
req.body()
.and_then(|body| {
...
})
}
```
use
```rust
fn index(body: Bytes) -> Responder {
...
}
```
- `actix_web::server` module has been removed. To start http server use `actix_web::HttpServer` type
- StaticFiles and NamedFile have been moved to a separate crate.
instead of `use actix_web::fs::StaticFile`
use `use actix_files::Files`
instead of `use actix_web::fs::Namedfile`
use `use actix_files::NamedFile`
- Multipart has been moved to a separate crate.
instead of `use actix_web::multipart::Multipart`
use `use actix_multipart::Multipart`
- Response compression is not enabled by default.
To enable, use `Compress` middleware, `App::new().wrap(Compress::default())`.
- Session middleware moved to actix-session crate
- Actors support have been moved to `actix-web-actors` crate
- Custom Error
Instead of error_response method alone, ResponseError now provides two methods: error_response and render_response respectively. Where, error_response creates the error response and render_response returns the error response to the caller.
Simplest migration from 0.7 to 1.0 shall include below method to the custom implementation of ResponseError:
```rust
fn render_response(&self) -> HttpResponse {
self.error_response()
}
```

View File

@ -0,0 +1,48 @@
# Migrating to 2.0.0
- `HttpServer::start()` renamed to `HttpServer::run()`. It also possible to
`.await` on `run` method result, in that case it awaits server exit.
- `App::register_data()` renamed to `App::app_data()` and accepts any type `T: 'static`.
Stored data is available via `HttpRequest::app_data()` method at runtime.
- Extractor configuration must be registered with `App::app_data()` instead of `App::data()`
- Sync handlers has been removed. `.to_async()` method has been renamed to `.to()`
replace `fn` with `async fn` to convert sync handler to async
- `actix_http_test::TestServer` moved to `actix_web::test` module. To start
test server use `test::start()` or `test_start_with_config()` methods
- `ResponseError` trait has been reafctored. `ResponseError::error_response()` renders
http response.
- Feature `rust-tls` renamed to `rustls`
instead of
```rust
actix-web = { version = "2.0.0", features = ["rust-tls"] }
```
use
```rust
actix-web = { version = "2.0.0", features = ["rustls"] }
```
- Feature `ssl` renamed to `openssl`
instead of
```rust
actix-web = { version = "2.0.0", features = ["ssl"] }
```
use
```rust
actix-web = { version = "2.0.0", features = ["openssl"] }
```
- `Cors` builder now requires that you call `.finish()` to construct the middleware

View File

@ -0,0 +1,53 @@
# Migrating to 3.0.0
- The return type for `ServiceRequest::app_data::<T>()` was changed from returning a `Data<T>` to
simply a `T`. To access a `Data<T>` use `ServiceRequest::app_data::<Data<T>>()`.
- Cookie handling has been offloaded to the `cookie` crate:
- `USERINFO_ENCODE_SET` is no longer exposed. Percent-encoding is still supported; check docs.
- Some types now require lifetime parameters.
- The time crate was updated to `v0.2`, a major breaking change to the time crate, which affects
any `actix-web` method previously expecting a time v0.1 input.
- Setting a cookie's SameSite property, explicitly, to `SameSite::None` will now
result in `SameSite=None` being sent with the response Set-Cookie header.
To create a cookie without a SameSite attribute, remove any calls setting same_site.
- actix-http support for Actors messages was moved to actix-http crate and is enabled
with feature `actors`
- content_length function is removed from actix-http.
You can set Content-Length by normally setting the response body or calling no_chunking function.
- `BodySize::Sized64` variant has been removed. `BodySize::Sized` now receives a
`u64` instead of a `usize`.
- Code that was using `path.<index>` to access a `web::Path<(A, B, C)>`s elements now needs to use
destructuring or `.into_inner()`. For example:
```rust
// Previously:
async fn some_route(path: web::Path<(String, String)>) -> String {
format!("Hello, {} {}", path.0, path.1)
}
// Now (this also worked before):
async fn some_route(path: web::Path<(String, String)>) -> String {
let (first_name, last_name) = path.into_inner();
format!("Hello, {} {}", first_name, last_name)
}
// Or (this wasn't previously supported):
async fn some_route(web::Path((first_name, last_name)): web::Path<(String, String)>) -> String {
format!("Hello, {} {}", first_name, last_name)
}
```
- `middleware::NormalizePath` can now also be configured to trim trailing slashes instead of always keeping one.
It will need `middleware::normalize::TrailingSlash` when being constructed with `NormalizePath::new(...)`,
or for an easier migration you can replace `wrap(middleware::NormalizePath)` with `wrap(middleware::NormalizePath::new(TrailingSlash::MergeOnly))`.
- `HttpServer::maxconn` is renamed to the more expressive `HttpServer::max_connections`.
- `HttpServer::maxconnrate` is renamed to the more expressive `HttpServer::max_connection_rate`.

483
actix-web/MIGRATION-4.0.md Normal file
View File

@ -0,0 +1,483 @@
# Migrating to 4.0.0
This guide walks you through the process of migrating from v3.x.y to v4.x.y.
If you are migrating to v4.x.y from an older version of Actix Web (v2.x.y or earlier), check out the other historical migration notes in this folder.
This document is not designed to be exhaustive—it focuses on the most significant changes coming in v4. You can find an exhaustive changelog in the changelogs for [`actix-web`](./CHANGES.md#400---2022-02-25) and [`actix-http`](../actix-http/CHANGES.md#300---2022-02-25), complete of PR links. If you think that some of the changes that we omitted deserve to be called out in this document, please open an issue or submit a PR.
Headings marked with :warning: are **breaking behavioral changes**. They will probably not surface as compile-time errors though automated tests _might_ detect their effects on your app.
## Table of Contents:
- [MSRV](#msrv)
- [Tokio v1 Ecosystem](#tokio-v1-ecosystem)
- [Module Structure](#module-structure)
- [`NormalizePath` Middleware :warning:](#normalizepath-middleware-warning)
- [Server Settings :warning:](#server-settings-warning)
- [`FromRequest` Trait](#fromrequest-trait)
- [Compression Feature Flags](#compression-feature-flags)
- [`web::Path`](#webpath)
- [Rustls Crate Upgrade](#rustls-crate-upgrade)
- [Removed `awc` Client Re-export](#removed-awc-client-re-export)
- [Integration Testing Utils Moved To `actix-test`](#integration-testing-utils-moved-to-actix-test)
- [Header APIs](#header-apis)
- [Response Body Types](#response-body-types)
- [Middleware Trait APIs](#middleware-trait-apis)
- [`Responder` Trait](#responder-trait)
- [`App::data` Deprecation :warning:](#appdata-deprecation-warning)
- [Direct Dependency On `actix-rt` And `actix-service`](#direct-dependency-on-actix-rt-and-actix-service)
- [Server Must Be Polled :warning:](#server-must-be-polled-warning)
- [Guards API](#guards-api)
- [Returning `HttpResponse` synchronously](#returning-httpresponse-synchronously)
- [`#[actix_web::main]` and `#[tokio::main]`](#actixwebmain-and-tokiomain)
- [`web::block`](#webblock)
## MSRV
The MSRV of Actix Web has been raised from 1.42 to 1.54.
## Tokio v1 Ecosystem
Actix Web v4 is now underpinned by `tokio`'s v1 ecosystem.
`cargo` supports having multiple versions of the same crate within the same dependency tree, but `tokio` v1 does not interoperate transparently with its previous versions (v0.2, v0.1). Some of your dependencies might rely on `tokio`, either directly or indirectly—if they are using an older version of `tokio`, check if an update is available.
The following command can help you to identify these dependencies:
```sh
# Find all crates in your dependency tree that depend on `tokio`
# It also reports the different versions of `tokio` in your dependency tree.
cargo tree -i tokio
# if you depend on multiple versions of tokio, use this command to
# list the dependencies relying on a specific version of tokio:
cargo tree -i tokio:0.2.25
```
## Module Structure
Lots of modules have been re-organized in this release. If a compile error refers to "item XYZ not found in module..." or "module XYZ not found", check the [documentation on docs.rs](https://docs.rs/actix-web) to search for items' new locations.
## `NormalizePath` Middleware :warning:
The default `NormalizePath` behavior now strips trailing slashes by default. This was the _documented_ behaviour in Actix Web v3, but the _actual_ behaviour differed. The discrepancy has now been resolved.
As a consequence of this change, routes defined with trailing slashes will become inaccessible when using `NormalizePath::default()`. Calling `NormalizePath::default()` will log a warning. We suggest to use `new` or `trim`.
```diff
- #[get("/test/")]
+ #[get("/test")]
async fn handler() {
App::new()
- .wrap(NormalizePath::default())
+ .wrap(NormalizePath::trim())
```
Alternatively, explicitly require trailing slashes: `NormalizePath::new(TrailingSlash::Always)`.
## Server Settings :warning:
Until Actix Web v4, the underlying `actix-server` crate used the number of available **logical** cores as the default number of worker threads. The new default is the number of [physical CPU cores available](https://github.com/actix/actix-net/commit/3a3d654c). For more information about this change, refer to [this analysis](https://github.com/actix/actix-web/issues/957).
If you notice performance regressions, please open a new issue detailing your observations.
## `FromRequest` Trait
The associated type `Config` of `FromRequest` was removed. If you have custom extractors, you can just remove this implementation and refer to config types directly, if required.
```diff
impl FromRequest for MyExtractor {
- type Config = ();
}
```
Consequently, the `FromRequest::configure` method was also removed. Config for extractors is still provided using `App::app_data` but should now be constructed in a standalone way.
## Compression Feature Flags
The `compress` feature flag has been split into more granular feature flags, one for each supported algorithm (brotli, gzip, zstd). By default, all compression algorithms are enabled. If you want to select specific compression codecs, the new flags are:
- `compress-brotli`
- `compress-gzip`
- `compress-zstd`
## `web::Path`
The inner field for `web::Path` is now private. It was causing ambiguity when trying to use tuple indexing due to its `Deref` implementation.
```diff
- async fn handler(web::Path((foo, bar)): web::Path<(String, String)>) {
+ async fn handler(params: web::Path<(String, String)>) {
+ let (foo, bar) = params.into_inner();
```
## Rustls Crate Upgrade
Actix Web now depends on version 0.20 of `rustls`. As a result, the server config builder has changed. [See the updated example project.](https://github.com/actix/examples/tree/master/https-tls/rustls/)
## Removed `awc` Client Re-export
Actix Web's sister crate `awc` is no longer re-exported through the `client` module. This allows `awc` to have its own release cadence—its breaking changes are no longer blocked by Actix Web's (more conservative) release schedule.
```diff
- use actix_web::client::Client;
+ use awc::Client;
```
## Integration Testing Utils Moved To `actix-test`
`TestServer` has been moved to its own crate, [`actix-test`](https://docs.rs/actix-test).
```diff
- use use actix_web::test::start;
+ use use actix_test::start;
```
`TestServer` previously lived in `actix_web::test`, but it depends on `awc` which is no longer part of Actix Web's public API (see above).
## Header APIs
Header related APIs have been standardized across all `actix-*` crates. The terminology now better matches the underlying `HeaderMap` naming conventions.
In short, "insert" always indicates that any existing headers with the same name are overridden, while "append" is used for adding with no removal (e.g. multi-valued headers).
For request and response builder APIs, the new methods provide a unified interface for adding key-value pairs _and_ typed headers, which can often be more expressive.
```diff
- .set_header("Api-Key", "1234")
+ .insert_header(("Api-Key", "1234"))
- .header("Api-Key", "1234")
+ .append_header(("Api-Key", "1234"))
- .set(ContentType::json())
+ .insert_header(ContentType::json())
```
We chose to deprecate most of the old methods instead of removing them immediately—the warning notes will guide you on how to update.
## Response Body Types
There have been a lot of changes to response body types. They are now more expressive and their purpose should be more intuitive.
We have boosted the quality and completeness of the documentation for all items in the [`body` module](https://docs.rs/actix-web/4/actix_web/body).
### `ResponseBody`
`ResponseBody` is gone. Its purpose was confusing and has been replaced by better components.
### `Body`
`Body` is also gone. In combination with `ResponseBody`, the API it provided was sub-optimal and did not encourage expressive types. Here are the equivalents in the new system (check docs):
- `Body::None` => `body::None::new()`
- `Body::Empty` => `()` / `web::Bytes::new()`
- `Body::Bytes` => `web::Bytes::from(...)`
- `Body::Message` => `.boxed()` / `BoxBody`
### `BoxBody`
`BoxBody` is a new type-erased body type.
It can be useful when writing handlers, responders, and middleware when you want to trade a (very) small amount of performance for a simpler type.
Creating a boxed body is done most efficiently by calling [`.boxed()`](https://docs.rs/actix-web/4/actix_web/body/trait.MessageBody.html#method.boxed) on a `MessageBody` type.
### `EitherBody`
`EitherBody` is a new "either" type that implements `MessageBody`
It is particularly useful in middleware that can bail early, returning their own response plus body type. By default the "right" variant is `BoxBody` (i.e., `EitherBody<B>` === `EitherBody<B, BoxBody>`) but it can be anything that implements `MessageBody`.
For example, it will be common among middleware which value performance of the hot path to use:
```rust
type Response = Result<ServiceResponse<EitherBody<B>>, Error>
```
This can be read (ignoring the `Result`) as "resolves with a `ServiceResponse` that is either the inner service's `B` body type or a boxed body type from elsewhere, likely constructed within the middleware itself". Of course, if your middleware contains only simple string other/error responses, it's possible to use them without boxes at the cost of a less simple implementation:
```rust
type Response = Result<ServiceResponse<EitherBody<B, String>>, Error>
```
### Error Handlers
`ErrorHandlers` is a commonly used middleware that has changed in design slightly due to the other body type changes.
In particular, an implicit `EitherBody` is used in the `ErrorHandlerResponse<B>` type. An `ErrorHandlerResponse<B>` now expects a `ServiceResponse<EitherBody<B>>` to be returned within response variants. The following is a migration for an error handler that **only modifies** the response argument (left body).
```diff
fn add_error_header<B>(mut res: ServiceResponse<B>) -> Result<ErrorHandlerResponse<B>, Error> {
res.response_mut().headers_mut().insert(
header::CONTENT_TYPE,
header::HeaderValue::from_static("Error"),
);
- Ok(ErrorHandlerResponse::Response(res))
+ Ok(ErrorHandlerResponse::Response(res.map_into_left_body()))
}
```
The following is a migration for an error handler that creates a new response instead (right body).
```diff
fn error_handler<B>(res: ServiceResponse<B>) -> Result<ErrorHandlerResponse<B>, Error> {
- let req = res.request().clone();
+ let (req, _res) = res.into_parts();
let res = actix_files::NamedFile::open("./templates/404.html")?
.set_status_code(StatusCode::NOT_FOUND)
- .into_response(&req)?
- .into_body();
+ .into_response(&req);
- let res = ServiceResponse::new(req, res);
+ let res = ServiceResponse::new(req, res).map_into_right_body();
Ok(ErrorHandlerResponse::Response(res))
}
```
## Middleware Trait APIs
The underlying traits that are used for creating middleware, `Service`, `ServiceFactory`, and `Transform`, have changed in design.
- The associated `Request` type has moved to the type parameter position in order to allow multiple request implementations in other areas of the service stack.
- The `self` arguments in `Service` have changed from exclusive (mutable) borrows to shared (immutable) borrows. Since most service layers, such as middleware, do not host mutable state, it reduces the runtime overhead in places where a `RefCell` used to be required for wrapping an inner service.
- We've also introduced some macros that reduce boilerplate when implementing `poll_ready`.
- Further to the guidance on [response body types](#response-body-types), any use of the old methods on `ServiceResponse` designed to match up body types (e.g., the old `into_body` method), should be replaced with an explicit response body type utilizing `EitherBody<B>`.
A typical migration would look like this:
```diff
use std::{
- cell::RefCell,
future::Future,
pin::Pin,
rc::Rc,
- task::{Context, Poll},
};
use actix_web::{
dev::{Service, ServiceRequest, ServiceResponse, Transform},
Error,
};
use futures_util::future::{ok, LocalBoxFuture, Ready};
pub struct SayHi;
- impl<S, B> Transform<S> for SayHi
+ impl<S, B> Transform<S, ServiceRequest> for SayHi
where
- S: Service<Request = ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
+ S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
S::Future: 'static,
B: 'static,
{
- type Request = ServiceRequest;
type Response = ServiceResponse<B>;
type Error = Error;
type InitError = ();
type Transform = SayHiMiddleware<S>;
type Future = Ready<Result<Self::Transform, Self::InitError>>;
fn new_transform(&self, service: S) -> Self::Future {
ok(SayHiMiddleware {
- service: Rc::new(RefCell::new(service)),
+ service: Rc::new(service),
})
}
}
pub struct SayHiMiddleware<S> {
- service: Rc<RefCell<S>>,
+ service: Rc<S>,
}
- impl<S, B> Service for SayHiMiddleware<S>
+ impl<S, B> Service<ServiceRequest> for SayHiMiddleware<S>
where
- S: Service<Request = ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
+ S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
S::Future: 'static,
B: 'static,
{
- type Request = ServiceRequest;
type Response = ServiceResponse<B>;
type Error = Error;
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
- fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
- self.service.poll_ready(cx)
- }
+ actix_web::dev::forward_ready!(service);
- fn call(&mut self, req: ServiceRequest) -> Self::Future {
+ fn call(&self, req: ServiceRequest) -> Self::Future {
println!("Hi from start. You requested: {}", req.path());
let fut = self.service.call(req);
Box::pin(async move {
let res = fut.await?;
println!("Hi from response");
Ok(res)
})
}
}
```
This new design is forward-looking and should ease transition to traits that support the upcoming Generic Associated Type (GAT) feature in Rust while also trimming down the boilerplate required to implement middleware.
We understand that creating middleware is still a pain point for Actix Web and we hope to provide [an even more ergonomic solution](https://docs.rs/actix-web-lab/0.11.0/actix_web_lab/middleware/fn.from_fn.html) in a v4.x release.
## `Responder` Trait
The `Responder` trait's interface has changed. Errors should be handled and converted to responses within the `respond_to` method. It's also no longer async so the associated `type Future` has been removed; there was no compelling use case found for it. These changes simplify the interface and implementation a lot.
Now that more emphasis is placed on expressive body types, as explained in the [body types migration section](#response-body-types), this trait has introduced an associated `type Body`. The simplest migration will be to use `BoxBody` + `.map_into_boxed_body()` but if there is a more expressive type for your responder then try to use that instead.
```diff
impl Responder for &'static str {
- type Error = Error;
- type Future = Ready<Result<HttpResponse, Error>>;
+ type Body = &'static str;
- fn respond_to(self, req: &HttpRequest) -> Self::Future {
+ fn respond_to(self, req: &HttpRequest) -> HttpResponse<Self::Body> {
let res = HttpResponse::build(StatusCode::OK)
.content_type("text/plain; charset=utf-8")
.body(self);
- ok(res)
+ res
}
}
```
## `App::data` Deprecation :warning:
The `App::data` method is deprecated. Replace instances of this with `App::app_data`. Exposing both methods led to lots of confusion when trying to extract the data in handlers. Now, when using the `Data` wrapper, the type you put in to `app_data` is the same type you extract in handler arguments.
You may need to review the [guidance on shared mutable state](https://docs.rs/actix-web/4/actix_web/struct.App.html#shared-mutable-state) in order to migrate this correctly.
```diff
use actix_web::web::Data;
#[get("/")]
async fn handler(my_state: Data<MyState>) -> { todo!() }
HttpServer::new(|| {
- App::new()
- .data(MyState::default())
- .service(hander)
+ let my_state: Data<MyState> = Data::new(MyState::default());
+
+ App::new()
+ .app_data(my_state)
+ .service(hander)
})
```
## Direct Dependency On `actix-rt` And `actix-service`
Improvements to module management and re-exports have resulted in not needing direct dependencies on these underlying crates for the vast majority of cases. In particular:
- all traits necessary for creating middlewares are now re-exported through the `dev` modules;
- `#[actix_web::test]` now exists for async test definitions.
Relying on these re-exports will ease the transition to future versions of Actix Web.
```diff
- use actix_service::{Service, Transform};
+ use actix_web::dev::{Service, Transform};
```
```diff
- #[actix_rt::test]
+ #[actix_web::test]
async fn test_thing() {
```
## Server Must Be Polled :warning:
In order to _start_ serving requests, the `Server` object returned from `run` **must** be `poll`ed, `await`ed, or `spawn`ed. This was done to prevent unexpected behavior and ensure that things like signal handlers are able to function correctly when enabled.
For example, in this contrived example where the server is started and then the main thread is sent to sleep, the server will no longer be able to serve requests with v4.0:
```rust
#[actix_web::main]
async fn main() {
HttpServer::new(|| App::new().default_service(web::to(HttpResponse::Conflict)))
.bind(("127.0.0.1", 8080))
.unwrap()
.run();
thread::sleep(Duration::from_secs(1000));
}
```
## Guards API
Implementors of routing guards will need to use the modified interface of the `Guard` trait. The API is more flexible than before. See [guard module docs](https://docs.rs/actix-web/4/actix_web/guard/struct.GuardContext.html) for more details.
```diff
struct MethodGuard(HttpMethod);
impl Guard for MethodGuard {
- fn check(&self, request: &RequestHead) -> bool {
+ fn check(&self, ctx: &GuardContext<'_>) -> bool {
- request.method == self.0
+ ctx.head().method == self.0
}
}
```
## Returning `HttpResponse` synchronously
The implementation of `Future` for `HttpResponse` was removed because it was largely useless for all but the simplest handlers like `web::to(|| HttpResponse::Ok().finish())`. It also caused false positives on the `async_yields_async` clippy lint in reasonable scenarios. The compiler errors will looks something like:
```
web::to(|| HttpResponse::Ok().finish())
^^^^^^^ the trait `Handler<_>` is not implemented for `[closure@...]`
```
This form should be replaced with explicit async functions and closures:
```diff
- fn handler() -> HttpResponse {
+ async fn handler() -> HttpResponse {
HttpResponse::Ok().finish()
}
```
```diff
- web::to(|| HttpResponse::Ok().finish())
+ web::to(|| async { HttpResponse::Ok().finish() })
```
Or, for these extremely simple cases, utilise an `HttpResponseBuilder`:
```diff
- web::to(|| HttpResponse::Ok().finish())
+ web::to(HttpResponse::Ok)
```
## `#[actix_web::main]` and `#[tokio::main]`
Actix Web now works seamlessly with the primary way of starting a multi-threaded Tokio runtime, `#[tokio::main]`. Therefore, it is no longer necessary to spawn a thread when you need to run something alongside Actix Web that uses Tokio's multi-threaded mode; you can simply await the server within this context or, if preferred, use `tokio::spawn` just like any other async task.
For now, `actix` actor support (and therefore WebSocket support via `actix-web-actors`) still requires `#[actix_web::main]` so that a `System` context is created. Designs are being created for an alternative WebSocket interface that does not require actors that should land sometime in the v4.x cycle.
## `web::block`
The `web::block` helper has changed return type from roughly `async fn(fn() -> Result<T, E>) Result<T, BlockingError<E>>` to `async fn(fn() -> T) Result<T, BlockingError>`. That's to say that the blocking function can now return things that are not `Result`s and it does not wrap error types anymore. If you still need to return `Result`s then you'll likely want to use double `?` after the `.await`.
```diff
- let n: u32 = web::block(|| Ok(123)).await?;
+ let n: u32 = web::block(|| 123).await?;
- let n: u32 = web::block(|| Ok(123)).await?;
+ let n: u32 = web::block(|| Ok(123)).await??;
```

106
actix-web/README.md Normal file
View File

@ -0,0 +1,106 @@
<div align="center">
<h1>Actix Web</h1>
<p>
<strong>Actix Web is a powerful, pragmatic, and extremely fast web framework for Rust</strong>
</p>
<p>
[![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.0.0)](https://docs.rs/actix-web/4.0.0)
![MSRV](https://img.shields.io/badge/rustc-1.54+-ab6000.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.0.0/status.svg)](https://deps.rs/crate/actix-web/4.0.0)
<br />
[![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)
![downloads](https://img.shields.io/crates/d/actix-web.svg)
[![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x)
</p>
</div>
## Features
- Supports _HTTP/1.x_ and _HTTP/2_
- Streaming and pipelining
- Powerful [request routing](https://actix.rs/docs/url-dispatch/) with optional macros
- Full [Tokio](https://tokio.rs) compatibility
- Keep-alive and slow requests handling
- Client/server [WebSockets](https://actix.rs/docs/websockets/) support
- Transparent content compression/decompression (br, gzip, deflate, zstd)
- Multipart streams
- Static assets
- SSL support using OpenSSL or Rustls
- Middlewares ([Logger, Session, CORS, etc](https://actix.rs/docs/middleware/))
- Integrates with the [`awc` HTTP client](https://docs.rs/awc/)
- Runs on stable Rust 1.54+
## Documentation
- [Website & User Guide](https://actix.rs)
- [Examples Repository](https://github.com/actix/examples)
- [API Documentation](https://docs.rs/actix-web)
- [API Documentation (master branch)](https://actix.rs/actix-web/actix_web)
## Example
Dependencies:
```toml
[dependencies]
actix-web = "4.0.0"
```
Code:
```rust
use actix_web::{get, web, App, HttpServer, Responder};
#[get("/{id}/{name}/index.html")]
async fn index(params: web::Path<(u32, String)>) -> impl Responder {
let (id, name) = params.into_inner();
format!("Hello {}! id:{}", name, id)
}
#[actix_web::main] // or #[tokio::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| App::new().service(index))
.bind(("127.0.0.1", 8080))?
.run()
.await
}
```
### More Examples
- [Hello World](https://github.com/actix/examples/tree/master/basics/hello-world)
- [Basic Setup](https://github.com/actix/examples/tree/master/basics/basics)
- [Application State](https://github.com/actix/examples/tree/master/basics/state)
- [JSON Handling](https://github.com/actix/examples/tree/master/json/json)
- [Multipart Streams](https://github.com/actix/examples/tree/master/forms/multipart)
- [Diesel Integration](https://github.com/actix/examples/tree/master/databases/diesel)
- [SQLite Integration](https://github.com/actix/examples/tree/master/databases/sqlite)
- [Postgres Integration](https://github.com/actix/examples/tree/master/databases/postgres)
- [Tera Templates](https://github.com/actix/examples/tree/master/templating/tera)
- [Askama Templates](https://github.com/actix/examples/tree/master/templating/askama)
- [HTTPS using Rustls](https://github.com/actix/examples/tree/master/https-tls/rustls)
- [HTTPS using OpenSSL](https://github.com/actix/examples/tree/master/https-tls/openssl)
- [Simple WebSocket](https://github.com/actix/examples/tree/master/websockets)
- [WebSocket Chat](https://github.com/actix/examples/tree/master/websockets/chat)
You may consider checking out [this directory](https://github.com/actix/examples/tree/master) for more examples.
## Benchmarks
One of the fastest web frameworks available according to the [TechEmpower Framework Benchmark](https://www.techempower.com/benchmarks/#section=data-r20&test=composite).
## License
This project is licensed under either of the following licenses, at your option:
- Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or [http://www.apache.org/licenses/LICENSE-2.0])
- MIT license ([LICENSE-MIT](LICENSE-MIT) or [http://opensource.org/licenses/MIT])
## 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

@ -0,0 +1,21 @@
use actix_web::{middleware, rt, web, App, HttpRequest, HttpServer};
async fn index(req: HttpRequest) -> &'static str {
println!("REQ: {:?}", req);
"Hello world!\r\n"
}
fn main() -> std::io::Result<()> {
env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));
rt::System::new().block_on(
HttpServer::new(|| {
App::new()
.wrap(middleware::Logger::default())
.service(web::resource("/").route(web::get().to(index)))
})
.bind(("127.0.0.1", 8080))?
.workers(1)
.run(),
)
}

View File

@ -2,7 +2,7 @@
//! properties and pass them to a handler through request-local data.
//!
//! For an example of extracting a client TLS certificate, see:
//! <https://github.com/actix/examples/tree/HEAD/security/rustls-client-cert>
//! <https://github.com/actix/examples/tree/master/https-tls/rustls-client-cert>
use std::{any::Any, io, net::SocketAddr};

View File

@ -21,8 +21,7 @@ use crate::{
},
};
/// Application builder - structure that follows the builder pattern
/// for building application instances.
/// The top-level builder for an Actix Web application.
pub struct App<T> {
endpoint: T,
services: Vec<Box<dyn AppServiceFactory>>,
@ -291,12 +290,10 @@ where
/// Ok(HttpResponse::Ok().into())
/// }
///
/// fn main() {
/// let app = App::new()
/// .service(web::resource("/index.html").route(
/// web::get().to(index)))
/// .external_resource("youtube", "https://youtube.com/watch/{video_id}");
/// }
/// let app = App::new()
/// .service(web::resource("/index.html").route(
/// web::get().to(index)))
/// .external_resource("youtube", "https://youtube.com/watch/{video_id}");
/// ```
pub fn external_resource<N, U>(mut self, name: N, url: U) -> Self
where

View File

@ -2,6 +2,10 @@
//!
//! Most users will not have to interact with the types in this module, but it is useful for those
//! writing extractors, middleware, libraries, or interacting with the service API directly.
//!
//! # Request Extractors
//! - [`ConnectionInfo`]: Connection information
//! - [`PeerAddr`]: Connection information
pub use actix_http::{Extensions, Payload, RequestHead, Response, ResponseHead};
pub use actix_router::{Path, ResourceDef, ResourcePath, Url};

View File

@ -47,7 +47,6 @@ impl fmt::Debug for Error {
impl StdError for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
// TODO: populate if replacement for Box<dyn Error> is found
None
}
}

View File

@ -6,7 +6,7 @@
//
// See <https://github.com/rust-lang/rust/issues/83375>
pub use actix_http::error::{
BlockingError, ContentTypeError, DispatchError, HttpError, ParseError, PayloadError,
ContentTypeError, DispatchError, HttpError, ParseError, PayloadError,
};
use derive_more::{Display, Error, From};
@ -33,6 +33,14 @@ pub(crate) use macros::{downcast_dyn, downcast_get_type_id};
/// This type alias is generally used to avoid writing out `actix_http::Error` directly.
pub type Result<T, E = Error> = std::result::Result<T, E>;
/// An error representing a problem running a blocking task on a thread pool.
#[derive(Debug, Display, Error)]
#[display(fmt = "Blocking thread pool is shut down unexpectedly")]
#[non_exhaustive]
pub struct BlockingError;
impl ResponseError for crate::error::BlockingError {}
/// Errors which can occur when attempting to generate resource uri.
#[derive(Debug, PartialEq, Display, Error, From)]
#[non_exhaustive]

View File

@ -6,20 +6,22 @@ use std::{
io::{self, Write as _},
};
use actix_http::{
body::BoxBody,
header::{self, TryIntoHeaderValue},
Response, StatusCode,
};
use actix_http::Response;
use bytes::BytesMut;
use crate::{
body::BoxBody,
error::{downcast_dyn, downcast_get_type_id},
helpers, HttpResponse,
helpers,
http::{
header::{self, TryIntoHeaderValue},
StatusCode,
},
HttpResponse,
};
/// Errors that can generate responses.
// TODO: add std::error::Error bound when replacement for Box<dyn Error> is found
// TODO: flesh out documentation
pub trait ResponseError: fmt::Debug + fmt::Display {
/// Returns appropriate status code for error.
///
@ -73,7 +75,6 @@ impl ResponseError for std::str::Utf8Error {
impl ResponseError for std::io::Error {
fn status_code(&self) -> StatusCode {
// TODO: decide if these errors should consider not found or permission errors
match self.kind() {
io::ErrorKind::NotFound => StatusCode::NOT_FOUND,
io::ErrorKind::PermissionDenied => StatusCode::FORBIDDEN,
@ -86,7 +87,6 @@ impl ResponseError for actix_http::error::HttpError {}
impl ResponseError for actix_http::Error {
fn status_code(&self) -> StatusCode {
// TODO: map error kinds to status code better
StatusCode::INTERNAL_SERVER_ERROR
}
@ -107,8 +107,6 @@ impl ResponseError for actix_http::error::ParseError {
}
}
impl ResponseError for actix_http::error::BlockingError {}
impl ResponseError for actix_http::error::PayloadError {
fn status_code(&self) -> StatusCode {
match *self {

View File

@ -118,12 +118,10 @@ pub trait FromRequest: Sized {
/// }
/// }
///
/// fn main() {
/// let app = App::new().service(
/// web::resource("/users/:first").route(
/// web::post().to(index))
/// );
/// }
/// let app = App::new().service(
/// web::resource("/users/:first").route(
/// web::post().to(index))
/// );
/// ```
impl<T> FromRequest for Option<T>
where
@ -205,11 +203,9 @@ where
/// }
/// }
///
/// fn main() {
/// let app = App::new().service(
/// web::resource("/users/:first").route(web::post().to(index))
/// );
/// }
/// let app = App::new().service(
/// web::resource("/users/:first").route(web::post().to(index))
/// );
/// ```
impl<T, E> FromRequest for Result<T, E>
where

View File

@ -10,12 +10,16 @@ use crate::{
/// The interface for request handlers.
///
/// # What Is A Request Handler
/// A request handler has three requirements:
/// In short, a handler is just an async function that receives request-based arguments, in any
/// order, and returns something that can be converted to a response.
///
/// In particular, a request handler has three requirements:
/// 1. It is an async function (or a function/closure that returns an appropriate future);
/// 1. The function parameters (up to 12) implement [`FromRequest`];
/// 1. The async function (or future) resolves to a type that can be converted into an
/// [`HttpResponse`] (i.e., it implements the [`Responder`] trait).
///
///
/// # Compiler Errors
/// If you get the error `the trait Handler<_> is not implemented`, then your handler does not
/// fulfill the _first_ of the above requirements. Missing other requirements manifest as errors on

Some files were not shown because too many files have changed in this diff Show More