mirror of
https://github.com/fafhrd91/actix-web
synced 2025-07-06 19:00:18 +02:00
Compare commits
21 Commits
http-v2.0.
...
http-v2.0.
Author | SHA1 | Date | |
---|---|---|---|
aa2bd6fbfb | |||
5aad8e24c7 | |||
6e97bc09f8 | |||
160995b8d4 | |||
187646b2f9 | |||
46627be36f | |||
a78380739e | |||
cf1c8abe62 | |||
92b5bcd13f | |||
701bdacfa2 | |||
6dc47c4093 | |||
0ec335a39c | |||
f8d5ad6b53 | |||
43c362779d | |||
971ba3eee1 | |||
2fd96c03e5 | |||
ad7c6d2633 | |||
3362a3d61b | |||
769ea6bd5b | |||
1382094c15 | |||
78594a72bd |
7
.github/PULL_REQUEST_TEMPLATE.md
vendored
7
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -1,6 +1,8 @@
|
|||||||
## PR Type
|
<!-- Thanks for considering contributing actix! -->
|
||||||
What kind of change does this PR make?
|
<!-- Please fill out the following to make our reviews easy. -->
|
||||||
|
|
||||||
|
## PR Type
|
||||||
|
<!-- What kind of change does this PR make? -->
|
||||||
<!-- Bug Fix / Feature / Refactor / Code Style / Other -->
|
<!-- Bug Fix / Feature / Refactor / Code Style / Other -->
|
||||||
INSERT_PR_TYPE
|
INSERT_PR_TYPE
|
||||||
|
|
||||||
@ -13,6 +15,7 @@ Check your PR fulfills the following:
|
|||||||
- [ ] Tests for the changes have been added / updated.
|
- [ ] Tests for the changes have been added / updated.
|
||||||
- [ ] Documentation comments have been added / updated.
|
- [ ] Documentation comments have been added / updated.
|
||||||
- [ ] A changelog entry has been made for the appropriate packages.
|
- [ ] A changelog entry has been made for the appropriate packages.
|
||||||
|
- [ ] Format code with the latest stable rustfmt
|
||||||
|
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
9
.github/workflows/bench.yml
vendored
9
.github/workflows/bench.yml
vendored
@ -1,13 +1,18 @@
|
|||||||
name: Benchmark (Linux)
|
name: Benchmark (Linux)
|
||||||
|
|
||||||
on: [push, pull_request]
|
on:
|
||||||
|
pull_request:
|
||||||
|
types: [opened, synchronize, reopened]
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
check_benchmark:
|
check_benchmark:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@master
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: actions-rs/toolchain@v1
|
uses: actions-rs/toolchain@v1
|
||||||
|
32
.github/workflows/clippy-fmt.yml
vendored
Normal file
32
.github/workflows/clippy-fmt.yml
vendored
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types: [opened, synchronize, reopened]
|
||||||
|
|
||||||
|
name: Clippy and rustfmt Check
|
||||||
|
jobs:
|
||||||
|
clippy_check:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: stable
|
||||||
|
components: rustfmt
|
||||||
|
override: true
|
||||||
|
- name: Check with rustfmt
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: fmt
|
||||||
|
args: --all -- --check
|
||||||
|
|
||||||
|
- uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: nightly
|
||||||
|
components: clippy
|
||||||
|
override: true
|
||||||
|
- name: Check with Clippy
|
||||||
|
uses: actions-rs/clippy-check@v1
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
args: --all-features --all --tests
|
11
.github/workflows/linux.yml
vendored
11
.github/workflows/linux.yml
vendored
@ -1,6 +1,11 @@
|
|||||||
name: CI (Linux)
|
name: CI (Linux)
|
||||||
|
|
||||||
on: [push, pull_request]
|
on:
|
||||||
|
pull_request:
|
||||||
|
types: [opened, synchronize, reopened]
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build_and_test:
|
build_and_test:
|
||||||
@ -8,7 +13,7 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
version:
|
version:
|
||||||
- 1.41.1 # MSRV
|
- 1.42.0 # MSRV
|
||||||
- stable
|
- stable
|
||||||
- nightly
|
- nightly
|
||||||
|
|
||||||
@ -16,7 +21,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@master
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Install ${{ matrix.version }}
|
- name: Install ${{ matrix.version }}
|
||||||
uses: actions-rs/toolchain@v1
|
uses: actions-rs/toolchain@v1
|
||||||
|
9
.github/workflows/macos.yml
vendored
9
.github/workflows/macos.yml
vendored
@ -1,6 +1,11 @@
|
|||||||
name: CI (macOS)
|
name: CI (macOS)
|
||||||
|
|
||||||
on: [push, pull_request]
|
on:
|
||||||
|
pull_request:
|
||||||
|
types: [opened, synchronize, reopened]
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build_and_test:
|
build_and_test:
|
||||||
@ -15,7 +20,7 @@ jobs:
|
|||||||
runs-on: macOS-latest
|
runs-on: macOS-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@master
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Install ${{ matrix.version }}
|
- name: Install ${{ matrix.version }}
|
||||||
uses: actions-rs/toolchain@v1
|
uses: actions-rs/toolchain@v1
|
||||||
|
12
.github/workflows/upload-doc.yml
vendored
12
.github/workflows/upload-doc.yml
vendored
@ -11,7 +11,7 @@ jobs:
|
|||||||
if: github.repository == 'actix/actix-web'
|
if: github.repository == 'actix/actix-web'
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@master
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: actions-rs/toolchain@v1
|
uses: actions-rs/toolchain@v1
|
||||||
@ -29,7 +29,9 @@ jobs:
|
|||||||
- name: Tweak HTML
|
- name: Tweak HTML
|
||||||
run: echo "<meta http-equiv=refresh content=0;url=os_balloon/index.html>" > target/doc/index.html
|
run: echo "<meta http-equiv=refresh content=0;url=os_balloon/index.html>" > target/doc/index.html
|
||||||
|
|
||||||
- name: Upload documentation
|
- name: Deploy to GitHub Pages
|
||||||
run: |
|
uses: JamesIves/github-pages-deploy-action@3.5.8
|
||||||
git clone https://github.com/davisp/ghp-import.git
|
with:
|
||||||
./ghp-import/ghp_import.py -n -p -f -m "Documentation upload" -r https://${{ secrets.GITHUB_TOKEN }}@github.com/"${{ github.repository }}.git" target/doc
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
BRANCH: gh-pages
|
||||||
|
FOLDER: target/doc
|
||||||
|
9
.github/workflows/windows.yml
vendored
9
.github/workflows/windows.yml
vendored
@ -1,6 +1,11 @@
|
|||||||
name: CI (Windows)
|
name: CI (Windows)
|
||||||
|
|
||||||
on: [push, pull_request]
|
on:
|
||||||
|
pull_request:
|
||||||
|
types: [opened, synchronize, reopened]
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
env:
|
env:
|
||||||
VCPKGRS_DYNAMIC: 1
|
VCPKGRS_DYNAMIC: 1
|
||||||
@ -18,7 +23,7 @@ jobs:
|
|||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@master
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Install ${{ matrix.version }}
|
- name: Install ${{ matrix.version }}
|
||||||
uses: actions-rs/toolchain@v1
|
uses: actions-rs/toolchain@v1
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -9,6 +9,7 @@ guide/build/
|
|||||||
*.pid
|
*.pid
|
||||||
*.sock
|
*.sock
|
||||||
*~
|
*~
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
# These are backup files generated by rustfmt
|
# These are backup files generated by rustfmt
|
||||||
**/*.rs.bk
|
**/*.rs.bk
|
||||||
|
32
CHANGES.md
32
CHANGES.md
@ -1,38 +1,50 @@
|
|||||||
# Changes
|
# Changes
|
||||||
|
|
||||||
## [Unreleased]
|
## Unreleased - 2020-xx-xx
|
||||||
|
### Changed
|
||||||
|
* `PayloadConfig` is now also considered in `Bytes` and `String` extractors when set
|
||||||
|
using `App::data`. [#1610]
|
||||||
|
* `web::Path` now has a public representation: `web::Path(pub T)` that enables
|
||||||
|
destructuring. [#1594]
|
||||||
|
* `ServiceRequest::app_data` allows retrieval of non-Data data without splitting into parts to
|
||||||
|
access `HttpRequest` which already allows this. [#1618]
|
||||||
|
* Re-export all error types from `awc`. [#1621]
|
||||||
|
* MSRV is now 1.42.0.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* Memory leak of app data in pooled requests. [#1609]
|
||||||
|
|
||||||
|
[#1594]: https://github.com/actix/actix-web/pull/1594
|
||||||
|
[#1609]: https://github.com/actix/actix-web/pull/1609
|
||||||
|
[#1610]: https://github.com/actix/actix-web/pull/1610
|
||||||
|
[#1618]: https://github.com/actix/actix-web/pull/1618
|
||||||
|
[#1621]: https://github.com/actix/actix-web/pull/1621
|
||||||
|
|
||||||
|
|
||||||
|
## 3.0.0-beta.1 - 2020-07-13
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
* Re-export `actix_rt::main` as `actix_web::main`.
|
* Re-export `actix_rt::main` as `actix_web::main`.
|
||||||
* `HttpRequest::match_pattern` and `ServiceRequest::match_pattern` for extracting the matched
|
* `HttpRequest::match_pattern` and `ServiceRequest::match_pattern` for extracting the matched
|
||||||
resource pattern.
|
resource pattern.
|
||||||
* `HttpRequest::match_name` and `ServiceRequest::match_name` for extracting matched resource name.
|
* `HttpRequest::match_name` and `ServiceRequest::match_name` for extracting matched resource name.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
* Fix actix_http::h1::dispatcher so it returns when HW_BUFFER_SIZE is reached. Should reduce peak memory consumption during large uploads. [#1550]
|
* Fix actix_http::h1::dispatcher so it returns when HW_BUFFER_SIZE is reached. Should reduce peak memory consumption during large uploads. [#1550]
|
||||||
* Migrate cookie handling to `cookie` crate. Actix-web no longer requires `ring` dependency.
|
* Migrate cookie handling to `cookie` crate. Actix-web no longer requires `ring` dependency.
|
||||||
* MSRV is now 1.41.1
|
* MSRV is now 1.41.1
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
* `NormalizePath` improved consistency when path needs slashes added _and_ removed.
|
* `NormalizePath` improved consistency when path needs slashes added _and_ removed.
|
||||||
|
|
||||||
## [3.0.0-alpha.3] - 2020-05-21
|
|
||||||
|
|
||||||
|
## 3.0.0-alpha.3 - 2020-05-21
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
* Add option to create `Data<T>` from `Arc<T>` [#1509]
|
* Add option to create `Data<T>` from `Arc<T>` [#1509]
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
* Resources and Scopes can now access non-overridden data types set on App (or containing scopes) when setting their own data. [#1486]
|
* Resources and Scopes can now access non-overridden data types set on App (or containing scopes) when setting their own data. [#1486]
|
||||||
|
|
||||||
* Fix audit issue logging by default peer address [#1485]
|
* Fix audit issue logging by default peer address [#1485]
|
||||||
|
|
||||||
* Bump minimum supported Rust version to 1.40
|
* Bump minimum supported Rust version to 1.40
|
||||||
|
|
||||||
* Replace deprecated `net2` crate with `socket2`
|
* Replace deprecated `net2` crate with `socket2`
|
||||||
|
|
||||||
[#1485]: https://github.com/actix/actix-web/pull/1485
|
[#1485]: https://github.com/actix/actix-web/pull/1485
|
||||||
|
14
Cargo.toml
14
Cargo.toml
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "actix-web"
|
name = "actix-web"
|
||||||
version = "3.0.0-alpha.3"
|
version = "3.0.0-beta.1"
|
||||||
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
||||||
description = "Actix web is a simple, pragmatic and extremely fast web framework for Rust."
|
description = "Actix web is a simple, pragmatic and extremely fast web framework for Rust."
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
@ -11,7 +11,7 @@ documentation = "https://docs.rs/actix-web/"
|
|||||||
categories = ["network-programming", "asynchronous",
|
categories = ["network-programming", "asynchronous",
|
||||||
"web-programming::http-server",
|
"web-programming::http-server",
|
||||||
"web-programming::websocket"]
|
"web-programming::websocket"]
|
||||||
license = "MIT/Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
@ -76,9 +76,9 @@ actix-macros = "0.1.0"
|
|||||||
actix-threadpool = "0.3.1"
|
actix-threadpool = "0.3.1"
|
||||||
actix-tls = "2.0.0-alpha.1"
|
actix-tls = "2.0.0-alpha.1"
|
||||||
|
|
||||||
actix-web-codegen = "0.2.2"
|
actix-web-codegen = "0.3.0-beta.1"
|
||||||
actix-http = "2.0.0-alpha.4"
|
actix-http = "2.0.0-beta.3"
|
||||||
awc = { version = "2.0.0-alpha.2", default-features = false }
|
awc = { version = "2.0.0-beta.1", default-features = false }
|
||||||
|
|
||||||
bytes = "0.5.3"
|
bytes = "0.5.3"
|
||||||
derive_more = "0.99.2"
|
derive_more = "0.99.2"
|
||||||
@ -124,6 +124,10 @@ actix-files = { path = "actix-files" }
|
|||||||
actix-multipart = { path = "actix-multipart" }
|
actix-multipart = { path = "actix-multipart" }
|
||||||
awc = { path = "awc" }
|
awc = { path = "awc" }
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "client"
|
||||||
|
required-features = ["rustls"]
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "server"
|
name = "server"
|
||||||
harness = false
|
harness = false
|
||||||
|
20
MIGRATION.md
20
MIGRATION.md
@ -12,6 +12,26 @@
|
|||||||
* `BodySize::Sized64` variant has been removed. `BodySize::Sized` now receives a
|
* `BodySize::Sized64` variant has been removed. `BodySize::Sized` now receives a
|
||||||
`u64` instead of a `usize`.
|
`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)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## 2.0.0
|
## 2.0.0
|
||||||
|
|
||||||
* `HttpServer::start()` renamed to `HttpServer::run()`. It also possible to
|
* `HttpServer::start()` renamed to `HttpServer::run()`. It also possible to
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
[](https://crates.io/crates/actix-web)
|
[](https://crates.io/crates/actix-web)
|
||||||
[](https://docs.rs/actix-web)
|
[](https://docs.rs/actix-web)
|
||||||
[](https://blog.rust-lang.org/2020/02/27/Rust-1.41.1.html)
|
[](https://blog.rust-lang.org/2020/03/12/Rust-1.42.html)
|
||||||

|

|
||||||
<br />
|
<br />
|
||||||
[](https://travis-ci.org/actix/actix-web)
|
[](https://travis-ci.org/actix/actix-web)
|
||||||
@ -32,7 +32,7 @@
|
|||||||
* Middlewares ([Logger, Session, CORS, etc](https://actix.rs/docs/middleware/))
|
* Middlewares ([Logger, Session, CORS, etc](https://actix.rs/docs/middleware/))
|
||||||
* Includes an async [HTTP client](https://actix.rs/actix-web/actix_web/client/index.html)
|
* Includes an async [HTTP client](https://actix.rs/actix-web/actix_web/client/index.html)
|
||||||
* Supports [Actix actor framework](https://github.com/actix/actix)
|
* Supports [Actix actor framework](https://github.com/actix/actix)
|
||||||
* Runs on stable Rust 1.41+
|
* Runs on stable Rust 1.42+
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
@ -61,8 +61,8 @@ Code:
|
|||||||
use actix_web::{get, web, App, HttpServer, Responder};
|
use actix_web::{get, web, App, HttpServer, Responder};
|
||||||
|
|
||||||
#[get("/{id}/{name}/index.html")]
|
#[get("/{id}/{name}/index.html")]
|
||||||
async fn index(info: web::Path<(u32, String)>) -> impl Responder {
|
async fn index(web::Path((id, name)): web::Path<(u32, String)>) -> impl Responder {
|
||||||
format!("Hello {}! id:{}", info.1, info.0)
|
format!("Hello {}! id:{}", name, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[actix_web::main]
|
#[actix_web::main]
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
# Changes
|
# Changes
|
||||||
|
|
||||||
## [unreleased] - xxx
|
## [Unreleased] - 2020-xx-xx
|
||||||
|
|
||||||
|
## [0.3.0-beta.1] - 2020-07-15
|
||||||
* Update `v_htmlescape` to 0.10
|
* Update `v_htmlescape` to 0.10
|
||||||
|
* Update `actix-web` and `actix-http` dependencies to beta.1
|
||||||
|
|
||||||
## [0.3.0-alpha.1] - 2020-05-23
|
## [0.3.0-alpha.1] - 2020-05-23
|
||||||
|
|
||||||
* Update `actix-web` and `actix-http` dependencies to alpha
|
* Update `actix-web` and `actix-http` dependencies to alpha
|
||||||
* Fix some typos in the docs
|
* Fix some typos in the docs
|
||||||
* Bump minimum supported Rust version to 1.40
|
* Bump minimum supported Rust version to 1.40
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "actix-files"
|
name = "actix-files"
|
||||||
version = "0.3.0-alpha.1"
|
version = "0.3.0-beta.1"
|
||||||
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
||||||
description = "Static files support for actix web."
|
description = "Static files support for actix web."
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
@ -9,7 +9,7 @@ homepage = "https://actix.rs"
|
|||||||
repository = "https://github.com/actix/actix-web.git"
|
repository = "https://github.com/actix/actix-web.git"
|
||||||
documentation = "https://docs.rs/actix-files/"
|
documentation = "https://docs.rs/actix-files/"
|
||||||
categories = ["asynchronous", "web-programming::http-server"]
|
categories = ["asynchronous", "web-programming::http-server"]
|
||||||
license = "MIT/Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
@ -17,8 +17,8 @@ name = "actix_files"
|
|||||||
path = "src/lib.rs"
|
path = "src/lib.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix-web = { version = "3.0.0-alpha.3", default-features = false }
|
actix-web = { version = "3.0.0-beta.1", default-features = false }
|
||||||
actix-http = "2.0.0-alpha.4"
|
actix-http = "2.0.0-beta.3"
|
||||||
actix-service = "1.0.1"
|
actix-service = "1.0.1"
|
||||||
bitflags = "1"
|
bitflags = "1"
|
||||||
bytes = "0.5.3"
|
bytes = "0.5.3"
|
||||||
@ -33,4 +33,4 @@ v_htmlescape = "0.10"
|
|||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
actix-rt = "1.0.0"
|
actix-rt = "1.0.0"
|
||||||
actix-web = { version = "3.0.0-alpha.3", features = ["openssl"] }
|
actix-web = { version = "3.0.0-beta.1", features = ["openssl"] }
|
||||||
|
@ -26,7 +26,6 @@ use actix_web::{web, FromRequest, HttpRequest, HttpResponse};
|
|||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use futures_core::Stream;
|
use futures_core::Stream;
|
||||||
use futures_util::future::{ok, ready, Either, FutureExt, LocalBoxFuture, Ready};
|
use futures_util::future::{ok, ready, Either, FutureExt, LocalBoxFuture, Ready};
|
||||||
use mime;
|
|
||||||
use mime_guess::from_ext;
|
use mime_guess::from_ext;
|
||||||
use percent_encoding::{utf8_percent_encode, CONTROLS};
|
use percent_encoding::{utf8_percent_encode, CONTROLS};
|
||||||
use v_htmlescape::escape as escape_html_entity;
|
use v_htmlescape::escape as escape_html_entity;
|
||||||
@ -250,6 +249,8 @@ pub struct Files {
|
|||||||
renderer: Rc<DirectoryRenderer>,
|
renderer: Rc<DirectoryRenderer>,
|
||||||
mime_override: Option<Rc<MimeOverride>>,
|
mime_override: Option<Rc<MimeOverride>>,
|
||||||
file_flags: named::Flags,
|
file_flags: named::Flags,
|
||||||
|
// FIXME: Should re-visit later.
|
||||||
|
#[allow(clippy::redundant_allocation)]
|
||||||
guards: Option<Rc<Box<dyn Guard>>>,
|
guards: Option<Rc<Box<dyn Guard>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -462,6 +463,8 @@ pub struct FilesService {
|
|||||||
renderer: Rc<DirectoryRenderer>,
|
renderer: Rc<DirectoryRenderer>,
|
||||||
mime_override: Option<Rc<MimeOverride>>,
|
mime_override: Option<Rc<MimeOverride>>,
|
||||||
file_flags: named::Flags,
|
file_flags: named::Flags,
|
||||||
|
// FIXME: Should re-visit later.
|
||||||
|
#[allow(clippy::redundant_allocation)]
|
||||||
guards: Option<Rc<Box<dyn Guard>>>,
|
guards: Option<Rc<Box<dyn Guard>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -501,11 +504,8 @@ impl Service for FilesService {
|
|||||||
// execute user defined guards
|
// execute user defined guards
|
||||||
(**guard).check(req.head())
|
(**guard).check(req.head())
|
||||||
} else {
|
} else {
|
||||||
// default behaviour
|
// default behavior
|
||||||
match *req.method() {
|
matches!(*req.method(), Method::HEAD | Method::GET)
|
||||||
Method::HEAD | Method::GET => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if !is_method_valid {
|
if !is_method_valid {
|
||||||
@ -952,9 +952,7 @@ mod tests {
|
|||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_named_file_content_range_headers() {
|
async fn test_named_file_content_range_headers() {
|
||||||
let srv = test::start(|| {
|
let srv = test::start(|| App::new().service(Files::new("/", ".")));
|
||||||
App::new().service(Files::new("/", "."))
|
|
||||||
});
|
|
||||||
|
|
||||||
// Valid range header
|
// Valid range header
|
||||||
let response = srv
|
let response = srv
|
||||||
@ -979,9 +977,7 @@ mod tests {
|
|||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_named_file_content_length_headers() {
|
async fn test_named_file_content_length_headers() {
|
||||||
let srv = test::start(|| {
|
let srv = test::start(|| App::new().service(Files::new("/", ".")));
|
||||||
App::new().service(Files::new("/", "."))
|
|
||||||
});
|
|
||||||
|
|
||||||
// Valid range header
|
// Valid range header
|
||||||
let response = srv
|
let response = srv
|
||||||
@ -1020,15 +1016,9 @@ mod tests {
|
|||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_head_content_length_headers() {
|
async fn test_head_content_length_headers() {
|
||||||
let srv = test::start(|| {
|
let srv = test::start(|| App::new().service(Files::new("/", ".")));
|
||||||
App::new().service(Files::new("/", "."))
|
|
||||||
});
|
|
||||||
|
|
||||||
let response = srv
|
let response = srv.head("/tests/test.binary").send().await.unwrap();
|
||||||
.head("/tests/test.binary")
|
|
||||||
.send()
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let content_length = response
|
let content_length = response
|
||||||
.headers()
|
.headers()
|
||||||
@ -1097,12 +1087,10 @@ mod tests {
|
|||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_named_file_content_encoding() {
|
async fn test_named_file_content_encoding() {
|
||||||
let mut srv = test::init_service(App::new().wrap(Compress::default()).service(
|
let mut srv = test::init_service(App::new().wrap(Compress::default()).service(
|
||||||
web::resource("/").to(|| {
|
web::resource("/").to(|| async {
|
||||||
async {
|
|
||||||
NamedFile::open("Cargo.toml")
|
NamedFile::open("Cargo.toml")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.set_content_encoding(header::ContentEncoding::Identity)
|
.set_content_encoding(header::ContentEncoding::Identity)
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
))
|
))
|
||||||
.await;
|
.await;
|
||||||
@ -1119,12 +1107,10 @@ mod tests {
|
|||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_named_file_content_encoding_gzip() {
|
async fn test_named_file_content_encoding_gzip() {
|
||||||
let mut srv = test::init_service(App::new().wrap(Compress::default()).service(
|
let mut srv = test::init_service(App::new().wrap(Compress::default()).service(
|
||||||
web::resource("/").to(|| {
|
web::resource("/").to(|| async {
|
||||||
async {
|
|
||||||
NamedFile::open("Cargo.toml")
|
NamedFile::open("Cargo.toml")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.set_content_encoding(header::ContentEncoding::Gzip)
|
.set_content_encoding(header::ContentEncoding::Gzip)
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
))
|
))
|
||||||
.await;
|
.await;
|
||||||
|
@ -8,7 +8,6 @@ use std::time::{SystemTime, UNIX_EPOCH};
|
|||||||
use std::os::unix::fs::MetadataExt;
|
use std::os::unix::fs::MetadataExt;
|
||||||
|
|
||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
use mime;
|
|
||||||
use mime_guess::from_path;
|
use mime_guess::from_path;
|
||||||
|
|
||||||
use actix_http::body::SizedStream;
|
use actix_http::body::SizedStream;
|
||||||
@ -90,7 +89,7 @@ impl NamedFile {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let ct = from_path(&path).first_or_octet_stream();
|
let ct = from_path(&path).first_or_octet_stream();
|
||||||
let disposition_type = match ct.type_() {
|
let disposition = match ct.type_() {
|
||||||
mime::IMAGE | mime::TEXT | mime::VIDEO => DispositionType::Inline,
|
mime::IMAGE | mime::TEXT | mime::VIDEO => DispositionType::Inline,
|
||||||
_ => DispositionType::Attachment,
|
_ => DispositionType::Attachment,
|
||||||
};
|
};
|
||||||
@ -104,8 +103,8 @@ impl NamedFile {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
let cd = ContentDisposition {
|
let cd = ContentDisposition {
|
||||||
disposition: disposition_type,
|
disposition,
|
||||||
parameters: parameters,
|
parameters,
|
||||||
};
|
};
|
||||||
(ct, cd)
|
(ct, cd)
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,23 @@
|
|||||||
# Changes
|
# Changes
|
||||||
|
|
||||||
## [Unreleased] - xxx
|
## [2.0.0-beta.3] - 2020-08-14
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* Memory leak of `client::pool::ConnectorPoolSupport`. [#1626]
|
||||||
|
|
||||||
|
[#1626]: https://github.com/actix/actix-web/pull/1626
|
||||||
|
|
||||||
|
|
||||||
|
## [2.0.0-beta.2] - 2020-07-21
|
||||||
|
### Fixed
|
||||||
|
* Potential UB in h1 decoder using uninitialized memory. [#1614]
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
* Fix illegal chunked encoding. [#1615]
|
||||||
|
|
||||||
|
[#1614]: https://github.com/actix/actix-web/pull/1614
|
||||||
|
[#1615]: https://github.com/actix/actix-web/pull/1615
|
||||||
|
|
||||||
|
|
||||||
## [2.0.0-beta.1] - 2020-07-11
|
## [2.0.0-beta.1] - 2020-07-11
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "actix-http"
|
name = "actix-http"
|
||||||
version = "2.0.0-beta.1"
|
version = "2.0.0-beta.3"
|
||||||
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
||||||
description = "Actix HTTP primitives"
|
description = "Actix HTTP primitives"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
@ -11,7 +11,7 @@ documentation = "https://docs.rs/actix-http/"
|
|||||||
categories = ["network-programming", "asynchronous",
|
categories = ["network-programming", "asynchronous",
|
||||||
"web-programming::http-server",
|
"web-programming::http-server",
|
||||||
"web-programming::websocket"]
|
"web-programming::websocket"]
|
||||||
license = "MIT/Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
@ -103,3 +103,7 @@ harness = false
|
|||||||
[[bench]]
|
[[bench]]
|
||||||
name = "status-line"
|
name = "status-line"
|
||||||
harness = false
|
harness = false
|
||||||
|
|
||||||
|
[[bench]]
|
||||||
|
name = "uninit-headers"
|
||||||
|
harness = false
|
||||||
|
137
actix-http/benches/uninit-headers.rs
Normal file
137
actix-http/benches/uninit-headers.rs
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
use criterion::{criterion_group, criterion_main, Criterion};
|
||||||
|
|
||||||
|
use bytes::BytesMut;
|
||||||
|
|
||||||
|
// A Miri run detects UB, seen on this playground:
|
||||||
|
// https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=f5d9aa166aa48df8dca05fce2b6c3915
|
||||||
|
|
||||||
|
fn bench_header_parsing(c: &mut Criterion) {
|
||||||
|
c.bench_function("Original (Unsound) [short]", |b| {
|
||||||
|
b.iter(|| {
|
||||||
|
let mut buf = BytesMut::from(REQ_SHORT);
|
||||||
|
_original::parse_headers(&mut buf);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
c.bench_function("New (safe) [short]", |b| {
|
||||||
|
b.iter(|| {
|
||||||
|
let mut buf = BytesMut::from(REQ_SHORT);
|
||||||
|
_new::parse_headers(&mut buf);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
c.bench_function("Original (Unsound) [realistic]", |b| {
|
||||||
|
b.iter(|| {
|
||||||
|
let mut buf = BytesMut::from(REQ);
|
||||||
|
_original::parse_headers(&mut buf);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
c.bench_function("New (safe) [realistic]", |b| {
|
||||||
|
b.iter(|| {
|
||||||
|
let mut buf = BytesMut::from(REQ);
|
||||||
|
_new::parse_headers(&mut buf);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
criterion_group!(benches, bench_header_parsing);
|
||||||
|
criterion_main!(benches);
|
||||||
|
|
||||||
|
const MAX_HEADERS: usize = 96;
|
||||||
|
|
||||||
|
const EMPTY_HEADER_ARRAY: [httparse::Header<'static>; MAX_HEADERS] =
|
||||||
|
[httparse::EMPTY_HEADER; MAX_HEADERS];
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
struct HeaderIndex {
|
||||||
|
name: (usize, usize),
|
||||||
|
value: (usize, usize),
|
||||||
|
}
|
||||||
|
|
||||||
|
const EMPTY_HEADER_INDEX: HeaderIndex = HeaderIndex {
|
||||||
|
name: (0, 0),
|
||||||
|
value: (0, 0),
|
||||||
|
};
|
||||||
|
|
||||||
|
const EMPTY_HEADER_INDEX_ARRAY: [HeaderIndex; MAX_HEADERS] =
|
||||||
|
[EMPTY_HEADER_INDEX; MAX_HEADERS];
|
||||||
|
|
||||||
|
impl HeaderIndex {
|
||||||
|
fn record(
|
||||||
|
bytes: &[u8],
|
||||||
|
headers: &[httparse::Header<'_>],
|
||||||
|
indices: &mut [HeaderIndex],
|
||||||
|
) {
|
||||||
|
let bytes_ptr = bytes.as_ptr() as usize;
|
||||||
|
for (header, indices) in headers.iter().zip(indices.iter_mut()) {
|
||||||
|
let name_start = header.name.as_ptr() as usize - bytes_ptr;
|
||||||
|
let name_end = name_start + header.name.len();
|
||||||
|
indices.name = (name_start, name_end);
|
||||||
|
let value_start = header.value.as_ptr() as usize - bytes_ptr;
|
||||||
|
let value_end = value_start + header.value.len();
|
||||||
|
indices.value = (value_start, value_end);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// test cases taken from:
|
||||||
|
// https://github.com/seanmonstar/httparse/blob/master/benches/parse.rs
|
||||||
|
|
||||||
|
const REQ_SHORT: &'static [u8] = b"\
|
||||||
|
GET / HTTP/1.0\r\n\
|
||||||
|
Host: example.com\r\n\
|
||||||
|
Cookie: session=60; user_id=1\r\n\r\n";
|
||||||
|
|
||||||
|
const REQ: &'static [u8] = b"\
|
||||||
|
GET /wp-content/uploads/2010/03/hello-kitty-darth-vader-pink.jpg HTTP/1.1\r\n\
|
||||||
|
Host: www.kittyhell.com\r\n\
|
||||||
|
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; ja-JP-mac; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3 Pathtraq/0.9\r\n\
|
||||||
|
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n\
|
||||||
|
Accept-Language: ja,en-us;q=0.7,en;q=0.3\r\n\
|
||||||
|
Accept-Encoding: gzip,deflate\r\n\
|
||||||
|
Accept-Charset: Shift_JIS,utf-8;q=0.7,*;q=0.7\r\n\
|
||||||
|
Keep-Alive: 115\r\n\
|
||||||
|
Connection: keep-alive\r\n\
|
||||||
|
Cookie: wp_ozh_wsa_visits=2; wp_ozh_wsa_visit_lasttime=xxxxxxxxxx; __utma=xxxxxxxxx.xxxxxxxxxx.xxxxxxxxxx.xxxxxxxxxx.xxxxxxxxxx.x; __utmz=xxxxxxxxx.xxxxxxxxxx.x.x.utmccn=(referral)|utmcsr=reader.livedoor.com|utmcct=/reader/|utmcmd=referral|padding=under256\r\n\r\n";
|
||||||
|
|
||||||
|
mod _new {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub fn parse_headers(src: &mut BytesMut) -> usize {
|
||||||
|
let mut headers: [HeaderIndex; MAX_HEADERS] = EMPTY_HEADER_INDEX_ARRAY;
|
||||||
|
let mut parsed: [httparse::Header<'_>; MAX_HEADERS] = EMPTY_HEADER_ARRAY;
|
||||||
|
|
||||||
|
let mut req = httparse::Request::new(&mut parsed);
|
||||||
|
match req.parse(src).unwrap() {
|
||||||
|
httparse::Status::Complete(_len) => {
|
||||||
|
HeaderIndex::record(src, req.headers, &mut headers);
|
||||||
|
req.headers.len()
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod _original {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
use std::mem::MaybeUninit;
|
||||||
|
|
||||||
|
pub fn parse_headers(src: &mut BytesMut) -> usize {
|
||||||
|
let mut headers: [HeaderIndex; MAX_HEADERS] =
|
||||||
|
unsafe { MaybeUninit::uninit().assume_init() };
|
||||||
|
|
||||||
|
let mut parsed: [httparse::Header<'_>; MAX_HEADERS] =
|
||||||
|
unsafe { MaybeUninit::uninit().assume_init() };
|
||||||
|
|
||||||
|
let mut req = httparse::Request::new(&mut parsed);
|
||||||
|
match req.parse(src).unwrap() {
|
||||||
|
httparse::Status::Complete(_len) => {
|
||||||
|
HeaderIndex::record(src, req.headers, &mut headers);
|
||||||
|
req.headers.len()
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -21,12 +21,7 @@ pub enum BodySize {
|
|||||||
|
|
||||||
impl BodySize {
|
impl BodySize {
|
||||||
pub fn is_eof(&self) -> bool {
|
pub fn is_eof(&self) -> bool {
|
||||||
match self {
|
matches!(self, BodySize::None | BodySize::Empty | BodySize::Sized(0))
|
||||||
BodySize::None
|
|
||||||
| BodySize::Empty
|
|
||||||
| BodySize::Sized(0) => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,14 +187,8 @@ impl MessageBody for Body {
|
|||||||
impl PartialEq for Body {
|
impl PartialEq for Body {
|
||||||
fn eq(&self, other: &Body) -> bool {
|
fn eq(&self, other: &Body) -> bool {
|
||||||
match *self {
|
match *self {
|
||||||
Body::None => match *other {
|
Body::None => matches!(*other, Body::None),
|
||||||
Body::None => true,
|
Body::Empty => matches!(*other, Body::Empty),
|
||||||
_ => false,
|
|
||||||
},
|
|
||||||
Body::Empty => match *other {
|
|
||||||
Body::Empty => true,
|
|
||||||
_ => false,
|
|
||||||
},
|
|
||||||
Body::Bytes(ref b) => match *other {
|
Body::Bytes(ref b) => match *other {
|
||||||
Body::Bytes(ref b2) => b == b2,
|
Body::Bytes(ref b2) => b == b2,
|
||||||
_ => false,
|
_ => false,
|
||||||
@ -476,9 +465,9 @@ where
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use futures_util::stream;
|
|
||||||
use futures_util::future::poll_fn;
|
use futures_util::future::poll_fn;
|
||||||
use futures_util::pin_mut;
|
use futures_util::pin_mut;
|
||||||
|
use futures_util::stream;
|
||||||
|
|
||||||
impl Body {
|
impl Body {
|
||||||
pub(crate) fn get_ref(&self) -> &[u8] {
|
pub(crate) fn get_ref(&self) -> &[u8] {
|
||||||
@ -612,10 +601,6 @@ mod tests {
|
|||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_body_eq() {
|
async fn test_body_eq() {
|
||||||
assert!(Body::None == Body::None);
|
|
||||||
assert!(Body::None != Body::Empty);
|
|
||||||
assert!(Body::Empty == Body::Empty);
|
|
||||||
assert!(Body::Empty != Body::None);
|
|
||||||
assert!(
|
assert!(
|
||||||
Body::Bytes(Bytes::from_static(b"1"))
|
Body::Bytes(Bytes::from_static(b"1"))
|
||||||
== Body::Bytes(Bytes::from_static(b"1"))
|
== Body::Bytes(Bytes::from_static(b"1"))
|
||||||
@ -627,7 +612,7 @@ mod tests {
|
|||||||
async fn test_body_debug() {
|
async fn test_body_debug() {
|
||||||
assert!(format!("{:?}", Body::None).contains("Body::None"));
|
assert!(format!("{:?}", Body::None).contains("Body::None"));
|
||||||
assert!(format!("{:?}", Body::Empty).contains("Body::Empty"));
|
assert!(format!("{:?}", Body::Empty).contains("Body::Empty"));
|
||||||
assert!(format!("{:?}", Body::Bytes(Bytes::from_static(b"1"))).contains("1"));
|
assert!(format!("{:?}", Body::Bytes(Bytes::from_static(b"1"))).contains('1'));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
|
@ -37,10 +37,10 @@ where
|
|||||||
trace!("Sending client request: {:?} {:?}", head, body.size());
|
trace!("Sending client request: {:?} {:?}", head, body.size());
|
||||||
let head_req = head.as_ref().method == Method::HEAD;
|
let head_req = head.as_ref().method == Method::HEAD;
|
||||||
let length = body.size();
|
let length = body.size();
|
||||||
let eof = match length {
|
let eof = matches!(
|
||||||
BodySize::None | BodySize::Empty | BodySize::Sized(0) => true,
|
length,
|
||||||
_ => false,
|
BodySize::None | BodySize::Empty | BodySize::Sized(0)
|
||||||
};
|
);
|
||||||
|
|
||||||
let mut req = Request::new(());
|
let mut req = Request::new(());
|
||||||
*req.uri_mut() = head.as_ref().uri.clone();
|
*req.uri_mut() = head.as_ref().uri.clone();
|
||||||
|
@ -2,7 +2,7 @@ use std::cell::RefCell;
|
|||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::rc::{Rc, Weak};
|
use std::rc::Rc;
|
||||||
use std::task::{Context, Poll};
|
use std::task::{Context, Poll};
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
@ -65,14 +65,11 @@ where
|
|||||||
|
|
||||||
// start support future
|
// start support future
|
||||||
actix_rt::spawn(ConnectorPoolSupport {
|
actix_rt::spawn(ConnectorPoolSupport {
|
||||||
connector: connector_rc.clone(),
|
connector: Rc::clone(&connector_rc),
|
||||||
inner: Rc::downgrade(&inner_rc),
|
inner: Rc::clone(&inner_rc),
|
||||||
});
|
});
|
||||||
|
|
||||||
ConnectionPool(
|
ConnectionPool(connector_rc, inner_rc)
|
||||||
connector_rc,
|
|
||||||
inner_rc,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,6 +82,13 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T, Io> Drop for ConnectionPool<T, Io> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
// wake up the ConnectorPoolSupport when dropping so it can exit properly.
|
||||||
|
self.1.borrow().waker.wake();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T, Io> Service for ConnectionPool<T, Io>
|
impl<T, Io> Service for ConnectionPool<T, Io>
|
||||||
where
|
where
|
||||||
Io: AsyncRead + AsyncWrite + Unpin + 'static,
|
Io: AsyncRead + AsyncWrite + Unpin + 'static,
|
||||||
@ -424,7 +428,7 @@ where
|
|||||||
Io: AsyncRead + AsyncWrite + Unpin + 'static,
|
Io: AsyncRead + AsyncWrite + Unpin + 'static,
|
||||||
{
|
{
|
||||||
connector: T,
|
connector: T,
|
||||||
inner: Weak<RefCell<Inner<Io>>>,
|
inner: Rc<RefCell<Inner<Io>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, Io> Future for ConnectorPoolSupport<T, Io>
|
impl<T, Io> Future for ConnectorPoolSupport<T, Io>
|
||||||
@ -438,8 +442,13 @@ where
|
|||||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
let this = self.project();
|
let this = self.project();
|
||||||
|
|
||||||
if let Some(this_inner) = this.inner.upgrade() {
|
if Rc::strong_count(this.inner) == 1 {
|
||||||
let mut inner = this_inner.as_ref().borrow_mut();
|
// If we are last copy of Inner<Io> it means the ConnectionPool is already gone
|
||||||
|
// and we are safe to exit.
|
||||||
|
return Poll::Ready(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut inner = this.inner.borrow_mut();
|
||||||
inner.waker.register(cx.waker());
|
inner.waker.register(cx.waker());
|
||||||
|
|
||||||
// check waiters
|
// check waiters
|
||||||
@ -462,7 +471,7 @@ where
|
|||||||
if let Err(conn) = tx.send(Ok(IoConnection::new(
|
if let Err(conn) = tx.send(Ok(IoConnection::new(
|
||||||
io,
|
io,
|
||||||
created,
|
created,
|
||||||
Some(Acquired(key.clone(), Some(this_inner.clone()))),
|
Some(Acquired(key.clone(), Some(this.inner.clone()))),
|
||||||
))) {
|
))) {
|
||||||
let (io, created) = conn.unwrap().into_inner();
|
let (io, created) = conn.unwrap().into_inner();
|
||||||
inner.release_conn(&key, io, created);
|
inner.release_conn(&key, io, created);
|
||||||
@ -474,7 +483,7 @@ where
|
|||||||
OpenWaitingConnection::spawn(
|
OpenWaitingConnection::spawn(
|
||||||
key.clone(),
|
key.clone(),
|
||||||
tx,
|
tx,
|
||||||
this_inner.clone(),
|
this.inner.clone(),
|
||||||
this.connector.call(connect),
|
this.connector.call(connect),
|
||||||
inner.config.clone(),
|
inner.config.clone(),
|
||||||
);
|
);
|
||||||
@ -484,9 +493,6 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
Poll::Pending
|
Poll::Pending
|
||||||
} else {
|
|
||||||
Poll::Ready(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -964,7 +964,6 @@ impl ResponseError for actix::actors::resolver::ResolverError {}
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use http::{Error as HttpError, StatusCode};
|
use http::{Error as HttpError, StatusCode};
|
||||||
use httparse;
|
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::mem::MaybeUninit;
|
|
||||||
use std::task::Poll;
|
use std::task::Poll;
|
||||||
|
|
||||||
use actix_codec::Decoder;
|
use actix_codec::Decoder;
|
||||||
@ -46,7 +45,7 @@ impl<T: MessageType> Decoder for MessageDecoder<T> {
|
|||||||
|
|
||||||
pub(crate) enum PayloadLength {
|
pub(crate) enum PayloadLength {
|
||||||
Payload(PayloadType),
|
Payload(PayloadType),
|
||||||
Upgrade,
|
UpgradeWebSocket,
|
||||||
None,
|
None,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,7 +64,7 @@ pub(crate) trait MessageType: Sized {
|
|||||||
raw_headers: &[HeaderIndex],
|
raw_headers: &[HeaderIndex],
|
||||||
) -> Result<PayloadLength, ParseError> {
|
) -> Result<PayloadLength, ParseError> {
|
||||||
let mut ka = None;
|
let mut ka = None;
|
||||||
let mut has_upgrade = false;
|
let mut has_upgrade_websocket = false;
|
||||||
let mut expect = false;
|
let mut expect = false;
|
||||||
let mut chunked = false;
|
let mut chunked = false;
|
||||||
let mut content_length = None;
|
let mut content_length = None;
|
||||||
@ -77,7 +76,7 @@ pub(crate) trait MessageType: Sized {
|
|||||||
let name =
|
let name =
|
||||||
HeaderName::from_bytes(&slice[idx.name.0..idx.name.1]).unwrap();
|
HeaderName::from_bytes(&slice[idx.name.0..idx.name.1]).unwrap();
|
||||||
|
|
||||||
// Unsafe: httparse check header value for valid utf-8
|
// SAFETY: httparse checks header value is valid UTF-8
|
||||||
let value = unsafe {
|
let value = unsafe {
|
||||||
HeaderValue::from_maybe_shared_unchecked(
|
HeaderValue::from_maybe_shared_unchecked(
|
||||||
slice.slice(idx.value.0..idx.value.1),
|
slice.slice(idx.value.0..idx.value.1),
|
||||||
@ -124,12 +123,9 @@ pub(crate) trait MessageType: Sized {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
header::UPGRADE => {
|
header::UPGRADE => {
|
||||||
has_upgrade = true;
|
|
||||||
// check content-length, some clients (dart)
|
|
||||||
// sends "content-length: 0" with websocket upgrade
|
|
||||||
if let Ok(val) = value.to_str().map(|val| val.trim()) {
|
if let Ok(val) = value.to_str().map(|val| val.trim()) {
|
||||||
if val.eq_ignore_ascii_case("websocket") {
|
if val.eq_ignore_ascii_case("websocket") {
|
||||||
content_length = None;
|
has_upgrade_websocket = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -156,13 +152,13 @@ pub(crate) trait MessageType: Sized {
|
|||||||
Ok(PayloadLength::Payload(PayloadType::Payload(
|
Ok(PayloadLength::Payload(PayloadType::Payload(
|
||||||
PayloadDecoder::chunked(),
|
PayloadDecoder::chunked(),
|
||||||
)))
|
)))
|
||||||
|
} else if has_upgrade_websocket {
|
||||||
|
Ok(PayloadLength::UpgradeWebSocket)
|
||||||
} else if let Some(len) = content_length {
|
} else if let Some(len) = content_length {
|
||||||
// Content-Length
|
// Content-Length
|
||||||
Ok(PayloadLength::Payload(PayloadType::Payload(
|
Ok(PayloadLength::Payload(PayloadType::Payload(
|
||||||
PayloadDecoder::length(len),
|
PayloadDecoder::length(len),
|
||||||
)))
|
)))
|
||||||
} else if has_upgrade {
|
|
||||||
Ok(PayloadLength::Upgrade)
|
|
||||||
} else {
|
} else {
|
||||||
Ok(PayloadLength::None)
|
Ok(PayloadLength::None)
|
||||||
}
|
}
|
||||||
@ -184,16 +180,11 @@ impl MessageType for Request {
|
|||||||
&mut self.head_mut().headers
|
&mut self.head_mut().headers
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::uninit_assumed_init)]
|
|
||||||
fn decode(src: &mut BytesMut) -> Result<Option<(Self, PayloadType)>, ParseError> {
|
fn decode(src: &mut BytesMut) -> Result<Option<(Self, PayloadType)>, ParseError> {
|
||||||
// Unsafe: we read only this data only after httparse parses headers into.
|
let mut headers: [HeaderIndex; MAX_HEADERS] = EMPTY_HEADER_INDEX_ARRAY;
|
||||||
// performance bump for pipeline benchmarks.
|
|
||||||
let mut headers: [HeaderIndex; MAX_HEADERS] =
|
|
||||||
unsafe { MaybeUninit::uninit().assume_init() };
|
|
||||||
|
|
||||||
let (len, method, uri, ver, h_len) = {
|
let (len, method, uri, ver, h_len) = {
|
||||||
let mut parsed: [httparse::Header<'_>; MAX_HEADERS] =
|
let mut parsed: [httparse::Header<'_>; MAX_HEADERS] = EMPTY_HEADER_ARRAY;
|
||||||
unsafe { MaybeUninit::uninit().assume_init() };
|
|
||||||
|
|
||||||
let mut req = httparse::Request::new(&mut parsed);
|
let mut req = httparse::Request::new(&mut parsed);
|
||||||
match req.parse(src)? {
|
match req.parse(src)? {
|
||||||
@ -222,7 +213,7 @@ impl MessageType for Request {
|
|||||||
// payload decoder
|
// payload decoder
|
||||||
let decoder = match length {
|
let decoder = match length {
|
||||||
PayloadLength::Payload(pl) => pl,
|
PayloadLength::Payload(pl) => pl,
|
||||||
PayloadLength::Upgrade => {
|
PayloadLength::UpgradeWebSocket => {
|
||||||
// upgrade(websocket)
|
// upgrade(websocket)
|
||||||
PayloadType::Stream(PayloadDecoder::eof())
|
PayloadType::Stream(PayloadDecoder::eof())
|
||||||
}
|
}
|
||||||
@ -260,16 +251,11 @@ impl MessageType for ResponseHead {
|
|||||||
&mut self.headers
|
&mut self.headers
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::uninit_assumed_init)]
|
|
||||||
fn decode(src: &mut BytesMut) -> Result<Option<(Self, PayloadType)>, ParseError> {
|
fn decode(src: &mut BytesMut) -> Result<Option<(Self, PayloadType)>, ParseError> {
|
||||||
// Unsafe: we read only this data only after httparse parses headers into.
|
let mut headers: [HeaderIndex; MAX_HEADERS] = EMPTY_HEADER_INDEX_ARRAY;
|
||||||
// performance bump for pipeline benchmarks.
|
|
||||||
let mut headers: [HeaderIndex; MAX_HEADERS] =
|
|
||||||
unsafe { MaybeUninit::uninit().assume_init() };
|
|
||||||
|
|
||||||
let (len, ver, status, h_len) = {
|
let (len, ver, status, h_len) = {
|
||||||
let mut parsed: [httparse::Header<'_>; MAX_HEADERS] =
|
let mut parsed: [httparse::Header<'_>; MAX_HEADERS] = EMPTY_HEADER_ARRAY;
|
||||||
unsafe { MaybeUninit::uninit().assume_init() };
|
|
||||||
|
|
||||||
let mut res = httparse::Response::new(&mut parsed);
|
let mut res = httparse::Response::new(&mut parsed);
|
||||||
match res.parse(src)? {
|
match res.parse(src)? {
|
||||||
@ -324,6 +310,17 @@ pub(crate) struct HeaderIndex {
|
|||||||
pub(crate) value: (usize, usize),
|
pub(crate) value: (usize, usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) const EMPTY_HEADER_INDEX: HeaderIndex = HeaderIndex {
|
||||||
|
name: (0, 0),
|
||||||
|
value: (0, 0),
|
||||||
|
};
|
||||||
|
|
||||||
|
pub(crate) const EMPTY_HEADER_INDEX_ARRAY: [HeaderIndex; MAX_HEADERS] =
|
||||||
|
[EMPTY_HEADER_INDEX; MAX_HEADERS];
|
||||||
|
|
||||||
|
pub(crate) const EMPTY_HEADER_ARRAY: [httparse::Header<'static>; MAX_HEADERS] =
|
||||||
|
[httparse::EMPTY_HEADER; MAX_HEADERS];
|
||||||
|
|
||||||
impl HeaderIndex {
|
impl HeaderIndex {
|
||||||
pub(crate) fn record(
|
pub(crate) fn record(
|
||||||
bytes: &[u8],
|
bytes: &[u8],
|
||||||
@ -655,10 +652,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn is_unhandled(&self) -> bool {
|
fn is_unhandled(&self) -> bool {
|
||||||
match self {
|
matches!(self, PayloadType::Stream(_))
|
||||||
PayloadType::Stream(_) => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -670,10 +664,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn eof(&self) -> bool {
|
fn eof(&self) -> bool {
|
||||||
match *self {
|
matches!(*self, PayloadItem::Eof)
|
||||||
PayloadItem::Eof => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -979,7 +970,7 @@ mod tests {
|
|||||||
unreachable!("Error");
|
unreachable!("Error");
|
||||||
}
|
}
|
||||||
|
|
||||||
// type in chunked
|
// intentional typo in "chunked"
|
||||||
let mut buf = BytesMut::from(
|
let mut buf = BytesMut::from(
|
||||||
"GET /test HTTP/1.1\r\n\
|
"GET /test HTTP/1.1\r\n\
|
||||||
transfer-encoding: chnked\r\n\r\n",
|
transfer-encoding: chnked\r\n\r\n",
|
||||||
@ -1040,7 +1031,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_http_request_upgrade() {
|
fn test_http_request_upgrade_websocket() {
|
||||||
let mut buf = BytesMut::from(
|
let mut buf = BytesMut::from(
|
||||||
"GET /test HTTP/1.1\r\n\
|
"GET /test HTTP/1.1\r\n\
|
||||||
connection: upgrade\r\n\
|
connection: upgrade\r\n\
|
||||||
@ -1054,6 +1045,26 @@ mod tests {
|
|||||||
assert!(pl.is_unhandled());
|
assert!(pl.is_unhandled());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_http_request_upgrade_h2c() {
|
||||||
|
let mut buf = BytesMut::from(
|
||||||
|
"GET /test HTTP/1.1\r\n\
|
||||||
|
connection: upgrade, http2-settings\r\n\
|
||||||
|
upgrade: h2c\r\n\
|
||||||
|
http2-settings: dummy\r\n\r\n",
|
||||||
|
);
|
||||||
|
let mut reader = MessageDecoder::<Request>::default();
|
||||||
|
let (req, pl) = reader.decode(&mut buf).unwrap().unwrap();
|
||||||
|
// `connection: upgrade, http2-settings` doesn't work properly..
|
||||||
|
// see MessageType::set_headers().
|
||||||
|
//
|
||||||
|
// The line below should be:
|
||||||
|
// assert_eq!(req.head().connection_type(), ConnectionType::Upgrade);
|
||||||
|
assert_eq!(req.head().connection_type(), ConnectionType::KeepAlive);
|
||||||
|
assert!(req.upgrade());
|
||||||
|
assert!(!pl.is_unhandled());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_http_request_parser_utf8() {
|
fn test_http_request_parser_utf8() {
|
||||||
let mut buf = BytesMut::from(
|
let mut buf = BytesMut::from(
|
||||||
|
@ -132,19 +132,11 @@ where
|
|||||||
B: MessageBody,
|
B: MessageBody,
|
||||||
{
|
{
|
||||||
fn is_empty(&self) -> bool {
|
fn is_empty(&self) -> bool {
|
||||||
if let State::None = self {
|
matches!(self, State::None)
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_call(&self) -> bool {
|
fn is_call(&self) -> bool {
|
||||||
if let State::ServiceCall(_) = self {
|
matches!(self, State::ServiceCall(_))
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
enum PollResponse {
|
enum PollResponse {
|
||||||
@ -156,14 +148,8 @@ enum PollResponse {
|
|||||||
impl PartialEq for PollResponse {
|
impl PartialEq for PollResponse {
|
||||||
fn eq(&self, other: &PollResponse) -> bool {
|
fn eq(&self, other: &PollResponse) -> bool {
|
||||||
match self {
|
match self {
|
||||||
PollResponse::DrainWriteBuf => match other {
|
PollResponse::DrainWriteBuf => matches!(other, PollResponse::DrainWriteBuf),
|
||||||
PollResponse::DrainWriteBuf => true,
|
PollResponse::DoNothing => matches!(other, PollResponse::DoNothing),
|
||||||
_ => false,
|
|
||||||
},
|
|
||||||
PollResponse::DoNothing => match other {
|
|
||||||
PollResponse::DoNothing => true,
|
|
||||||
_ => false,
|
|
||||||
},
|
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -303,7 +303,8 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ServiceResponseStateProj::SendPayload(ref mut stream, ref mut body) => loop {
|
ServiceResponseStateProj::SendPayload(ref mut stream, ref mut body) => {
|
||||||
|
loop {
|
||||||
loop {
|
loop {
|
||||||
if let Some(ref mut buffer) = this.buffer {
|
if let Some(ref mut buffer) = this.buffer {
|
||||||
match stream.poll_capacity(cx) {
|
match stream.poll_capacity(cx) {
|
||||||
@ -317,7 +318,8 @@ where
|
|||||||
warn!("{:?}", e);
|
warn!("{:?}", e);
|
||||||
return Poll::Ready(());
|
return Poll::Ready(());
|
||||||
} else if !buffer.is_empty() {
|
} else if !buffer.is_empty() {
|
||||||
let cap = std::cmp::min(buffer.len(), CHUNK_SIZE);
|
let cap =
|
||||||
|
std::cmp::min(buffer.len(), CHUNK_SIZE);
|
||||||
stream.reserve_capacity(cap);
|
stream.reserve_capacity(cap);
|
||||||
} else {
|
} else {
|
||||||
this.buffer.take();
|
this.buffer.take();
|
||||||
@ -332,7 +334,8 @@ where
|
|||||||
match body.as_mut().poll_next(cx) {
|
match body.as_mut().poll_next(cx) {
|
||||||
Poll::Pending => return Poll::Pending,
|
Poll::Pending => return Poll::Pending,
|
||||||
Poll::Ready(None) => {
|
Poll::Ready(None) => {
|
||||||
if let Err(e) = stream.send_data(Bytes::new(), true) {
|
if let Err(e) = stream.send_data(Bytes::new(), true)
|
||||||
|
{
|
||||||
warn!("{:?}", e);
|
warn!("{:?}", e);
|
||||||
}
|
}
|
||||||
return Poll::Ready(());
|
return Poll::Ready(());
|
||||||
@ -351,7 +354,8 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use http::Method;
|
|
||||||
use http::header;
|
use http::header;
|
||||||
|
use http::Method;
|
||||||
|
|
||||||
header! {
|
header! {
|
||||||
/// `Allow` header, defined in [RFC7231](http://tools.ietf.org/html/rfc7231#section-7.4.1)
|
/// `Allow` header, defined in [RFC7231](http://tools.ietf.org/html/rfc7231#section-7.4.1)
|
||||||
|
@ -387,26 +387,17 @@ impl ContentDisposition {
|
|||||||
|
|
||||||
/// Returns `true` if it is [`Inline`](DispositionType::Inline).
|
/// Returns `true` if it is [`Inline`](DispositionType::Inline).
|
||||||
pub fn is_inline(&self) -> bool {
|
pub fn is_inline(&self) -> bool {
|
||||||
match self.disposition {
|
matches!(self.disposition, DispositionType::Inline)
|
||||||
DispositionType::Inline => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if it is [`Attachment`](DispositionType::Attachment).
|
/// Returns `true` if it is [`Attachment`](DispositionType::Attachment).
|
||||||
pub fn is_attachment(&self) -> bool {
|
pub fn is_attachment(&self) -> bool {
|
||||||
match self.disposition {
|
matches!(self.disposition, DispositionType::Attachment)
|
||||||
DispositionType::Attachment => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if it is [`FormData`](DispositionType::FormData).
|
/// Returns `true` if it is [`FormData`](DispositionType::FormData).
|
||||||
pub fn is_form_data(&self) -> bool {
|
pub fn is_form_data(&self) -> bool {
|
||||||
match self.disposition {
|
matches!(self.disposition, DispositionType::FormData)
|
||||||
DispositionType::FormData => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if it is [`Ext`](DispositionType::Ext) and the `disp_type` matches.
|
/// Returns `true` if it is [`Ext`](DispositionType::Ext) and the `disp_type` matches.
|
||||||
|
@ -9,11 +9,13 @@
|
|||||||
|
|
||||||
pub use self::accept_charset::AcceptCharset;
|
pub use self::accept_charset::AcceptCharset;
|
||||||
//pub use self::accept_encoding::AcceptEncoding;
|
//pub use self::accept_encoding::AcceptEncoding;
|
||||||
pub use self::accept_language::AcceptLanguage;
|
|
||||||
pub use self::accept::Accept;
|
pub use self::accept::Accept;
|
||||||
|
pub use self::accept_language::AcceptLanguage;
|
||||||
pub use self::allow::Allow;
|
pub use self::allow::Allow;
|
||||||
pub use self::cache_control::{CacheControl, CacheDirective};
|
pub use self::cache_control::{CacheControl, CacheDirective};
|
||||||
pub use self::content_disposition::{ContentDisposition, DispositionType, DispositionParam};
|
pub use self::content_disposition::{
|
||||||
|
ContentDisposition, DispositionParam, DispositionType,
|
||||||
|
};
|
||||||
pub use self::content_language::ContentLanguage;
|
pub use self::content_language::ContentLanguage;
|
||||||
pub use self::content_range::{ContentRange, ContentRangeSpec};
|
pub use self::content_range::{ContentRange, ContentRangeSpec};
|
||||||
pub use self::content_type::ContentType;
|
pub use self::content_type::ContentType;
|
||||||
@ -47,7 +49,7 @@ macro_rules! __hyper__deref {
|
|||||||
&mut self.0
|
&mut self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
@ -74,8 +76,8 @@ macro_rules! test_header {
|
|||||||
($id:ident, $raw:expr) => {
|
($id:ident, $raw:expr) => {
|
||||||
#[test]
|
#[test]
|
||||||
fn $id() {
|
fn $id() {
|
||||||
use $crate::test;
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use $crate::test;
|
||||||
|
|
||||||
let raw = $raw;
|
let raw = $raw;
|
||||||
let a: Vec<Vec<u8>> = raw.iter().map(|x| x.to_vec()).collect();
|
let a: Vec<Vec<u8>> = raw.iter().map(|x| x.to_vec()).collect();
|
||||||
@ -128,7 +130,7 @@ macro_rules! test_header {
|
|||||||
assert_eq!(format!("{}", typed.unwrap()), joined);
|
assert_eq!(format!("{}", typed.unwrap()), joined);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
@ -330,11 +332,10 @@ macro_rules! header {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
mod accept_charset;
|
mod accept_charset;
|
||||||
//mod accept_encoding;
|
//mod accept_encoding;
|
||||||
mod accept_language;
|
|
||||||
mod accept;
|
mod accept;
|
||||||
|
mod accept_language;
|
||||||
mod allow;
|
mod allow;
|
||||||
mod cache_control;
|
mod cache_control;
|
||||||
mod content_disposition;
|
mod content_disposition;
|
||||||
|
@ -148,10 +148,7 @@ impl ContentEncoding {
|
|||||||
#[inline]
|
#[inline]
|
||||||
/// Is the content compressed?
|
/// Is the content compressed?
|
||||||
pub fn is_compression(self) -> bool {
|
pub fn is_compression(self) -> bool {
|
||||||
match self {
|
matches!(self, ContentEncoding::Identity | ContentEncoding::Auto)
|
||||||
ContentEncoding::Identity | ContentEncoding::Auto => false,
|
|
||||||
_ => true,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -167,7 +167,6 @@ where
|
|||||||
mod tests {
|
mod tests {
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use encoding_rs::ISO_8859_2;
|
use encoding_rs::ISO_8859_2;
|
||||||
use mime;
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::test::TestRequest;
|
use crate::test::TestRequest;
|
||||||
|
@ -229,10 +229,7 @@ mod tests {
|
|||||||
fn is_none(
|
fn is_none(
|
||||||
frm: &Result<Option<(bool, OpCode, Option<BytesMut>)>, ProtocolError>,
|
frm: &Result<Option<(bool, OpCode, Option<BytesMut>)>, ProtocolError>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
match *frm {
|
matches!(*frm, Ok(None))
|
||||||
Ok(None) => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extract(
|
fn extract(
|
||||||
|
@ -139,7 +139,7 @@ mod tests {
|
|||||||
let mut masked = unmasked.clone();
|
let mut masked = unmasked.clone();
|
||||||
apply_mask_fallback(&mut masked[1..], &mask);
|
apply_mask_fallback(&mut masked[1..], &mask);
|
||||||
|
|
||||||
let mut masked_fast = unmasked.clone();
|
let mut masked_fast = unmasked;
|
||||||
apply_mask(&mut masked_fast[1..], mask_u32);
|
apply_mask(&mut masked_fast[1..], mask_u32);
|
||||||
|
|
||||||
assert_eq!(masked, masked_fast);
|
assert_eq!(masked, masked_fast);
|
||||||
|
@ -274,9 +274,7 @@ async fn test_h2_head_empty() {
|
|||||||
async fn test_h2_head_binary() {
|
async fn test_h2_head_binary() {
|
||||||
let mut srv = test_server(move || {
|
let mut srv = test_server(move || {
|
||||||
HttpService::build()
|
HttpService::build()
|
||||||
.h2(|_| {
|
.h2(|_| ok::<_, ()>(Response::Ok().body(STR)))
|
||||||
ok::<_, ()>(Response::Ok().body(STR))
|
|
||||||
})
|
|
||||||
.openssl(ssl_acceptor())
|
.openssl(ssl_acceptor())
|
||||||
.map_err(|_| ())
|
.map_err(|_| ())
|
||||||
})
|
})
|
||||||
|
@ -280,9 +280,7 @@ async fn test_h2_head_empty() {
|
|||||||
async fn test_h2_head_binary() {
|
async fn test_h2_head_binary() {
|
||||||
let mut srv = test_server(move || {
|
let mut srv = test_server(move || {
|
||||||
HttpService::build()
|
HttpService::build()
|
||||||
.h2(|_| {
|
.h2(|_| ok::<_, ()>(Response::Ok().body(STR)))
|
||||||
ok::<_, ()>(Response::Ok().body(STR))
|
|
||||||
})
|
|
||||||
.rustls(ssl_acceptor())
|
.rustls(ssl_acceptor())
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
@ -489,9 +489,7 @@ async fn test_h1_head_empty() {
|
|||||||
async fn test_h1_head_binary() {
|
async fn test_h1_head_binary() {
|
||||||
let mut srv = test_server(|| {
|
let mut srv = test_server(|| {
|
||||||
HttpService::build()
|
HttpService::build()
|
||||||
.h1(|_| {
|
.h1(|_| ok::<_, ()>(Response::Ok().body(STR)))
|
||||||
ok::<_, ()>(Response::Ok().body(STR))
|
|
||||||
})
|
|
||||||
.tcp()
|
.tcp()
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
@ -1,15 +1,17 @@
|
|||||||
# Changes
|
# Changes
|
||||||
|
|
||||||
## [0.3.0-alpha.1] - 2020-05-25
|
## Unreleased - 2020-xx-xx
|
||||||
|
|
||||||
|
|
||||||
|
## 0.3.0-beta.1 - 2020-07-15
|
||||||
|
* Update `actix-web` to 3.0.0-beta.1
|
||||||
|
|
||||||
|
|
||||||
|
## 0.3.0-alpha.1 - 2020-05-25
|
||||||
* Update `actix-web` to 3.0.0-alpha.3
|
* Update `actix-web` to 3.0.0-alpha.3
|
||||||
|
|
||||||
* Bump minimum supported Rust version to 1.40
|
* Bump minimum supported Rust version to 1.40
|
||||||
|
|
||||||
* Minimize `futures` dependencies
|
* Minimize `futures` dependencies
|
||||||
|
|
||||||
* Remove the unused `time` dependency
|
* Remove the unused `time` dependency
|
||||||
|
|
||||||
* Fix missing `std::error::Error` implement for `MultipartError`.
|
* Fix missing `std::error::Error` implement for `MultipartError`.
|
||||||
|
|
||||||
## [0.2.0] - 2019-12-20
|
## [0.2.0] - 2019-12-20
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "actix-multipart"
|
name = "actix-multipart"
|
||||||
version = "0.3.0-alpha.1"
|
version = "0.3.0-beta.1"
|
||||||
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
||||||
description = "Multipart support for actix web framework."
|
description = "Multipart support for actix web framework."
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
@ -8,7 +8,7 @@ keywords = ["http", "web", "framework", "async", "futures"]
|
|||||||
homepage = "https://actix.rs"
|
homepage = "https://actix.rs"
|
||||||
repository = "https://github.com/actix/actix-web.git"
|
repository = "https://github.com/actix/actix-web.git"
|
||||||
documentation = "https://docs.rs/actix-multipart/"
|
documentation = "https://docs.rs/actix-multipart/"
|
||||||
license = "MIT/Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
@ -16,7 +16,7 @@ name = "actix_multipart"
|
|||||||
path = "src/lib.rs"
|
path = "src/lib.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix-web = { version = "3.0.0-alpha.3", default-features = false }
|
actix-web = { version = "3.0.0-beta.1", default-features = false }
|
||||||
actix-service = "1.0.1"
|
actix-service = "1.0.1"
|
||||||
actix-utils = "1.0.3"
|
actix-utils = "1.0.3"
|
||||||
bytes = "0.5.3"
|
bytes = "0.5.3"
|
||||||
@ -29,4 +29,4 @@ twoway = "0.2"
|
|||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
actix-rt = "1.0.0"
|
actix-rt = "1.0.0"
|
||||||
actix-http = "2.0.0-alpha.4"
|
actix-http = "2.0.0-beta.3"
|
||||||
|
@ -9,8 +9,6 @@ use std::{cmp, fmt};
|
|||||||
|
|
||||||
use bytes::{Bytes, BytesMut};
|
use bytes::{Bytes, BytesMut};
|
||||||
use futures_util::stream::{LocalBoxStream, Stream, StreamExt};
|
use futures_util::stream::{LocalBoxStream, Stream, StreamExt};
|
||||||
use httparse;
|
|
||||||
use mime;
|
|
||||||
|
|
||||||
use actix_utils::task::LocalWaker;
|
use actix_utils::task::LocalWaker;
|
||||||
use actix_web::error::{ParseError, PayloadError};
|
use actix_web::error::{ParseError, PayloadError};
|
||||||
@ -876,11 +874,11 @@ mod tests {
|
|||||||
|
|
||||||
impl SlowStream {
|
impl SlowStream {
|
||||||
fn new(bytes: Bytes) -> SlowStream {
|
fn new(bytes: Bytes) -> SlowStream {
|
||||||
return SlowStream {
|
SlowStream {
|
||||||
bytes: bytes,
|
bytes,
|
||||||
pos: 0,
|
pos: 0,
|
||||||
ready: false,
|
ready: false,
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,10 +2,13 @@
|
|||||||
|
|
||||||
## [Unreleased] - 2020-xx-xx
|
## [Unreleased] - 2020-xx-xx
|
||||||
|
|
||||||
|
|
||||||
|
## [3.0.0-beta.1] - 2020-xx-xx
|
||||||
|
* Update `actix-web` & `actix-http` dependencies to beta.1
|
||||||
* Bump minimum supported Rust version to 1.40
|
* Bump minimum supported Rust version to 1.40
|
||||||
|
|
||||||
## [3.0.0-alpha.1] - 2020-05-08
|
|
||||||
|
|
||||||
|
## [3.0.0-alpha.1] - 2020-05-08
|
||||||
* Update the actix-web dependency to 3.0.0-alpha.1
|
* Update the actix-web dependency to 3.0.0-alpha.1
|
||||||
* Update the actix dependency to 0.10.0-alpha.2
|
* Update the actix dependency to 0.10.0-alpha.2
|
||||||
* Update the actix-http dependency to 2.0.0-alpha.3
|
* Update the actix-http dependency to 2.0.0-alpha.3
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "actix-web-actors"
|
name = "actix-web-actors"
|
||||||
version = "3.0.0-alpha.1"
|
version = "3.0.0-beta.1"
|
||||||
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
||||||
description = "Actix actors support for actix web framework."
|
description = "Actix actors support for actix web framework."
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
@ -8,7 +8,7 @@ keywords = ["actix", "http", "web", "framework", "async"]
|
|||||||
homepage = "https://actix.rs"
|
homepage = "https://actix.rs"
|
||||||
repository = "https://github.com/actix/actix-web.git"
|
repository = "https://github.com/actix/actix-web.git"
|
||||||
documentation = "https://docs.rs/actix-web-actors/"
|
documentation = "https://docs.rs/actix-web-actors/"
|
||||||
license = "MIT/Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
@ -17,8 +17,8 @@ path = "src/lib.rs"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix = "0.10.0-alpha.2"
|
actix = "0.10.0-alpha.2"
|
||||||
actix-web = { version = "3.0.0-alpha.3", default-features = false }
|
actix-web = { version = "3.0.0-beta.1", default-features = false }
|
||||||
actix-http = "2.0.0-alpha.4"
|
actix-http = "2.0.0-beta.3"
|
||||||
actix-codec = "0.2.0"
|
actix-codec = "0.2.0"
|
||||||
bytes = "0.5.2"
|
bytes = "0.5.2"
|
||||||
futures-channel = { version = "0.3.5", default-features = false }
|
futures-channel = { version = "0.3.5", default-features = false }
|
||||||
|
@ -30,8 +30,8 @@ impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for Ws {
|
|||||||
async fn test_simple() {
|
async fn test_simple() {
|
||||||
let mut srv = test::start(|| {
|
let mut srv = test::start(|| {
|
||||||
App::new().service(web::resource("/").to(
|
App::new().service(web::resource("/").to(
|
||||||
|req: HttpRequest, stream: web::Payload| {
|
|req: HttpRequest, stream: web::Payload| async move {
|
||||||
async move { ws::start(Ws, &req, stream) }
|
ws::start(Ws, &req, stream)
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
});
|
});
|
||||||
@ -51,7 +51,7 @@ async fn test_simple() {
|
|||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let item = framed.next().await.unwrap().unwrap();
|
let item = framed.next().await.unwrap().unwrap();
|
||||||
assert_eq!(item, ws::Frame::Binary(Bytes::from_static(b"text").into()));
|
assert_eq!(item, ws::Frame::Binary(Bytes::from_static(b"text")));
|
||||||
|
|
||||||
framed.send(ws::Message::Ping("text".into())).await.unwrap();
|
framed.send(ws::Message::Ping("text".into())).await.unwrap();
|
||||||
let item = framed.next().await.unwrap().unwrap();
|
let item = framed.next().await.unwrap().unwrap();
|
||||||
|
@ -1,18 +1,20 @@
|
|||||||
# Changes
|
# Changes
|
||||||
|
|
||||||
## [Unreleased] - XXXX-XX-XX
|
## Unreleased - 2020-xx-xx
|
||||||
|
|
||||||
* Add main entry-point macro that uses re-exported runtime.
|
|
||||||
|
## 0.3.0-beta.1 - 2020-07-14
|
||||||
|
* Add main entry-point macro that uses re-exported runtime. [#1559]
|
||||||
|
|
||||||
|
[#1559]: https://github.com/actix/actix-web/pull/1559
|
||||||
|
|
||||||
|
|
||||||
## [0.2.2] - 2020-05-23
|
## [0.2.2] - 2020-05-23
|
||||||
|
|
||||||
* Add resource middleware on actix-web-codegen [#1467]
|
* Add resource middleware on actix-web-codegen [#1467]
|
||||||
|
|
||||||
[#1467]: https://github.com/actix/actix-web/pull/1467
|
[#1467]: https://github.com/actix/actix-web/pull/1467
|
||||||
|
|
||||||
## [0.2.1] - 2020-02-25
|
## [0.2.1] - 2020-02-25
|
||||||
|
|
||||||
* Add `#[allow(missing_docs)]` attribute to generated structs [#1368]
|
* Add `#[allow(missing_docs)]` attribute to generated structs [#1368]
|
||||||
* Allow the handler function to be named as `config` [#1290]
|
* Allow the handler function to be named as `config` [#1290]
|
||||||
|
|
||||||
@ -26,7 +28,6 @@
|
|||||||
## [0.1.3] - 2019-10-14
|
## [0.1.3] - 2019-10-14
|
||||||
|
|
||||||
* Bump up `syn` & `quote` to 1.0
|
* Bump up `syn` & `quote` to 1.0
|
||||||
|
|
||||||
* Provide better error message
|
* Provide better error message
|
||||||
|
|
||||||
## [0.1.2] - 2019-06-04
|
## [0.1.2] - 2019-06-04
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "actix-web-codegen"
|
name = "actix-web-codegen"
|
||||||
version = "0.2.2"
|
version = "0.3.0-beta.1"
|
||||||
description = "Actix web proc macros"
|
description = "Actix web proc macros"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
homepage = "https://actix.rs"
|
homepage = "https://actix.rs"
|
||||||
repository = "https://github.com/actix/actix-web"
|
repository = "https://github.com/actix/actix-web"
|
||||||
documentation = "https://docs.rs/actix-web-codegen"
|
documentation = "https://docs.rs/actix-web-codegen"
|
||||||
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
||||||
license = "MIT/Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
@ -20,5 +20,5 @@ proc-macro2 = "1"
|
|||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
actix-rt = "1.0.0"
|
actix-rt = "1.0.0"
|
||||||
actix-web = "3.0.0-alpha.3"
|
actix-web = "3.0.0-beta.1"
|
||||||
futures-util = { version = "0.3.5", default-features = false }
|
futures-util = { version = "0.3.5", default-features = false }
|
||||||
|
@ -3,7 +3,7 @@ extern crate proc_macro;
|
|||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use proc_macro2::{Span, TokenStream as TokenStream2};
|
use proc_macro2::{Span, TokenStream as TokenStream2};
|
||||||
use quote::{format_ident, quote, ToTokens, TokenStreamExt};
|
use quote::{format_ident, quote, ToTokens, TokenStreamExt};
|
||||||
use syn::{AttributeArgs, Ident, NestedMeta, parse_macro_input};
|
use syn::{parse_macro_input, AttributeArgs, Ident, NestedMeta};
|
||||||
|
|
||||||
enum ResourceType {
|
enum ResourceType {
|
||||||
Async,
|
Async,
|
||||||
@ -196,7 +196,12 @@ impl ToTokens for Route {
|
|||||||
name,
|
name,
|
||||||
guard,
|
guard,
|
||||||
ast,
|
ast,
|
||||||
args: Args { path, guards, wrappers },
|
args:
|
||||||
|
Args {
|
||||||
|
path,
|
||||||
|
guards,
|
||||||
|
wrappers,
|
||||||
|
},
|
||||||
resource_type,
|
resource_type,
|
||||||
} = self;
|
} = self;
|
||||||
let resource_name = name.to_string();
|
let resource_name = name.to_string();
|
||||||
|
@ -2,11 +2,11 @@ use std::future::Future;
|
|||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::task::{Context, Poll};
|
use std::task::{Context, Poll};
|
||||||
|
|
||||||
use actix_web::{http, test, web::Path, App, HttpResponse, Responder, Error};
|
use actix_web::dev::{Service, ServiceRequest, ServiceResponse, Transform};
|
||||||
use actix_web::dev::{Service, Transform, ServiceRequest, ServiceResponse};
|
use actix_web::http::header::{HeaderName, HeaderValue};
|
||||||
|
use actix_web::{http, test, web::Path, App, Error, HttpResponse, Responder};
|
||||||
use actix_web_codegen::{connect, delete, get, head, options, patch, post, put, trace};
|
use actix_web_codegen::{connect, delete, get, head, options, patch, post, put, trace};
|
||||||
use futures_util::future;
|
use futures_util::future;
|
||||||
use actix_web::http::header::{HeaderName, HeaderValue};
|
|
||||||
|
|
||||||
// Make sure that we can name function as 'config'
|
// Make sure that we can name function as 'config'
|
||||||
#[get("/config")]
|
#[get("/config")]
|
||||||
@ -112,6 +112,7 @@ where
|
|||||||
type Request = ServiceRequest;
|
type Request = ServiceRequest;
|
||||||
type Response = ServiceResponse<B>;
|
type Response = ServiceResponse<B>;
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>;
|
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>;
|
||||||
|
|
||||||
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||||
@ -119,7 +120,6 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn call(&mut self, req: ServiceRequest) -> Self::Future {
|
fn call(&mut self, req: ServiceRequest) -> Self::Future {
|
||||||
|
|
||||||
let fut = self.service.call(req);
|
let fut = self.service.call(req);
|
||||||
|
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
@ -223,10 +223,7 @@ async fn test_auto_async() {
|
|||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_wrap() {
|
async fn test_wrap() {
|
||||||
let srv = test::start(|| {
|
let srv = test::start(|| App::new().service(get_wrap));
|
||||||
App::new()
|
|
||||||
.service(get_wrap)
|
|
||||||
});
|
|
||||||
|
|
||||||
let request = srv.request(http::Method::GET, srv.url("/test/wrap"));
|
let request = srv.request(http::Method::GET, srv.url("/test/wrap"));
|
||||||
let response = request.send().await.unwrap();
|
let response = request.send().await.unwrap();
|
||||||
|
@ -1,5 +1,17 @@
|
|||||||
# Changes
|
# Changes
|
||||||
|
|
||||||
|
## Unreleased - 2020-xx-xx
|
||||||
|
|
||||||
|
|
||||||
|
## 2.0.0-beta.2 - 2020-07-21
|
||||||
|
### Changed
|
||||||
|
* Update `actix-http` dependency to 2.0.0-beta.2
|
||||||
|
|
||||||
|
|
||||||
|
## [2.0.0-beta.1] - 2020-07-14
|
||||||
|
### Changed
|
||||||
|
* Update `actix-http` dependency to 2.0.0-beta.1
|
||||||
|
|
||||||
## [2.0.0-alpha.2] - 2020-05-21
|
## [2.0.0-alpha.2] - 2020-05-21
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
@ -1,17 +1,20 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "awc"
|
name = "awc"
|
||||||
version = "2.0.0-alpha.2"
|
version = "2.0.0-beta.2"
|
||||||
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
||||||
description = "Actix http client."
|
description = "Async HTTP client library that uses the Actix runtime."
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
keywords = ["actix", "http", "framework", "async", "web"]
|
keywords = ["actix", "http", "framework", "async", "web"]
|
||||||
homepage = "https://actix.rs"
|
homepage = "https://actix.rs"
|
||||||
repository = "https://github.com/actix/actix-web.git"
|
repository = "https://github.com/actix/actix-web.git"
|
||||||
documentation = "https://docs.rs/awc/"
|
documentation = "https://docs.rs/awc/"
|
||||||
categories = ["network-programming", "asynchronous",
|
categories = [
|
||||||
|
"network-programming",
|
||||||
|
"asynchronous",
|
||||||
"web-programming::http-client",
|
"web-programming::http-client",
|
||||||
"web-programming::websocket"]
|
"web-programming::websocket",
|
||||||
license = "MIT/Apache-2.0"
|
]
|
||||||
|
license = "MIT OR Apache-2.0"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
@ -36,7 +39,7 @@ compress = ["actix-http/compress"]
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
actix-codec = "0.2.0"
|
actix-codec = "0.2.0"
|
||||||
actix-service = "1.0.1"
|
actix-service = "1.0.1"
|
||||||
actix-http = "2.0.0-alpha.4"
|
actix-http = "2.0.0-beta.3"
|
||||||
actix-rt = "1.0.0"
|
actix-rt = "1.0.0"
|
||||||
|
|
||||||
base64 = "0.12"
|
base64 = "0.12"
|
||||||
@ -56,7 +59,7 @@ rust-tls = { version = "0.17.0", package = "rustls", optional = true, features =
|
|||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
actix-connect = { version = "2.0.0-alpha.2", features = ["openssl"] }
|
actix-connect = { version = "2.0.0-alpha.2", features = ["openssl"] }
|
||||||
actix-web = { version = "3.0.0-alpha.3", features = ["openssl"] }
|
actix-web = { version = "3.0.0-alpha.3", features = ["openssl"] }
|
||||||
actix-http = { version = "2.0.0-alpha.4", features = ["openssl"] }
|
actix-http = { version = "2.0.0-beta.3", features = ["openssl"] }
|
||||||
actix-http-test = { version = "2.0.0-alpha.1", features = ["openssl"] }
|
actix-http-test = { version = "2.0.0-alpha.1", features = ["openssl"] }
|
||||||
actix-utils = "1.0.3"
|
actix-utils = "1.0.3"
|
||||||
actix-server = "1.0.0"
|
actix-server = "1.0.0"
|
||||||
|
@ -402,14 +402,12 @@ mod tests {
|
|||||||
|
|
||||||
fn json_eq(err: JsonPayloadError, other: JsonPayloadError) -> bool {
|
fn json_eq(err: JsonPayloadError, other: JsonPayloadError) -> bool {
|
||||||
match err {
|
match err {
|
||||||
JsonPayloadError::Payload(PayloadError::Overflow) => match other {
|
JsonPayloadError::Payload(PayloadError::Overflow) => {
|
||||||
JsonPayloadError::Payload(PayloadError::Overflow) => true,
|
matches!(other, JsonPayloadError::Payload(PayloadError::Overflow))
|
||||||
_ => false,
|
}
|
||||||
},
|
JsonPayloadError::ContentType => {
|
||||||
JsonPayloadError::ContentType => match other {
|
matches!(other, JsonPayloadError::ContentType)
|
||||||
JsonPayloadError::ContentType => true,
|
}
|
||||||
_ => false,
|
|
||||||
},
|
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -167,8 +167,7 @@ async fn test_connection_reuse() {
|
|||||||
})
|
})
|
||||||
.and_then(
|
.and_then(
|
||||||
HttpService::new(map_config(
|
HttpService::new(map_config(
|
||||||
App::new()
|
App::new().service(web::resource("/").route(web::to(HttpResponse::Ok))),
|
||||||
.service(web::resource("/").route(web::to(|| HttpResponse::Ok()))),
|
|
||||||
|_| AppConfig::default(),
|
|_| AppConfig::default(),
|
||||||
))
|
))
|
||||||
.tcp(),
|
.tcp(),
|
||||||
@ -205,8 +204,7 @@ async fn test_connection_force_close() {
|
|||||||
})
|
})
|
||||||
.and_then(
|
.and_then(
|
||||||
HttpService::new(map_config(
|
HttpService::new(map_config(
|
||||||
App::new()
|
App::new().service(web::resource("/").route(web::to(HttpResponse::Ok))),
|
||||||
.service(web::resource("/").route(web::to(|| HttpResponse::Ok()))),
|
|
||||||
|_| AppConfig::default(),
|
|_| AppConfig::default(),
|
||||||
))
|
))
|
||||||
.tcp(),
|
.tcp(),
|
||||||
|
@ -32,8 +32,7 @@ async fn test_connection_window_size() {
|
|||||||
let srv = test_server(move || {
|
let srv = test_server(move || {
|
||||||
HttpService::build()
|
HttpService::build()
|
||||||
.h2(map_config(
|
.h2(map_config(
|
||||||
App::new()
|
App::new().service(web::resource("/").route(web::to(HttpResponse::Ok))),
|
||||||
.service(web::resource("/").route(web::to(|| HttpResponse::Ok()))),
|
|
||||||
|_| AppConfig::default(),
|
|_| AppConfig::default(),
|
||||||
))
|
))
|
||||||
.openssl(ssl_acceptor())
|
.openssl(ssl_acceptor())
|
||||||
|
@ -64,9 +64,8 @@ async fn _test_connection_reuse_h2() {
|
|||||||
.and_then(
|
.and_then(
|
||||||
HttpService::build()
|
HttpService::build()
|
||||||
.h2(map_config(
|
.h2(map_config(
|
||||||
App::new().service(
|
App::new()
|
||||||
web::resource("/").route(web::to(|| HttpResponse::Ok())),
|
.service(web::resource("/").route(web::to(HttpResponse::Ok))),
|
||||||
),
|
|
||||||
|_| AppConfig::default(),
|
|_| AppConfig::default(),
|
||||||
))
|
))
|
||||||
.openssl(ssl_acceptor())
|
.openssl(ssl_acceptor())
|
||||||
|
@ -45,9 +45,8 @@ async fn test_connection_reuse_h2() {
|
|||||||
.and_then(
|
.and_then(
|
||||||
HttpService::build()
|
HttpService::build()
|
||||||
.h2(map_config(
|
.h2(map_config(
|
||||||
App::new().service(
|
App::new()
|
||||||
web::resource("/").route(web::to(|| HttpResponse::Ok())),
|
.service(web::resource("/").route(web::to(HttpResponse::Ok))),
|
||||||
),
|
|
||||||
|_| AppConfig::default(),
|
|_| AppConfig::default(),
|
||||||
))
|
))
|
||||||
.openssl(ssl_acceptor())
|
.openssl(ssl_acceptor())
|
||||||
|
2
docs/graphs/.gitignore
vendored
Normal file
2
docs/graphs/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
# do not track rendered graphs
|
||||||
|
*.png
|
11
docs/graphs/dependency-graphs.md
Normal file
11
docs/graphs/dependency-graphs.md
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# Actix Ecosystem Dependency Graphs
|
||||||
|
|
||||||
|
See rendered versions of these dot graphs [on the wiki](https://github.com/actix/actix-web/wiki/Dependency-Graph).
|
||||||
|
|
||||||
|
## Rendering
|
||||||
|
|
||||||
|
Dot graphs were rendered using the `dot` command from [GraphViz](https://www.graphviz.org/doc/info/command.html):
|
||||||
|
|
||||||
|
```sh
|
||||||
|
for f in $(ls docs/graphs/*.dot | xargs); do dot $f -Tpng -o${f:r}.png; done
|
||||||
|
```
|
25
docs/graphs/net-only.dot
Normal file
25
docs/graphs/net-only.dot
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
digraph {
|
||||||
|
subgraph cluster_net {
|
||||||
|
label="actix/actix-net";
|
||||||
|
"actix-codec"
|
||||||
|
"actix-connect"
|
||||||
|
"actix-macros"
|
||||||
|
"actix-rt"
|
||||||
|
"actix-server"
|
||||||
|
"actix-service"
|
||||||
|
"actix-testing"
|
||||||
|
"actix-threadpool"
|
||||||
|
"actix-tls"
|
||||||
|
"actix-tracing"
|
||||||
|
"actix-utils"
|
||||||
|
"actix-router"
|
||||||
|
}
|
||||||
|
|
||||||
|
"actix-utils" -> { "actix-service" "actix-rt" "actix-codec" }
|
||||||
|
"actix-tracing" -> { "actix-service" }
|
||||||
|
"actix-tls" -> { "actix-service" "actix-codec" "actix-utils" "actix-rt" }
|
||||||
|
"actix-testing" -> { "actix-rt" "actix-macros" "actix-server" "actix-service" }
|
||||||
|
"actix-server" -> { "actix-service" "actix-rt" "actix-codec" "actix-utils" }
|
||||||
|
"actix-rt" -> { "actix-macros" "actix-threadpool" }
|
||||||
|
"actix-connect" -> { "actix-service" "actix-codec" "actix-utils" "actix-rt" }
|
||||||
|
}
|
30
docs/graphs/web-focus.dot
Normal file
30
docs/graphs/web-focus.dot
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
digraph {
|
||||||
|
subgraph cluster_web {
|
||||||
|
label="actix/actix-web"
|
||||||
|
"awc"
|
||||||
|
"actix-web"
|
||||||
|
"actix-files"
|
||||||
|
"actix-http"
|
||||||
|
"actix-multipart"
|
||||||
|
"actix-web-actors"
|
||||||
|
"actix-web-codegen"
|
||||||
|
}
|
||||||
|
|
||||||
|
"actix-web" -> { "actix-codec" "actix-service" "actix-utils" "actix-router" "actix-rt" "actix-server" "actix-testing" "actix-macros" "actix-threadpool" "actix-tls" "actix-web-codegen" "actix-http" "awc" }
|
||||||
|
"awc" -> { "actix-codec" "actix-service" "actix-http" "actix-rt" }
|
||||||
|
"actix-web-actors" -> { "actix" "actix-web" "actix-http" "actix-codec" }
|
||||||
|
"actix-multipart" -> { "actix-web" "actix-service" "actix-utils" }
|
||||||
|
"actix-http" -> { "actix-service" "actix-codec" "actix-connect" "actix-utils" "actix-rt" "actix-threadpool" }
|
||||||
|
"actix-http" -> { "actix" "actix-tls" }[color=blue] // optional
|
||||||
|
"actix-files" -> { "actix-web" "actix-http" }
|
||||||
|
|
||||||
|
// net
|
||||||
|
|
||||||
|
"actix-utils" -> { "actix-service" "actix-rt" "actix-codec" }
|
||||||
|
"actix-tracing" -> { "actix-service" }
|
||||||
|
"actix-tls" -> { "actix-service" "actix-codec" "actix-utils" "actix-rt" }
|
||||||
|
"actix-testing" -> { "actix-rt" "actix-macros" "actix-server" "actix-service" }
|
||||||
|
"actix-server" -> { "actix-service" "actix-rt" "actix-codec" "actix-utils" }
|
||||||
|
"actix-rt" -> { "actix-macros" "actix-threadpool" }
|
||||||
|
"actix-connect" -> { "actix-service" "actix-codec" "actix-utils" "actix-rt" }
|
||||||
|
}
|
19
docs/graphs/web-only.dot
Normal file
19
docs/graphs/web-only.dot
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
digraph {
|
||||||
|
subgraph cluster_web {
|
||||||
|
label="actix/actix-web"
|
||||||
|
"awc"
|
||||||
|
"actix-web"
|
||||||
|
"actix-files"
|
||||||
|
"actix-http"
|
||||||
|
"actix-multipart"
|
||||||
|
"actix-web-actors"
|
||||||
|
"actix-web-codegen"
|
||||||
|
}
|
||||||
|
|
||||||
|
"actix-web" -> { "actix-web-codegen" "actix-http" "awc" }
|
||||||
|
"awc" -> { "actix-http" }
|
||||||
|
"actix-web-actors" -> { "actix" "actix-web" "actix-http" }
|
||||||
|
"actix-multipart" -> { "actix-web" }
|
||||||
|
"actix-http" -> { "actix" }[color=blue] // optional
|
||||||
|
"actix-files" -> { "actix-web" "actix-http" }
|
||||||
|
}
|
@ -1 +1 @@
|
|||||||
1.41.1
|
1.42.0
|
||||||
|
21
src/app.rs
21
src/app.rs
@ -489,7 +489,7 @@ mod tests {
|
|||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_default_resource() {
|
async fn test_default_resource() {
|
||||||
let mut srv = init_service(
|
let mut srv = init_service(
|
||||||
App::new().service(web::resource("/test").to(|| HttpResponse::Ok())),
|
App::new().service(web::resource("/test").to(HttpResponse::Ok)),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
let req = TestRequest::with_uri("/test").to_request();
|
let req = TestRequest::with_uri("/test").to_request();
|
||||||
@ -502,13 +502,13 @@ mod tests {
|
|||||||
|
|
||||||
let mut srv = init_service(
|
let mut srv = init_service(
|
||||||
App::new()
|
App::new()
|
||||||
.service(web::resource("/test").to(|| HttpResponse::Ok()))
|
.service(web::resource("/test").to(HttpResponse::Ok))
|
||||||
.service(
|
.service(
|
||||||
web::resource("/test2")
|
web::resource("/test2")
|
||||||
.default_service(|r: ServiceRequest| {
|
.default_service(|r: ServiceRequest| {
|
||||||
ok(r.into_response(HttpResponse::Created()))
|
ok(r.into_response(HttpResponse::Created()))
|
||||||
})
|
})
|
||||||
.route(web::get().to(|| HttpResponse::Ok())),
|
.route(web::get().to(HttpResponse::Ok)),
|
||||||
)
|
)
|
||||||
.default_service(|r: ServiceRequest| {
|
.default_service(|r: ServiceRequest| {
|
||||||
ok(r.into_response(HttpResponse::MethodNotAllowed()))
|
ok(r.into_response(HttpResponse::MethodNotAllowed()))
|
||||||
@ -585,7 +585,7 @@ mod tests {
|
|||||||
DefaultHeaders::new()
|
DefaultHeaders::new()
|
||||||
.header(header::CONTENT_TYPE, HeaderValue::from_static("0001")),
|
.header(header::CONTENT_TYPE, HeaderValue::from_static("0001")),
|
||||||
)
|
)
|
||||||
.route("/test", web::get().to(|| HttpResponse::Ok())),
|
.route("/test", web::get().to(HttpResponse::Ok)),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
let req = TestRequest::with_uri("/test").to_request();
|
let req = TestRequest::with_uri("/test").to_request();
|
||||||
@ -601,7 +601,7 @@ mod tests {
|
|||||||
async fn test_router_wrap() {
|
async fn test_router_wrap() {
|
||||||
let mut srv = init_service(
|
let mut srv = init_service(
|
||||||
App::new()
|
App::new()
|
||||||
.route("/test", web::get().to(|| HttpResponse::Ok()))
|
.route("/test", web::get().to(HttpResponse::Ok))
|
||||||
.wrap(
|
.wrap(
|
||||||
DefaultHeaders::new()
|
DefaultHeaders::new()
|
||||||
.header(header::CONTENT_TYPE, HeaderValue::from_static("0001")),
|
.header(header::CONTENT_TYPE, HeaderValue::from_static("0001")),
|
||||||
@ -632,7 +632,7 @@ mod tests {
|
|||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.service(web::resource("/test").to(|| HttpResponse::Ok())),
|
.service(web::resource("/test").to(HttpResponse::Ok)),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
let req = TestRequest::with_uri("/test").to_request();
|
let req = TestRequest::with_uri("/test").to_request();
|
||||||
@ -648,7 +648,7 @@ mod tests {
|
|||||||
async fn test_router_wrap_fn() {
|
async fn test_router_wrap_fn() {
|
||||||
let mut srv = init_service(
|
let mut srv = init_service(
|
||||||
App::new()
|
App::new()
|
||||||
.route("/test", web::get().to(|| HttpResponse::Ok()))
|
.route("/test", web::get().to(HttpResponse::Ok))
|
||||||
.wrap_fn(|req, srv| {
|
.wrap_fn(|req, srv| {
|
||||||
let fut = srv.call(req);
|
let fut = srv.call(req);
|
||||||
async {
|
async {
|
||||||
@ -679,10 +679,9 @@ mod tests {
|
|||||||
.route(
|
.route(
|
||||||
"/test",
|
"/test",
|
||||||
web::get().to(|req: HttpRequest| {
|
web::get().to(|req: HttpRequest| {
|
||||||
HttpResponse::Ok().body(format!(
|
HttpResponse::Ok().body(
|
||||||
"{}",
|
req.url_for("youtube", &["12345"]).unwrap().to_string(),
|
||||||
req.url_for("youtube", &["12345"]).unwrap()
|
)
|
||||||
))
|
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -10,6 +10,7 @@ use actix_router::{Path, ResourceDef, ResourceInfo, Router, Url};
|
|||||||
use actix_service::boxed::{self, BoxService, BoxServiceFactory};
|
use actix_service::boxed::{self, BoxService, BoxServiceFactory};
|
||||||
use actix_service::{fn_service, Service, ServiceFactory};
|
use actix_service::{fn_service, Service, ServiceFactory};
|
||||||
use futures_util::future::{join_all, ok, FutureExt, LocalBoxFuture};
|
use futures_util::future::{join_all, ok, FutureExt, LocalBoxFuture};
|
||||||
|
use tinyvec::tiny_vec;
|
||||||
|
|
||||||
use crate::config::{AppConfig, AppService};
|
use crate::config::{AppConfig, AppService};
|
||||||
use crate::data::{DataFactory, FnDataFactory};
|
use crate::data::{DataFactory, FnDataFactory};
|
||||||
@ -245,7 +246,7 @@ where
|
|||||||
inner.path.reset();
|
inner.path.reset();
|
||||||
inner.head = head;
|
inner.head = head;
|
||||||
inner.payload = payload;
|
inner.payload = payload;
|
||||||
inner.app_data.push(self.data.clone());
|
inner.app_data = tiny_vec![self.data.clone()];
|
||||||
req
|
req
|
||||||
} else {
|
} else {
|
||||||
HttpRequest::new(
|
HttpRequest::new(
|
||||||
@ -474,7 +475,7 @@ mod tests {
|
|||||||
let mut app = init_service(
|
let mut app = init_service(
|
||||||
App::new()
|
App::new()
|
||||||
.data(DropData(data.clone()))
|
.data(DropData(data.clone()))
|
||||||
.service(web::resource("/test").to(|| HttpResponse::Ok())),
|
.service(web::resource("/test").to(HttpResponse::Ok)),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
let req = TestRequest::with_uri("/test").to_request();
|
let req = TestRequest::with_uri("/test").to_request();
|
||||||
|
@ -311,10 +311,9 @@ mod tests {
|
|||||||
.route(
|
.route(
|
||||||
"/test",
|
"/test",
|
||||||
web::get().to(|req: HttpRequest| {
|
web::get().to(|req: HttpRequest| {
|
||||||
HttpResponse::Ok().body(format!(
|
HttpResponse::Ok().body(
|
||||||
"{}",
|
req.url_for("youtube", &["12345"]).unwrap().to_string(),
|
||||||
req.url_for("youtube", &["12345"]).unwrap()
|
)
|
||||||
))
|
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@ -330,9 +329,9 @@ mod tests {
|
|||||||
async fn test_service() {
|
async fn test_service() {
|
||||||
let mut srv = init_service(App::new().configure(|cfg| {
|
let mut srv = init_service(App::new().configure(|cfg| {
|
||||||
cfg.service(
|
cfg.service(
|
||||||
web::resource("/test").route(web::get().to(|| HttpResponse::Created())),
|
web::resource("/test").route(web::get().to(HttpResponse::Created)),
|
||||||
)
|
)
|
||||||
.route("/index.html", web::get().to(|| HttpResponse::Ok()));
|
.route("/index.html", web::get().to(HttpResponse::Ok));
|
||||||
}))
|
}))
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
15
src/data.rs
15
src/data.rs
@ -200,13 +200,13 @@ mod tests {
|
|||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_route_data_extractor() {
|
async fn test_route_data_extractor() {
|
||||||
let mut srv =
|
let mut srv = init_service(
|
||||||
init_service(App::new().service(web::resource("/").data(10usize).route(
|
App::new().service(
|
||||||
web::get().to(|data: web::Data<usize>| {
|
web::resource("/")
|
||||||
let _ = data.clone();
|
.data(10usize)
|
||||||
HttpResponse::Ok()
|
.route(web::get().to(|_data: web::Data<usize>| HttpResponse::Ok())),
|
||||||
}),
|
),
|
||||||
)))
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let req = TestRequest::default().to_request();
|
let req = TestRequest::default().to_request();
|
||||||
@ -233,7 +233,6 @@ mod tests {
|
|||||||
web::resource("/").data(10usize).route(web::get().to(
|
web::resource("/").data(10usize).route(web::get().to(
|
||||||
|data: web::Data<usize>| {
|
|data: web::Data<usize>| {
|
||||||
assert_eq!(**data, 10);
|
assert_eq!(**data, 10);
|
||||||
let _ = data.clone();
|
|
||||||
HttpResponse::Ok()
|
HttpResponse::Ok()
|
||||||
},
|
},
|
||||||
)),
|
)),
|
||||||
|
@ -9,8 +9,8 @@
|
|||||||
//! use actix_web::{get, web, App, HttpServer, Responder};
|
//! use actix_web::{get, web, App, HttpServer, Responder};
|
||||||
//!
|
//!
|
||||||
//! #[get("/{id}/{name}/index.html")]
|
//! #[get("/{id}/{name}/index.html")]
|
||||||
//! async fn index(info: web::Path<(u32, String)>) -> impl Responder {
|
//! async fn index(web::Path((id, name)): web::Path<(u32, String)>) -> impl Responder {
|
||||||
//! format!("Hello {}! id:{}", info.1, info.0)
|
//! format!("Hello {}! id:{}", name, id)
|
||||||
//! }
|
//! }
|
||||||
//!
|
//!
|
||||||
//! #[actix_web::main]
|
//! #[actix_web::main]
|
||||||
@ -213,9 +213,7 @@ pub mod client {
|
|||||||
//! }
|
//! }
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
pub use awc::error::{
|
pub use awc::error::*;
|
||||||
ConnectError, InvalidUrl, PayloadError, SendRequestError, WsClientError,
|
|
||||||
};
|
|
||||||
pub use awc::{
|
pub use awc::{
|
||||||
test, Client, ClientBuilder, ClientRequest, ClientResponse, Connector,
|
test, Client, ClientBuilder, ClientRequest, ClientResponse, Connector,
|
||||||
};
|
};
|
||||||
|
@ -626,7 +626,7 @@ mod tests {
|
|||||||
Ok(())
|
Ok(())
|
||||||
};
|
};
|
||||||
let s = format!("{}", FormatDisplay(&render));
|
let s = format!("{}", FormatDisplay(&render));
|
||||||
assert!(s.contains(&format!("{}", now.format("%Y-%m-%dT%H:%M:%S"))));
|
assert!(s.contains(&now.format("%Y-%m-%dT%H:%M:%S")));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
|
@ -129,7 +129,7 @@ mod tests {
|
|||||||
let mut app = init_service(
|
let mut app = init_service(
|
||||||
App::new()
|
App::new()
|
||||||
.wrap(NormalizePath::default())
|
.wrap(NormalizePath::default())
|
||||||
.service(web::resource("/v1/something/").to(|| HttpResponse::Ok())),
|
.service(web::resource("/v1/something/").to(HttpResponse::Ok)),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
@ -276,6 +276,7 @@ impl HttpMessage for HttpRequest {
|
|||||||
|
|
||||||
impl Drop for HttpRequest {
|
impl Drop for HttpRequest {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
|
// if possible, contribute to current worker's HttpRequest allocation pool
|
||||||
if Rc::strong_count(&self.0) == 1 {
|
if Rc::strong_count(&self.0) == 1 {
|
||||||
let v = &mut self.0.pool.0.borrow_mut();
|
let v = &mut self.0.pool.0.borrow_mut();
|
||||||
if v.len() < 128 {
|
if v.len() < 128 {
|
||||||
@ -340,25 +341,32 @@ impl fmt::Debug for HttpRequest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Request's objects pool
|
/// Slab-allocated `HttpRequest` Pool
|
||||||
|
///
|
||||||
|
/// Since request processing may yield for asynchronous events to complete, a worker may have many
|
||||||
|
/// requests in-flight at any time. Pooling requests like this amortizes the performance and memory
|
||||||
|
/// costs of allocating and de-allocating HttpRequest objects as frequently as they otherwise would.
|
||||||
|
///
|
||||||
|
/// Request objects are added when they are dropped (see `<HttpRequest as Drop>::drop`) and re-used
|
||||||
|
/// in `<AppInitService as Service>::call` when there are available objects in the list.
|
||||||
|
///
|
||||||
|
/// The pool's initial capacity is 128 items.
|
||||||
pub(crate) struct HttpRequestPool(RefCell<Vec<Rc<HttpRequestInner>>>);
|
pub(crate) struct HttpRequestPool(RefCell<Vec<Rc<HttpRequestInner>>>);
|
||||||
|
|
||||||
impl HttpRequestPool {
|
impl HttpRequestPool {
|
||||||
|
/// Allocates a slab of memory for pool use.
|
||||||
pub(crate) fn create() -> &'static HttpRequestPool {
|
pub(crate) fn create() -> &'static HttpRequestPool {
|
||||||
let pool = HttpRequestPool(RefCell::new(Vec::with_capacity(128)));
|
let pool = HttpRequestPool(RefCell::new(Vec::with_capacity(128)));
|
||||||
Box::leak(Box::new(pool))
|
Box::leak(Box::new(pool))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get message from the pool
|
/// Re-use a previously allocated (but now completed/discarded) HttpRequest object.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn get_request(&self) -> Option<HttpRequest> {
|
pub(crate) fn get_request(&self) -> Option<HttpRequest> {
|
||||||
if let Some(inner) = self.0.borrow_mut().pop() {
|
self.0.borrow_mut().pop().map(HttpRequest)
|
||||||
Some(HttpRequest(inner))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Clears all allocated HttpRequest objects.
|
||||||
pub(crate) fn clear(&self) {
|
pub(crate) fn clear(&self) {
|
||||||
self.0.borrow_mut().clear()
|
self.0.borrow_mut().clear()
|
||||||
}
|
}
|
||||||
|
@ -607,7 +607,7 @@ mod tests {
|
|||||||
header::CONTENT_TYPE,
|
header::CONTENT_TYPE,
|
||||||
HeaderValue::from_static("0001"),
|
HeaderValue::from_static("0001"),
|
||||||
))
|
))
|
||||||
.route(web::get().to(|| HttpResponse::Ok())),
|
.route(web::get().to(HttpResponse::Ok)),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
@ -637,7 +637,7 @@ mod tests {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.route(web::get().to(|| HttpResponse::Ok())),
|
.route(web::get().to(HttpResponse::Ok)),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
@ -684,9 +684,7 @@ mod tests {
|
|||||||
async fn test_default_resource() {
|
async fn test_default_resource() {
|
||||||
let mut srv = init_service(
|
let mut srv = init_service(
|
||||||
App::new()
|
App::new()
|
||||||
.service(
|
.service(web::resource("/test").route(web::get().to(HttpResponse::Ok)))
|
||||||
web::resource("/test").route(web::get().to(|| HttpResponse::Ok())),
|
|
||||||
)
|
|
||||||
.default_service(|r: ServiceRequest| {
|
.default_service(|r: ServiceRequest| {
|
||||||
ok(r.into_response(HttpResponse::BadRequest()))
|
ok(r.into_response(HttpResponse::BadRequest()))
|
||||||
}),
|
}),
|
||||||
@ -705,7 +703,7 @@ mod tests {
|
|||||||
let mut srv = init_service(
|
let mut srv = init_service(
|
||||||
App::new().service(
|
App::new().service(
|
||||||
web::resource("/test")
|
web::resource("/test")
|
||||||
.route(web::get().to(|| HttpResponse::Ok()))
|
.route(web::get().to(HttpResponse::Ok))
|
||||||
.default_service(|r: ServiceRequest| {
|
.default_service(|r: ServiceRequest| {
|
||||||
ok(r.into_response(HttpResponse::BadRequest()))
|
ok(r.into_response(HttpResponse::BadRequest()))
|
||||||
}),
|
}),
|
||||||
@ -731,17 +729,17 @@ mod tests {
|
|||||||
.service(
|
.service(
|
||||||
web::resource("/test/{p}")
|
web::resource("/test/{p}")
|
||||||
.guard(guard::Get())
|
.guard(guard::Get())
|
||||||
.to(|| HttpResponse::Ok()),
|
.to(HttpResponse::Ok),
|
||||||
)
|
)
|
||||||
.service(
|
.service(
|
||||||
web::resource("/test/{p}")
|
web::resource("/test/{p}")
|
||||||
.guard(guard::Put())
|
.guard(guard::Put())
|
||||||
.to(|| HttpResponse::Created()),
|
.to(HttpResponse::Created),
|
||||||
)
|
)
|
||||||
.service(
|
.service(
|
||||||
web::resource("/test/{p}")
|
web::resource("/test/{p}")
|
||||||
.guard(guard::Delete())
|
.guard(guard::Delete())
|
||||||
.to(|| HttpResponse::NoContent()),
|
.to(HttpResponse::NoContent),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
@ -783,7 +781,8 @@ mod tests {
|
|||||||
data3: web::Data<f64>| {
|
data3: web::Data<f64>| {
|
||||||
assert_eq!(**data1, 10);
|
assert_eq!(**data1, 10);
|
||||||
assert_eq!(**data2, '*');
|
assert_eq!(**data2, '*');
|
||||||
assert_eq!(**data3, 1.0);
|
let error = std::f64::EPSILON;
|
||||||
|
assert!((**data3 - 1.0).abs() < error);
|
||||||
HttpResponse::Ok()
|
HttpResponse::Ok()
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -480,7 +480,7 @@ pub(crate) mod tests {
|
|||||||
assert_eq!(resp.status(), StatusCode::OK);
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
match resp.response().body() {
|
match resp.response().body() {
|
||||||
ResponseBody::Body(Body::Bytes(ref b)) => {
|
ResponseBody::Body(Body::Bytes(ref b)) => {
|
||||||
let bytes: Bytes = b.clone().into();
|
let bytes = b.clone();
|
||||||
assert_eq!(bytes, Bytes::from_static(b"some"));
|
assert_eq!(bytes, Bytes::from_static(b"some"));
|
||||||
}
|
}
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
|
@ -362,7 +362,7 @@ mod tests {
|
|||||||
App::new()
|
App::new()
|
||||||
.service(
|
.service(
|
||||||
web::resource("/test")
|
web::resource("/test")
|
||||||
.route(web::get().to(|| HttpResponse::Ok()))
|
.route(web::get().to(HttpResponse::Ok))
|
||||||
.route(web::put().to(|| async {
|
.route(web::put().to(|| async {
|
||||||
Err::<HttpResponse, _>(error::ErrorBadRequest("err"))
|
Err::<HttpResponse, _>(error::ErrorBadRequest("err"))
|
||||||
}))
|
}))
|
||||||
|
97
src/scope.rs
97
src/scope.rs
@ -678,12 +678,9 @@ mod tests {
|
|||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_scope() {
|
async fn test_scope() {
|
||||||
let mut srv = init_service(
|
let mut srv = init_service(App::new().service(
|
||||||
App::new().service(
|
web::scope("/app").service(web::resource("/path1").to(HttpResponse::Ok)),
|
||||||
web::scope("/app")
|
))
|
||||||
.service(web::resource("/path1").to(|| HttpResponse::Ok())),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let req = TestRequest::with_uri("/app/path1").to_request();
|
let req = TestRequest::with_uri("/app/path1").to_request();
|
||||||
@ -696,8 +693,8 @@ mod tests {
|
|||||||
let mut srv = init_service(
|
let mut srv = init_service(
|
||||||
App::new().service(
|
App::new().service(
|
||||||
web::scope("/app")
|
web::scope("/app")
|
||||||
.service(web::resource("").to(|| HttpResponse::Ok()))
|
.service(web::resource("").to(HttpResponse::Ok))
|
||||||
.service(web::resource("/").to(|| HttpResponse::Created())),
|
.service(web::resource("/").to(HttpResponse::Created)),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
@ -714,7 +711,7 @@ mod tests {
|
|||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_scope_root2() {
|
async fn test_scope_root2() {
|
||||||
let mut srv = init_service(App::new().service(
|
let mut srv = init_service(App::new().service(
|
||||||
web::scope("/app/").service(web::resource("").to(|| HttpResponse::Ok())),
|
web::scope("/app/").service(web::resource("").to(HttpResponse::Ok)),
|
||||||
))
|
))
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
@ -730,7 +727,7 @@ mod tests {
|
|||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_scope_root3() {
|
async fn test_scope_root3() {
|
||||||
let mut srv = init_service(App::new().service(
|
let mut srv = init_service(App::new().service(
|
||||||
web::scope("/app/").service(web::resource("/").to(|| HttpResponse::Ok())),
|
web::scope("/app/").service(web::resource("/").to(HttpResponse::Ok)),
|
||||||
))
|
))
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
@ -748,8 +745,8 @@ mod tests {
|
|||||||
let mut srv = init_service(
|
let mut srv = init_service(
|
||||||
App::new().service(
|
App::new().service(
|
||||||
web::scope("app")
|
web::scope("app")
|
||||||
.route("/path1", web::get().to(|| HttpResponse::Ok()))
|
.route("/path1", web::get().to(HttpResponse::Ok))
|
||||||
.route("/path1", web::delete().to(|| HttpResponse::Ok())),
|
.route("/path1", web::delete().to(HttpResponse::Ok)),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
@ -777,8 +774,8 @@ mod tests {
|
|||||||
App::new().service(
|
App::new().service(
|
||||||
web::scope("app").service(
|
web::scope("app").service(
|
||||||
web::resource("path1")
|
web::resource("path1")
|
||||||
.route(web::get().to(|| HttpResponse::Ok()))
|
.route(web::get().to(HttpResponse::Ok))
|
||||||
.route(web::delete().to(|| HttpResponse::Ok())),
|
.route(web::delete().to(HttpResponse::Ok)),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@ -807,7 +804,7 @@ mod tests {
|
|||||||
App::new().service(
|
App::new().service(
|
||||||
web::scope("/app")
|
web::scope("/app")
|
||||||
.guard(guard::Get())
|
.guard(guard::Get())
|
||||||
.service(web::resource("/path1").to(|| HttpResponse::Ok())),
|
.service(web::resource("/path1").to(HttpResponse::Ok)),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
@ -842,7 +839,7 @@ mod tests {
|
|||||||
|
|
||||||
match resp.response().body() {
|
match resp.response().body() {
|
||||||
ResponseBody::Body(Body::Bytes(ref b)) => {
|
ResponseBody::Body(Body::Bytes(ref b)) => {
|
||||||
let bytes: Bytes = b.clone().into();
|
let bytes = b.clone();
|
||||||
assert_eq!(bytes, Bytes::from_static(b"project: project1"));
|
assert_eq!(bytes, Bytes::from_static(b"project: project1"));
|
||||||
}
|
}
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
@ -855,14 +852,9 @@ mod tests {
|
|||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_nested_scope() {
|
async fn test_nested_scope() {
|
||||||
let mut srv = init_service(
|
let mut srv = init_service(App::new().service(web::scope("/app").service(
|
||||||
App::new().service(
|
web::scope("/t1").service(web::resource("/path1").to(HttpResponse::Created)),
|
||||||
web::scope("/app")
|
)))
|
||||||
.service(web::scope("/t1").service(
|
|
||||||
web::resource("/path1").to(|| HttpResponse::Created()),
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let req = TestRequest::with_uri("/app/t1/path1").to_request();
|
let req = TestRequest::with_uri("/app/t1/path1").to_request();
|
||||||
@ -872,14 +864,9 @@ mod tests {
|
|||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_nested_scope_no_slash() {
|
async fn test_nested_scope_no_slash() {
|
||||||
let mut srv = init_service(
|
let mut srv = init_service(App::new().service(web::scope("/app").service(
|
||||||
App::new().service(
|
web::scope("t1").service(web::resource("/path1").to(HttpResponse::Created)),
|
||||||
web::scope("/app")
|
)))
|
||||||
.service(web::scope("t1").service(
|
|
||||||
web::resource("/path1").to(|| HttpResponse::Created()),
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let req = TestRequest::with_uri("/app/t1/path1").to_request();
|
let req = TestRequest::with_uri("/app/t1/path1").to_request();
|
||||||
@ -893,8 +880,8 @@ mod tests {
|
|||||||
App::new().service(
|
App::new().service(
|
||||||
web::scope("/app").service(
|
web::scope("/app").service(
|
||||||
web::scope("/t1")
|
web::scope("/t1")
|
||||||
.service(web::resource("").to(|| HttpResponse::Ok()))
|
.service(web::resource("").to(HttpResponse::Ok))
|
||||||
.service(web::resource("/").to(|| HttpResponse::Created())),
|
.service(web::resource("/").to(HttpResponse::Created)),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@ -916,7 +903,7 @@ mod tests {
|
|||||||
web::scope("/app").service(
|
web::scope("/app").service(
|
||||||
web::scope("/t1")
|
web::scope("/t1")
|
||||||
.guard(guard::Get())
|
.guard(guard::Get())
|
||||||
.service(web::resource("/path1").to(|| HttpResponse::Ok())),
|
.service(web::resource("/path1").to(HttpResponse::Ok)),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@ -953,7 +940,7 @@ mod tests {
|
|||||||
|
|
||||||
match resp.response().body() {
|
match resp.response().body() {
|
||||||
ResponseBody::Body(Body::Bytes(ref b)) => {
|
ResponseBody::Body(Body::Bytes(ref b)) => {
|
||||||
let bytes: Bytes = b.clone().into();
|
let bytes = b.clone();
|
||||||
assert_eq!(bytes, Bytes::from_static(b"project: project_1"));
|
assert_eq!(bytes, Bytes::from_static(b"project: project_1"));
|
||||||
}
|
}
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
@ -981,7 +968,7 @@ mod tests {
|
|||||||
|
|
||||||
match resp.response().body() {
|
match resp.response().body() {
|
||||||
ResponseBody::Body(Body::Bytes(ref b)) => {
|
ResponseBody::Body(Body::Bytes(ref b)) => {
|
||||||
let bytes: Bytes = b.clone().into();
|
let bytes = b.clone();
|
||||||
assert_eq!(bytes, Bytes::from_static(b"project: test - 1"));
|
assert_eq!(bytes, Bytes::from_static(b"project: test - 1"));
|
||||||
}
|
}
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
@ -997,7 +984,7 @@ mod tests {
|
|||||||
let mut srv = init_service(
|
let mut srv = init_service(
|
||||||
App::new().service(
|
App::new().service(
|
||||||
web::scope("/app")
|
web::scope("/app")
|
||||||
.service(web::resource("/path1").to(|| HttpResponse::Ok()))
|
.service(web::resource("/path1").to(HttpResponse::Ok))
|
||||||
.default_service(|r: ServiceRequest| {
|
.default_service(|r: ServiceRequest| {
|
||||||
ok(r.into_response(HttpResponse::BadRequest()))
|
ok(r.into_response(HttpResponse::BadRequest()))
|
||||||
}),
|
}),
|
||||||
@ -1018,9 +1005,10 @@ mod tests {
|
|||||||
async fn test_default_resource_propagation() {
|
async fn test_default_resource_propagation() {
|
||||||
let mut srv = init_service(
|
let mut srv = init_service(
|
||||||
App::new()
|
App::new()
|
||||||
.service(web::scope("/app1").default_service(
|
.service(
|
||||||
web::resource("").to(|| HttpResponse::BadRequest()),
|
web::scope("/app1")
|
||||||
))
|
.default_service(web::resource("").to(HttpResponse::BadRequest)),
|
||||||
|
)
|
||||||
.service(web::scope("/app2"))
|
.service(web::scope("/app2"))
|
||||||
.default_service(|r: ServiceRequest| {
|
.default_service(|r: ServiceRequest| {
|
||||||
ok(r.into_response(HttpResponse::MethodNotAllowed()))
|
ok(r.into_response(HttpResponse::MethodNotAllowed()))
|
||||||
@ -1043,17 +1031,17 @@ mod tests {
|
|||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_middleware() {
|
async fn test_middleware() {
|
||||||
let mut srv =
|
let mut srv = init_service(
|
||||||
init_service(
|
|
||||||
App::new().service(
|
App::new().service(
|
||||||
web::scope("app")
|
web::scope("app")
|
||||||
.wrap(DefaultHeaders::new().header(
|
.wrap(
|
||||||
|
DefaultHeaders::new().header(
|
||||||
header::CONTENT_TYPE,
|
header::CONTENT_TYPE,
|
||||||
HeaderValue::from_static("0001"),
|
HeaderValue::from_static("0001"),
|
||||||
))
|
),
|
||||||
|
)
|
||||||
.service(
|
.service(
|
||||||
web::resource("/test")
|
web::resource("/test").route(web::get().to(HttpResponse::Ok)),
|
||||||
.route(web::get().to(|| HttpResponse::Ok())),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@ -1084,7 +1072,7 @@ mod tests {
|
|||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.route("/test", web::get().to(|| HttpResponse::Ok())),
|
.route("/test", web::get().to(HttpResponse::Ok)),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
@ -1105,7 +1093,6 @@ mod tests {
|
|||||||
"/t",
|
"/t",
|
||||||
web::get().to(|data: web::Data<usize>| {
|
web::get().to(|data: web::Data<usize>| {
|
||||||
assert_eq!(**data, 10);
|
assert_eq!(**data, 10);
|
||||||
let _ = data.clone();
|
|
||||||
HttpResponse::Ok()
|
HttpResponse::Ok()
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
@ -1141,7 +1128,6 @@ mod tests {
|
|||||||
"/t",
|
"/t",
|
||||||
web::get().to(|data: web::Data<usize>| {
|
web::get().to(|data: web::Data<usize>| {
|
||||||
assert_eq!(**data, 10);
|
assert_eq!(**data, 10);
|
||||||
let _ = data.clone();
|
|
||||||
HttpResponse::Ok()
|
HttpResponse::Ok()
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
@ -1157,7 +1143,7 @@ mod tests {
|
|||||||
async fn test_scope_config() {
|
async fn test_scope_config() {
|
||||||
let mut srv =
|
let mut srv =
|
||||||
init_service(App::new().service(web::scope("/app").configure(|s| {
|
init_service(App::new().service(web::scope("/app").configure(|s| {
|
||||||
s.route("/path1", web::get().to(|| HttpResponse::Ok()));
|
s.route("/path1", web::get().to(HttpResponse::Ok));
|
||||||
})))
|
})))
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
@ -1171,7 +1157,7 @@ mod tests {
|
|||||||
let mut srv =
|
let mut srv =
|
||||||
init_service(App::new().service(web::scope("/app").configure(|s| {
|
init_service(App::new().service(web::scope("/app").configure(|s| {
|
||||||
s.service(web::scope("/v1").configure(|s| {
|
s.service(web::scope("/v1").configure(|s| {
|
||||||
s.route("/", web::get().to(|| HttpResponse::Ok()));
|
s.route("/", web::get().to(HttpResponse::Ok));
|
||||||
}));
|
}));
|
||||||
})))
|
})))
|
||||||
.await;
|
.await;
|
||||||
@ -1193,10 +1179,9 @@ mod tests {
|
|||||||
s.route(
|
s.route(
|
||||||
"/",
|
"/",
|
||||||
web::get().to(|req: HttpRequest| async move {
|
web::get().to(|req: HttpRequest| async move {
|
||||||
HttpResponse::Ok().body(format!(
|
HttpResponse::Ok().body(
|
||||||
"{}",
|
req.url_for("youtube", &["xxxxxx"]).unwrap().to_string(),
|
||||||
req.url_for("youtube", &["xxxxxx"]).unwrap().as_str()
|
)
|
||||||
))
|
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}));
|
}));
|
||||||
|
@ -12,7 +12,6 @@ use actix_router::{IntoPattern, Path, Resource, ResourceDef, Url};
|
|||||||
use actix_service::{IntoServiceFactory, ServiceFactory};
|
use actix_service::{IntoServiceFactory, ServiceFactory};
|
||||||
|
|
||||||
use crate::config::{AppConfig, AppService};
|
use crate::config::{AppConfig, AppService};
|
||||||
use crate::data::Data;
|
|
||||||
use crate::dev::insert_slash;
|
use crate::dev::insert_slash;
|
||||||
use crate::guard::Guard;
|
use crate::guard::Guard;
|
||||||
use crate::info::ConnectionInfo;
|
use crate::info::ConnectionInfo;
|
||||||
@ -226,12 +225,11 @@ impl ServiceRequest {
|
|||||||
self.0.app_config()
|
self.0.app_config()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get an application data stored with `App::data()` method during
|
/// Counterpart to [`HttpRequest::app_data`](../struct.HttpRequest.html#method.app_data).
|
||||||
/// application configuration.
|
pub fn app_data<T: 'static>(&self) -> Option<&T> {
|
||||||
pub fn app_data<T: 'static>(&self) -> Option<Data<T>> {
|
|
||||||
for container in (self.0).0.app_data.iter().rev() {
|
for container in (self.0).0.app_data.iter().rev() {
|
||||||
if let Some(data) = container.get::<Data<T>>() {
|
if let Some(data) = container.get::<T>() {
|
||||||
return Some(Data::clone(&data));
|
return Some(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -595,6 +593,28 @@ mod tests {
|
|||||||
let resp = srv.call(req).await.unwrap();
|
let resp = srv.call(req).await.unwrap();
|
||||||
assert_eq!(resp.status(), http::StatusCode::NOT_FOUND);
|
assert_eq!(resp.status(), http::StatusCode::NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn test_service_data() {
|
||||||
|
let mut srv = init_service(
|
||||||
|
App::new()
|
||||||
|
.data(42u32)
|
||||||
|
.service(web::service("/test").name("test").finish(
|
||||||
|
|req: ServiceRequest| {
|
||||||
|
assert_eq!(
|
||||||
|
req.app_data::<web::Data<u32>>().unwrap().as_ref(),
|
||||||
|
&42
|
||||||
|
);
|
||||||
|
ok(req.into_response(HttpResponse::Ok().finish()))
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
let req = TestRequest::with_uri("/test").to_request();
|
||||||
|
let resp = srv.call(req).await.unwrap();
|
||||||
|
assert_eq!(resp.status(), http::StatusCode::OK);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_fmt_debug() {
|
fn test_fmt_debug() {
|
||||||
let req = TestRequest::get()
|
let req = TestRequest::get()
|
||||||
|
@ -23,7 +23,6 @@ use futures_util::future::ok;
|
|||||||
use futures_util::StreamExt;
|
use futures_util::StreamExt;
|
||||||
use serde::de::DeserializeOwned;
|
use serde::de::DeserializeOwned;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use serde_json;
|
|
||||||
use socket2::{Domain, Protocol, Socket, Type};
|
use socket2::{Domain, Protocol, Socket, Type};
|
||||||
|
|
||||||
pub use actix_http::test::TestBuffer;
|
pub use actix_http::test::TestBuffer;
|
||||||
|
@ -407,18 +407,15 @@ mod tests {
|
|||||||
|
|
||||||
fn eq(err: UrlencodedError, other: UrlencodedError) -> bool {
|
fn eq(err: UrlencodedError, other: UrlencodedError) -> bool {
|
||||||
match err {
|
match err {
|
||||||
UrlencodedError::Overflow { .. } => match other {
|
UrlencodedError::Overflow { .. } => {
|
||||||
UrlencodedError::Overflow { .. } => true,
|
matches!(other, UrlencodedError::Overflow { .. })
|
||||||
_ => false,
|
}
|
||||||
},
|
UrlencodedError::UnknownLength => {
|
||||||
UrlencodedError::UnknownLength => match other {
|
matches!(other, UrlencodedError::UnknownLength)
|
||||||
UrlencodedError::UnknownLength => true,
|
}
|
||||||
_ => false,
|
UrlencodedError::ContentType => {
|
||||||
},
|
matches!(other, UrlencodedError::ContentType)
|
||||||
UrlencodedError::ContentType => match other {
|
}
|
||||||
UrlencodedError::ContentType => true,
|
|
||||||
_ => false,
|
|
||||||
},
|
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -433,14 +433,10 @@ mod tests {
|
|||||||
|
|
||||||
fn json_eq(err: JsonPayloadError, other: JsonPayloadError) -> bool {
|
fn json_eq(err: JsonPayloadError, other: JsonPayloadError) -> bool {
|
||||||
match err {
|
match err {
|
||||||
JsonPayloadError::Overflow => match other {
|
JsonPayloadError::Overflow => matches!(other, JsonPayloadError::Overflow),
|
||||||
JsonPayloadError::Overflow => true,
|
JsonPayloadError::ContentType => {
|
||||||
_ => false,
|
matches!(other, JsonPayloadError::ContentType)
|
||||||
},
|
}
|
||||||
JsonPayloadError::ContentType => match other {
|
|
||||||
JsonPayloadError::ContentType => true,
|
|
||||||
_ => false,
|
|
||||||
},
|
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -486,7 +482,7 @@ mod tests {
|
|||||||
.to_http_parts();
|
.to_http_parts();
|
||||||
|
|
||||||
let s = Json::<MyObject>::from_request(&req, &mut pl).await;
|
let s = Json::<MyObject>::from_request(&req, &mut pl).await;
|
||||||
let mut resp = Response::from_error(s.err().unwrap().into());
|
let mut resp = Response::from_error(s.err().unwrap());
|
||||||
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
|
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
|
||||||
|
|
||||||
let body = load_stream(resp.take_body()).await.unwrap();
|
let body = load_stream(resp.take_body()).await.unwrap();
|
||||||
|
@ -25,8 +25,8 @@ use crate::FromRequest;
|
|||||||
/// /// extract path info from "/{username}/{count}/index.html" url
|
/// /// extract path info from "/{username}/{count}/index.html" url
|
||||||
/// /// {username} - deserializes to a String
|
/// /// {username} - deserializes to a String
|
||||||
/// /// {count} - - deserializes to a u32
|
/// /// {count} - - deserializes to a u32
|
||||||
/// async fn index(info: web::Path<(String, u32)>) -> String {
|
/// async fn index(web::Path((username, count)): web::Path<(String, u32)>) -> String {
|
||||||
/// format!("Welcome {}! {}", info.0, info.1)
|
/// format!("Welcome {}! {}", username, count)
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// fn main() {
|
/// fn main() {
|
||||||
@ -61,20 +61,18 @@ use crate::FromRequest;
|
|||||||
/// );
|
/// );
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub struct Path<T> {
|
pub struct Path<T>(pub T);
|
||||||
inner: T,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Path<T> {
|
impl<T> Path<T> {
|
||||||
/// Deconstruct to an inner value
|
/// Deconstruct to an inner value
|
||||||
pub fn into_inner(self) -> T {
|
pub fn into_inner(self) -> T {
|
||||||
self.inner
|
self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> AsRef<T> for Path<T> {
|
impl<T> AsRef<T> for Path<T> {
|
||||||
fn as_ref(&self) -> &T {
|
fn as_ref(&self) -> &T {
|
||||||
&self.inner
|
&self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,31 +80,31 @@ impl<T> ops::Deref for Path<T> {
|
|||||||
type Target = T;
|
type Target = T;
|
||||||
|
|
||||||
fn deref(&self) -> &T {
|
fn deref(&self) -> &T {
|
||||||
&self.inner
|
&self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> ops::DerefMut for Path<T> {
|
impl<T> ops::DerefMut for Path<T> {
|
||||||
fn deref_mut(&mut self) -> &mut T {
|
fn deref_mut(&mut self) -> &mut T {
|
||||||
&mut self.inner
|
&mut self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> From<T> for Path<T> {
|
impl<T> From<T> for Path<T> {
|
||||||
fn from(inner: T) -> Path<T> {
|
fn from(inner: T) -> Path<T> {
|
||||||
Path { inner }
|
Path(inner)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: fmt::Debug> fmt::Debug for Path<T> {
|
impl<T: fmt::Debug> fmt::Debug for Path<T> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
self.inner.fmt(f)
|
self.0.fmt(f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: fmt::Display> fmt::Display for Path<T> {
|
impl<T: fmt::Display> fmt::Display for Path<T> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
self.inner.fmt(f)
|
self.0.fmt(f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,8 +118,8 @@ impl<T: fmt::Display> fmt::Display for Path<T> {
|
|||||||
/// /// extract path info from "/{username}/{count}/index.html" url
|
/// /// extract path info from "/{username}/{count}/index.html" url
|
||||||
/// /// {username} - deserializes to a String
|
/// /// {username} - deserializes to a String
|
||||||
/// /// {count} - - deserializes to a u32
|
/// /// {count} - - deserializes to a u32
|
||||||
/// async fn index(info: web::Path<(String, u32)>) -> String {
|
/// async fn index(web::Path((username, count)): web::Path<(String, u32)>) -> String {
|
||||||
/// format!("Welcome {}! {}", info.0, info.1)
|
/// format!("Welcome {}! {}", username, count)
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// fn main() {
|
/// fn main() {
|
||||||
@ -173,7 +171,7 @@ where
|
|||||||
|
|
||||||
ready(
|
ready(
|
||||||
de::Deserialize::deserialize(PathDeserializer::new(req.match_info()))
|
de::Deserialize::deserialize(PathDeserializer::new(req.match_info()))
|
||||||
.map(|inner| Path { inner })
|
.map(Path)
|
||||||
.map_err(move |e| {
|
.map_err(move |e| {
|
||||||
log::debug!(
|
log::debug!(
|
||||||
"Failed during Path extractor deserialization. \
|
"Failed during Path extractor deserialization. \
|
||||||
@ -290,21 +288,22 @@ mod tests {
|
|||||||
resource.match_path(req.match_info_mut());
|
resource.match_path(req.match_info_mut());
|
||||||
|
|
||||||
let (req, mut pl) = req.into_parts();
|
let (req, mut pl) = req.into_parts();
|
||||||
let res = <(Path<(String, String)>,)>::from_request(&req, &mut pl)
|
let (Path(res),) = <(Path<(String, String)>,)>::from_request(&req, &mut pl)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!((res.0).0, "name");
|
assert_eq!(res.0, "name");
|
||||||
assert_eq!((res.0).1, "user1");
|
assert_eq!(res.1, "user1");
|
||||||
|
|
||||||
let res = <(Path<(String, String)>, Path<(String, String)>)>::from_request(
|
let (Path(a), Path(b)) =
|
||||||
|
<(Path<(String, String)>, Path<(String, String)>)>::from_request(
|
||||||
&req, &mut pl,
|
&req, &mut pl,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!((res.0).0, "name");
|
assert_eq!(a.0, "name");
|
||||||
assert_eq!((res.0).1, "user1");
|
assert_eq!(a.1, "user1");
|
||||||
assert_eq!((res.1).0, "name");
|
assert_eq!(b.0, "name");
|
||||||
assert_eq!((res.1).1, "user1");
|
assert_eq!(b.1, "user1");
|
||||||
|
|
||||||
let () = <()>::from_request(&req, &mut pl).await.unwrap();
|
let () = <()>::from_request(&req, &mut pl).await.unwrap();
|
||||||
}
|
}
|
||||||
@ -329,7 +328,7 @@ mod tests {
|
|||||||
let s = s.into_inner();
|
let s = s.into_inner();
|
||||||
assert_eq!(s.value, "user2");
|
assert_eq!(s.value, "user2");
|
||||||
|
|
||||||
let s = Path::<(String, String)>::from_request(&req, &mut pl)
|
let Path(s) = Path::<(String, String)>::from_request(&req, &mut pl)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(s.0, "name");
|
assert_eq!(s.0, "name");
|
||||||
@ -344,7 +343,7 @@ mod tests {
|
|||||||
assert_eq!(s.as_ref().key, "name");
|
assert_eq!(s.as_ref().key, "name");
|
||||||
assert_eq!(s.value, 32);
|
assert_eq!(s.value, 32);
|
||||||
|
|
||||||
let s = Path::<(String, u8)>::from_request(&req, &mut pl)
|
let Path(s) = Path::<(String, u8)>::from_request(&req, &mut pl)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(s.0, "name");
|
assert_eq!(s.0, "name");
|
||||||
|
@ -13,10 +13,10 @@ use futures_util::future::{err, ok, Either, FutureExt, LocalBoxFuture, Ready};
|
|||||||
use futures_util::StreamExt;
|
use futures_util::StreamExt;
|
||||||
use mime::Mime;
|
use mime::Mime;
|
||||||
|
|
||||||
use crate::dev;
|
|
||||||
use crate::extract::FromRequest;
|
use crate::extract::FromRequest;
|
||||||
use crate::http::header;
|
use crate::http::header;
|
||||||
use crate::request::HttpRequest;
|
use crate::request::HttpRequest;
|
||||||
|
use crate::{dev, web};
|
||||||
|
|
||||||
/// Payload extractor returns request 's payload stream.
|
/// Payload extractor returns request 's payload stream.
|
||||||
///
|
///
|
||||||
@ -142,13 +142,8 @@ impl FromRequest for Bytes {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from_request(req: &HttpRequest, payload: &mut dev::Payload) -> Self::Future {
|
fn from_request(req: &HttpRequest, payload: &mut dev::Payload) -> Self::Future {
|
||||||
let tmp;
|
// allow both Config and Data<Config>
|
||||||
let cfg = if let Some(cfg) = req.app_data::<PayloadConfig>() {
|
let cfg = PayloadConfig::from_req(req);
|
||||||
cfg
|
|
||||||
} else {
|
|
||||||
tmp = PayloadConfig::default();
|
|
||||||
&tmp
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Err(e) = cfg.check_mimetype(req) {
|
if let Err(e) = cfg.check_mimetype(req) {
|
||||||
return Either::Right(err(e));
|
return Either::Right(err(e));
|
||||||
@ -197,13 +192,7 @@ impl FromRequest for String {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from_request(req: &HttpRequest, payload: &mut dev::Payload) -> Self::Future {
|
fn from_request(req: &HttpRequest, payload: &mut dev::Payload) -> Self::Future {
|
||||||
let tmp;
|
let cfg = PayloadConfig::from_req(req);
|
||||||
let cfg = if let Some(cfg) = req.app_data::<PayloadConfig>() {
|
|
||||||
cfg
|
|
||||||
} else {
|
|
||||||
tmp = PayloadConfig::default();
|
|
||||||
&tmp
|
|
||||||
};
|
|
||||||
|
|
||||||
// check content-type
|
// check content-type
|
||||||
if let Err(e) = cfg.check_mimetype(req) {
|
if let Err(e) = cfg.check_mimetype(req) {
|
||||||
@ -237,7 +226,12 @@ impl FromRequest for String {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Payload configuration for request's payload.
|
|
||||||
|
/// Configuration for request's payload.
|
||||||
|
///
|
||||||
|
/// Applies to the built-in `Bytes` and `String` extractors. Note that the Payload extractor does
|
||||||
|
/// not automatically check conformance with this configuration to allow more flexibility when
|
||||||
|
/// building extractors on top of `Payload`.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct PayloadConfig {
|
pub struct PayloadConfig {
|
||||||
limit: usize,
|
limit: usize,
|
||||||
@ -284,14 +278,28 @@ impl PayloadConfig {
|
|||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Allow payload config extraction from app data checking both `T` and `Data<T>`, in that
|
||||||
|
/// order, and falling back to the default payload config.
|
||||||
|
fn from_req(req: &HttpRequest) -> &PayloadConfig {
|
||||||
|
req.app_data::<PayloadConfig>()
|
||||||
|
.or_else(|| {
|
||||||
|
req.app_data::<web::Data<PayloadConfig>>()
|
||||||
|
.map(|d| d.as_ref())
|
||||||
|
})
|
||||||
|
.unwrap_or_else(|| &DEFAULT_PAYLOAD_CONFIG)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow shared refs to default.
|
||||||
|
static DEFAULT_PAYLOAD_CONFIG: PayloadConfig = PayloadConfig {
|
||||||
|
limit: 262_144, // 2^18 bytes (~256kB)
|
||||||
|
mimetype: None,
|
||||||
|
};
|
||||||
|
|
||||||
impl Default for PayloadConfig {
|
impl Default for PayloadConfig {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
PayloadConfig {
|
DEFAULT_PAYLOAD_CONFIG.clone()
|
||||||
limit: 262_144,
|
|
||||||
mimetype: None,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -407,8 +415,9 @@ mod tests {
|
|||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::http::header;
|
use crate::http::{header, StatusCode};
|
||||||
use crate::test::TestRequest;
|
use crate::test::{call_service, init_service, TestRequest};
|
||||||
|
use crate::{web, App, Responder};
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_payload_config() {
|
async fn test_payload_config() {
|
||||||
@ -428,6 +437,86 @@ mod tests {
|
|||||||
assert!(cfg.check_mimetype(&req).is_ok());
|
assert!(cfg.check_mimetype(&req).is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn test_config_recall_locations() {
|
||||||
|
async fn bytes_handler(_: Bytes) -> impl Responder {
|
||||||
|
"payload is probably json bytes"
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn string_handler(_: String) -> impl Responder {
|
||||||
|
"payload is probably json string"
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut srv = init_service(
|
||||||
|
App::new()
|
||||||
|
.service(
|
||||||
|
web::resource("/bytes-app-data")
|
||||||
|
.app_data(
|
||||||
|
PayloadConfig::default().mimetype(mime::APPLICATION_JSON),
|
||||||
|
)
|
||||||
|
.route(web::get().to(bytes_handler)),
|
||||||
|
)
|
||||||
|
.service(
|
||||||
|
web::resource("/bytes-data")
|
||||||
|
.data(PayloadConfig::default().mimetype(mime::APPLICATION_JSON))
|
||||||
|
.route(web::get().to(bytes_handler)),
|
||||||
|
)
|
||||||
|
.service(
|
||||||
|
web::resource("/string-app-data")
|
||||||
|
.app_data(
|
||||||
|
PayloadConfig::default().mimetype(mime::APPLICATION_JSON),
|
||||||
|
)
|
||||||
|
.route(web::get().to(string_handler)),
|
||||||
|
)
|
||||||
|
.service(
|
||||||
|
web::resource("/string-data")
|
||||||
|
.data(PayloadConfig::default().mimetype(mime::APPLICATION_JSON))
|
||||||
|
.route(web::get().to(string_handler)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let req = TestRequest::with_uri("/bytes-app-data").to_request();
|
||||||
|
let resp = call_service(&mut srv, req).await;
|
||||||
|
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
|
||||||
|
|
||||||
|
let req = TestRequest::with_uri("/bytes-data").to_request();
|
||||||
|
let resp = call_service(&mut srv, req).await;
|
||||||
|
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
|
||||||
|
|
||||||
|
let req = TestRequest::with_uri("/string-app-data").to_request();
|
||||||
|
let resp = call_service(&mut srv, req).await;
|
||||||
|
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
|
||||||
|
|
||||||
|
let req = TestRequest::with_uri("/string-data").to_request();
|
||||||
|
let resp = call_service(&mut srv, req).await;
|
||||||
|
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
|
||||||
|
|
||||||
|
let req = TestRequest::with_uri("/bytes-app-data")
|
||||||
|
.header(header::CONTENT_TYPE, mime::APPLICATION_JSON)
|
||||||
|
.to_request();
|
||||||
|
let resp = call_service(&mut srv, req).await;
|
||||||
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
|
|
||||||
|
let req = TestRequest::with_uri("/bytes-data")
|
||||||
|
.header(header::CONTENT_TYPE, mime::APPLICATION_JSON)
|
||||||
|
.to_request();
|
||||||
|
let resp = call_service(&mut srv, req).await;
|
||||||
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
|
|
||||||
|
let req = TestRequest::with_uri("/string-app-data")
|
||||||
|
.header(header::CONTENT_TYPE, mime::APPLICATION_JSON)
|
||||||
|
.to_request();
|
||||||
|
let resp = call_service(&mut srv, req).await;
|
||||||
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
|
|
||||||
|
let req = TestRequest::with_uri("/string-data")
|
||||||
|
.header(header::CONTENT_TYPE, mime::APPLICATION_JSON)
|
||||||
|
.to_request();
|
||||||
|
let resp = call_service(&mut srv, req).await;
|
||||||
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
|
}
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_bytes() {
|
async fn test_bytes() {
|
||||||
let (req, mut pl) = TestRequest::with_header(header::CONTENT_LENGTH, "11")
|
let (req, mut pl) = TestRequest::with_header(header::CONTENT_LENGTH, "11")
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
name = "actix-http-test"
|
name = "actix-http-test"
|
||||||
version = "2.0.0-alpha.1"
|
version = "2.0.0-alpha.1"
|
||||||
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
||||||
description = "Actix http test server"
|
description = "Actix HTTP test server"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
keywords = ["http", "web", "framework", "async", "futures"]
|
keywords = ["http", "web", "framework", "async", "futures"]
|
||||||
homepage = "https://actix.rs"
|
homepage = "https://actix.rs"
|
||||||
@ -11,7 +11,7 @@ documentation = "https://docs.rs/actix-http-test/"
|
|||||||
categories = ["network-programming", "asynchronous",
|
categories = ["network-programming", "asynchronous",
|
||||||
"web-programming::http-server",
|
"web-programming::http-server",
|
||||||
"web-programming::websocket"]
|
"web-programming::websocket"]
|
||||||
license = "MIT/Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
exclude = [".gitignore", ".cargo/config"]
|
exclude = [".gitignore", ".cargo/config"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
@ -53,4 +53,4 @@ open-ssl = { version = "0.10", package = "openssl", optional = true }
|
|||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
actix-web = "3.0.0-alpha.3"
|
actix-web = "3.0.0-alpha.3"
|
||||||
actix-http = "2.0.0-alpha.4"
|
actix-http = "2.0.0-beta.3"
|
||||||
|
@ -851,7 +851,7 @@ async fn test_slow_request() {
|
|||||||
use std::net;
|
use std::net;
|
||||||
|
|
||||||
let srv = test::start_with(test::config().client_timeout(200), || {
|
let srv = test::start_with(test::config().client_timeout(200), || {
|
||||||
App::new().service(web::resource("/").route(web::to(|| HttpResponse::Ok())))
|
App::new().service(web::resource("/").route(web::to(HttpResponse::Ok)))
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut stream = net::TcpStream::connect(srv.addr()).unwrap();
|
let mut stream = net::TcpStream::connect(srv.addr()).unwrap();
|
||||||
|
Reference in New Issue
Block a user