mirror of
https://github.com/fafhrd91/actix-web
synced 2025-07-04 18:06:23 +02:00
Compare commits
37 Commits
web-v3.0.0
...
http-v2.0.
Author | SHA1 | Date | |
---|---|---|---|
64a2c13cdf | |||
bf53fe5a22 | |||
cf5138e740 | |||
121075c1ef | |||
22089aff87 | |||
7787638f26 | |||
2f6e9738c4 | |||
e39d166a17 | |||
059d1671d7 | |||
3a27580ebe | |||
9d0534999d | |||
c54d73e0bb | |||
9a9d4b182e | |||
4e321595bc | |||
01cbef700f | |||
8497b5f490 | |||
75d86a6beb | |||
3892a95c11 | |||
5802eb797f | |||
ff2ca0f420 | |||
59ad1738e9 | |||
aa2bd6fbfb | |||
5aad8e24c7 | |||
6e97bc09f8 | |||
160995b8d4 | |||
187646b2f9 | |||
46627be36f | |||
a78380739e | |||
cf1c8abe62 | |||
92b5bcd13f | |||
701bdacfa2 | |||
6dc47c4093 | |||
0ec335a39c | |||
f8d5ad6b53 | |||
43c362779d | |||
971ba3eee1 | |||
2fd96c03e5 |
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
|
||||||
|
48
CHANGES.md
48
CHANGES.md
@ -3,6 +3,54 @@
|
|||||||
## Unreleased - 2020-xx-xx
|
## Unreleased - 2020-xx-xx
|
||||||
|
|
||||||
|
|
||||||
|
## 3.0.0 - 2020-09-11
|
||||||
|
* No significant changes from `3.0.0-beta.4`.
|
||||||
|
|
||||||
|
|
||||||
|
## 3.0.0-beta.4 - 2020-09-09
|
||||||
|
### Added
|
||||||
|
* `middleware::NormalizePath` now has configurable behaviour for either always having a trailing
|
||||||
|
slash, or as the new addition, always trimming trailing slashes. [#1639]
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
* Update actix-codec and actix-utils dependencies. [#1634]
|
||||||
|
* `FormConfig` and `JsonConfig` configurations are now also considered when set
|
||||||
|
using `App::data`. [#1641]
|
||||||
|
* `HttpServer::maxconn` is renamed to the more expressive `HttpServer::max_connections`. [#1655]
|
||||||
|
* `HttpServer::maxconnrate` is renamed to the more expressive
|
||||||
|
`HttpServer::max_connection_rate`. [#1655]
|
||||||
|
|
||||||
|
[#1639]: https://github.com/actix/actix-web/pull/1639
|
||||||
|
[#1641]: https://github.com/actix/actix-web/pull/1641
|
||||||
|
[#1634]: https://github.com/actix/actix-web/pull/1634
|
||||||
|
[#1655]: https://github.com/actix/actix-web/pull/1655
|
||||||
|
|
||||||
|
## 3.0.0-beta.3 - 2020-08-17
|
||||||
|
### Changed
|
||||||
|
* Update `rustls` to 0.18
|
||||||
|
|
||||||
|
|
||||||
|
## 3.0.0-beta.2 - 2020-08-17
|
||||||
|
### 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
|
## 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`.
|
||||||
|
27
Cargo.toml
27
Cargo.toml
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "actix-web"
|
name = "actix-web"
|
||||||
version = "3.0.0-beta.1"
|
version = "3.0.0"
|
||||||
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"
|
||||||
@ -65,20 +65,20 @@ name = "test_server"
|
|||||||
required-features = ["compress"]
|
required-features = ["compress"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix-codec = "0.2.0"
|
actix-codec = "0.3.0"
|
||||||
actix-service = "1.0.2"
|
actix-service = "1.0.6"
|
||||||
actix-utils = "1.0.6"
|
actix-utils = "2.0.0"
|
||||||
actix-router = "0.2.4"
|
actix-router = "0.2.4"
|
||||||
actix-rt = "1.1.1"
|
actix-rt = "1.1.1"
|
||||||
actix-server = "1.0.0"
|
actix-server = "1.0.0"
|
||||||
actix-testing = "1.0.0"
|
actix-testing = "1.0.0"
|
||||||
actix-macros = "0.1.0"
|
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"
|
||||||
|
|
||||||
actix-web-codegen = "0.3.0-beta.1"
|
actix-web-codegen = "0.3.0"
|
||||||
actix-http = "2.0.0-alpha.4"
|
actix-http = "2.0.0"
|
||||||
awc = { version = "2.0.0-beta.1", default-features = false }
|
awc = { version = "2.0.0", default-features = false }
|
||||||
|
|
||||||
bytes = "0.5.3"
|
bytes = "0.5.3"
|
||||||
derive_more = "0.99.2"
|
derive_more = "0.99.2"
|
||||||
@ -92,17 +92,18 @@ mime = "0.3"
|
|||||||
socket2 = "0.3"
|
socket2 = "0.3"
|
||||||
pin-project = "0.4.17"
|
pin-project = "0.4.17"
|
||||||
regex = "1.3"
|
regex = "1.3"
|
||||||
serde = { version = "1.0", features=["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
serde_urlencoded = "0.6.1"
|
serde_urlencoded = "0.6.1"
|
||||||
time = { version = "0.2.7", default-features = false, features = ["std"] }
|
time = { version = "0.2.7", default-features = false, features = ["std"] }
|
||||||
url = "2.1"
|
url = "2.1"
|
||||||
open-ssl = { package = "openssl", version = "0.10", optional = true }
|
open-ssl = { package = "openssl", version = "0.10", optional = true }
|
||||||
rust-tls = { package = "rustls", version = "0.17.0", optional = true }
|
rust-tls = { package = "rustls", version = "0.18.0", optional = true }
|
||||||
tinyvec = { version = "0.3", features = ["alloc"] }
|
tinyvec = { version = "0.3", features = ["alloc"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
actix = "0.10.0-alpha.1"
|
actix = "0.10.0"
|
||||||
|
actix-http = { version = "2.0.0-beta.4", features = ["actors"] }
|
||||||
rand = "0.7"
|
rand = "0.7"
|
||||||
env_logger = "0.7"
|
env_logger = "0.7"
|
||||||
serde_derive = "1.0"
|
serde_derive = "1.0"
|
||||||
@ -124,6 +125,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
|
||||||
|
@ -186,7 +186,7 @@
|
|||||||
same "printed page" as the copyright notice for easier
|
same "printed page" as the copyright notice for easier
|
||||||
identification within third-party archives.
|
identification within third-party archives.
|
||||||
|
|
||||||
Copyright 2017-NOW Nikolay Kim
|
Copyright 2017-NOW Actix Team
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
Copyright (c) 2017 Nikolay Kim
|
Copyright (c) 2017 Actix Team
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any
|
Permission is hereby granted, free of charge, to any
|
||||||
person obtaining a copy of this software and associated
|
person obtaining a copy of this software and associated
|
||||||
|
32
MIGRATION.md
32
MIGRATION.md
@ -1,5 +1,8 @@
|
|||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
|
|
||||||
|
## 3.0.0
|
||||||
|
|
||||||
* Setting a cookie's SameSite property, explicitly, to `SameSite::None` will now
|
* Setting a cookie's SameSite property, explicitly, to `SameSite::None` will now
|
||||||
result in `SameSite=None` being sent with the response Set-Cookie header.
|
result in `SameSite=None` being sent with the response Set-Cookie header.
|
||||||
To create a cookie without a SameSite attribute, remove any calls setting same_site.
|
To create a cookie without a SameSite attribute, remove any calls setting same_site.
|
||||||
@ -12,6 +15,35 @@
|
|||||||
* `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)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
* `middleware::NormalizePath` can now also be configured to trim trailing slashes instead of always keeping one.
|
||||||
|
It will need `middleware::normalize::TrailingSlash` when being constructed with `NormalizePath::new(...)`,
|
||||||
|
or for an easier migration you can replace `wrap(middleware::NormalizePath)` with `wrap(middleware::NormalizePath::default())`.
|
||||||
|
|
||||||
|
* `HttpServer::maxconn` is renamed to the more expressive `HttpServer::max_connections`.
|
||||||
|
|
||||||
|
* `HttpServer::maxconnrate` is renamed to the more expressive `HttpServer::max_connection_rate`.
|
||||||
|
|
||||||
|
|
||||||
## 2.0.0
|
## 2.0.0
|
||||||
|
|
||||||
* `HttpServer::start()` renamed to `HttpServer::run()`. It also possible to
|
* `HttpServer::start()` renamed to `HttpServer::run()`. It also possible to
|
||||||
|
15
README.md
15
README.md
@ -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,22 +32,17 @@
|
|||||||
* 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
|
||||||
|
|
||||||
* [Website & User Guide](https://actix.rs)
|
* [Website & User Guide](https://actix.rs)
|
||||||
* [Examples Repository](https://actix.rs/actix-web/actix_web)
|
* [Examples Repository](https://github.com/actix/examples)
|
||||||
* [API Documentation](https://docs.rs/actix-web)
|
* [API Documentation](https://docs.rs/actix-web)
|
||||||
* [API Documentation (master branch)](https://actix.rs/actix-web/actix_web)
|
* [API Documentation (master branch)](https://actix.rs/actix-web/actix_web)
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
|
|
||||||
<h2>
|
|
||||||
WARNING: This example is for the master branch which is currently in beta stages for v3. For
|
|
||||||
Actix web v2 see the <a href="https://actix.rs/docs/getting-started/">getting started guide</a>.
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
Dependencies:
|
Dependencies:
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
@ -61,8 +56,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 +0,0 @@
|
|||||||
# Cors Middleware for actix web framework [](https://travis-ci.org/actix/actix-web) [](https://codecov.io/gh/actix/actix-web) [](https://crates.io/crates/actix-cors) [](https://gitter.im/actix/actix?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
|
||||||
|
|
||||||
**This crate moved to https://github.com/actix/actix-extras.**
|
|
||||||
|
|
||||||
## Documentation & community resources
|
|
||||||
|
|
||||||
* [User Guide](https://actix.rs/docs/)
|
|
||||||
* [API Documentation](https://docs.rs/actix-cors/)
|
|
||||||
* [Chat on gitter](https://gitter.im/actix/actix)
|
|
||||||
* Cargo package: [actix-cors](https://crates.io/crates/actix-cors)
|
|
||||||
* Minimum supported Rust version: 1.34 or later
|
|
@ -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"
|
||||||
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"
|
||||||
@ -17,9 +17,9 @@ 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", default-features = false }
|
||||||
actix-http = "2.0.0-alpha.4"
|
actix-http = "2.0.0"
|
||||||
actix-service = "1.0.1"
|
actix-service = "1.0.6"
|
||||||
bitflags = "1"
|
bitflags = "1"
|
||||||
bytes = "0.5.3"
|
bytes = "0.5.3"
|
||||||
futures-core = { version = "0.3.5", default-features = false }
|
futures-core = { version = "0.3.5", default-features = false }
|
||||||
@ -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", features = ["openssl"] }
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#![allow(clippy::borrow_interior_mutable_const, clippy::type_complexity)]
|
|
||||||
|
|
||||||
//! Static files support
|
//! Static files support
|
||||||
|
|
||||||
|
#![deny(rust_2018_idioms)]
|
||||||
|
#![allow(clippy::borrow_interior_mutable_const)]
|
||||||
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
use std::fs::{DirEntry, File};
|
use std::fs::{DirEntry, File};
|
||||||
@ -26,7 +28,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;
|
||||||
@ -63,6 +64,7 @@ pub struct ChunkedReadFile {
|
|||||||
size: u64,
|
size: u64,
|
||||||
offset: u64,
|
offset: u64,
|
||||||
file: Option<File>,
|
file: Option<File>,
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
fut:
|
fut:
|
||||||
Option<LocalBoxFuture<'static, Result<(File, Bytes), BlockingError<io::Error>>>>,
|
Option<LocalBoxFuture<'static, Result<(File, Bytes), BlockingError<io::Error>>>>,
|
||||||
counter: u64,
|
counter: u64,
|
||||||
@ -73,7 +75,7 @@ impl Stream for ChunkedReadFile {
|
|||||||
|
|
||||||
fn poll_next(
|
fn poll_next(
|
||||||
mut self: Pin<&mut Self>,
|
mut self: Pin<&mut Self>,
|
||||||
cx: &mut Context,
|
cx: &mut Context<'_>,
|
||||||
) -> Poll<Option<Self::Item>> {
|
) -> Poll<Option<Self::Item>> {
|
||||||
if let Some(ref mut fut) = self.fut {
|
if let Some(ref mut fut) = self.fut {
|
||||||
return match Pin::new(fut).poll(cx) {
|
return match Pin::new(fut).poll(cx) {
|
||||||
@ -225,7 +227,7 @@ fn directory_listing(
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
type MimeOverride = dyn Fn(&mime::Name) -> DispositionType;
|
type MimeOverride = dyn Fn(&mime::Name<'_>) -> DispositionType;
|
||||||
|
|
||||||
/// Static files handling
|
/// Static files handling
|
||||||
///
|
///
|
||||||
@ -233,12 +235,10 @@ type MimeOverride = dyn Fn(&mime::Name) -> DispositionType;
|
|||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use actix_web::App;
|
/// use actix_web::App;
|
||||||
/// use actix_files as fs;
|
/// use actix_files::Files;
|
||||||
///
|
///
|
||||||
/// fn main() {
|
/// let app = App::new()
|
||||||
/// let app = App::new()
|
/// .service(Files::new("/static", "."));
|
||||||
/// .service(fs::Files::new("/static", "."));
|
|
||||||
/// }
|
|
||||||
/// ```
|
/// ```
|
||||||
pub struct Files {
|
pub struct Files {
|
||||||
path: String,
|
path: String,
|
||||||
@ -250,6 +250,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>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -329,7 +331,7 @@ impl Files {
|
|||||||
/// Specifies mime override callback
|
/// Specifies mime override callback
|
||||||
pub fn mime_override<F>(mut self, f: F) -> Self
|
pub fn mime_override<F>(mut self, f: F) -> Self
|
||||||
where
|
where
|
||||||
F: Fn(&mime::Name) -> DispositionType + 'static,
|
F: Fn(&mime::Name<'_>) -> DispositionType + 'static,
|
||||||
{
|
{
|
||||||
self.mime_override = Some(Rc::new(f));
|
self.mime_override = Some(Rc::new(f));
|
||||||
self
|
self
|
||||||
@ -462,10 +464,13 @@ 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>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FilesService {
|
impl FilesService {
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
fn handle_err(
|
fn handle_err(
|
||||||
&mut self,
|
&mut self,
|
||||||
e: io::Error,
|
e: io::Error,
|
||||||
@ -487,12 +492,13 @@ impl Service for FilesService {
|
|||||||
type Request = ServiceRequest;
|
type Request = ServiceRequest;
|
||||||
type Response = ServiceResponse;
|
type Response = ServiceResponse;
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
type Future = Either<
|
type Future = Either<
|
||||||
Ready<Result<Self::Response, Self::Error>>,
|
Ready<Result<Self::Response, Self::Error>>,
|
||||||
LocalBoxFuture<'static, Result<Self::Response, Self::Error>>,
|
LocalBoxFuture<'static, Result<Self::Response, Self::Error>>,
|
||||||
>;
|
>;
|
||||||
|
|
||||||
fn poll_ready(&mut self, _: &mut Context) -> Poll<Result<(), Self::Error>> {
|
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||||
Poll::Ready(Ok(()))
|
Poll::Ready(Ok(()))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -501,11 +507,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 {
|
||||||
@ -898,7 +901,7 @@ mod tests {
|
|||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_mime_override() {
|
async fn test_mime_override() {
|
||||||
fn all_attachment(_: &mime::Name) -> DispositionType {
|
fn all_attachment(_: &mime::Name<'_>) -> DispositionType {
|
||||||
DispositionType::Attachment
|
DispositionType::Attachment
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -952,9 +955,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 +980,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 +1019,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 +1090,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 +1110,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,3 +0,0 @@
|
|||||||
# Framed app for actix web
|
|
||||||
|
|
||||||
**This crate has been deprecated and removed.**
|
|
@ -1,6 +1,36 @@
|
|||||||
# Changes
|
# Changes
|
||||||
|
|
||||||
## [Unreleased] - xxx
|
## Unreleased - 2020-xx-xx
|
||||||
|
|
||||||
|
|
||||||
|
## 2.0.0 - 2020-09-11
|
||||||
|
* No significant changes from `2.0.0-beta.4`.
|
||||||
|
|
||||||
|
|
||||||
|
## 2.0.0-beta.4 - 2020-09-09
|
||||||
|
### Changed
|
||||||
|
* Update actix-codec and actix-utils dependencies.
|
||||||
|
* Update actix-connect and actix-tls dependencies.
|
||||||
|
|
||||||
|
|
||||||
|
## [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"
|
||||||
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"
|
||||||
@ -40,14 +40,14 @@ secure-cookies = ["cookie/secure"]
|
|||||||
actors = ["actix"]
|
actors = ["actix"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix-service = "1.0.5"
|
actix-service = "1.0.6"
|
||||||
actix-codec = "0.2.0"
|
actix-codec = "0.3.0"
|
||||||
actix-connect = "2.0.0-alpha.3"
|
actix-connect = "2.0.0"
|
||||||
actix-utils = "1.0.6"
|
actix-utils = "2.0.0"
|
||||||
actix-rt = "1.0.0"
|
actix-rt = "1.0.0"
|
||||||
actix-threadpool = "0.3.1"
|
actix-threadpool = "0.3.1"
|
||||||
actix-tls = { version = "2.0.0-alpha.1", optional = true }
|
actix-tls = { version = "2.0.0", optional = true }
|
||||||
actix = { version = "0.10.0-alpha.1", optional = true }
|
actix = { version = "0.10.0", optional = true }
|
||||||
|
|
||||||
base64 = "0.12"
|
base64 = "0.12"
|
||||||
bitflags = "1.2"
|
bitflags = "1.2"
|
||||||
@ -87,14 +87,14 @@ flate2 = { version = "1.0.13", optional = true }
|
|||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
actix-server = "1.0.1"
|
actix-server = "1.0.1"
|
||||||
actix-connect = { version = "2.0.0-alpha.2", features = ["openssl"] }
|
actix-connect = { version = "2.0.0", features = ["openssl"] }
|
||||||
actix-http-test = { version = "2.0.0-alpha.1", features = ["openssl"] }
|
actix-http-test = { version = "2.0.0", features = ["openssl"] }
|
||||||
actix-tls = { version = "2.0.0-alpha.1", features = ["openssl"] }
|
actix-tls = { version = "2.0.0", features = ["openssl"] }
|
||||||
criterion = "0.3"
|
criterion = "0.3"
|
||||||
env_logger = "0.7"
|
env_logger = "0.7"
|
||||||
serde_derive = "1.0"
|
serde_derive = "1.0"
|
||||||
open-ssl = { version="0.10", package = "openssl" }
|
open-ssl = { version="0.10", package = "openssl" }
|
||||||
rust-tls = { version="0.17", package = "rustls" }
|
rust-tls = { version="0.18", package = "rustls" }
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "content-length"
|
name = "content-length"
|
||||||
@ -103,3 +103,7 @@ harness = false
|
|||||||
[[bench]]
|
[[bench]]
|
||||||
name = "status-line"
|
name = "status-line"
|
||||||
harness = false
|
harness = false
|
||||||
|
|
||||||
|
[[bench]]
|
||||||
|
name = "uninit-headers"
|
||||||
|
harness = false
|
||||||
|
@ -1,201 +0,0 @@
|
|||||||
Apache License
|
|
||||||
Version 2.0, January 2004
|
|
||||||
http://www.apache.org/licenses/
|
|
||||||
|
|
||||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
||||||
|
|
||||||
1. Definitions.
|
|
||||||
|
|
||||||
"License" shall mean the terms and conditions for use, reproduction,
|
|
||||||
and distribution as defined by Sections 1 through 9 of this document.
|
|
||||||
|
|
||||||
"Licensor" shall mean the copyright owner or entity authorized by
|
|
||||||
the copyright owner that is granting the License.
|
|
||||||
|
|
||||||
"Legal Entity" shall mean the union of the acting entity and all
|
|
||||||
other entities that control, are controlled by, or are under common
|
|
||||||
control with that entity. For the purposes of this definition,
|
|
||||||
"control" means (i) the power, direct or indirect, to cause the
|
|
||||||
direction or management of such entity, whether by contract or
|
|
||||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
||||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
||||||
|
|
||||||
"You" (or "Your") shall mean an individual or Legal Entity
|
|
||||||
exercising permissions granted by this License.
|
|
||||||
|
|
||||||
"Source" form shall mean the preferred form for making modifications,
|
|
||||||
including but not limited to software source code, documentation
|
|
||||||
source, and configuration files.
|
|
||||||
|
|
||||||
"Object" form shall mean any form resulting from mechanical
|
|
||||||
transformation or translation of a Source form, including but
|
|
||||||
not limited to compiled object code, generated documentation,
|
|
||||||
and conversions to other media types.
|
|
||||||
|
|
||||||
"Work" shall mean the work of authorship, whether in Source or
|
|
||||||
Object form, made available under the License, as indicated by a
|
|
||||||
copyright notice that is included in or attached to the work
|
|
||||||
(an example is provided in the Appendix below).
|
|
||||||
|
|
||||||
"Derivative Works" shall mean any work, whether in Source or Object
|
|
||||||
form, that is based on (or derived from) the Work and for which the
|
|
||||||
editorial revisions, annotations, elaborations, or other modifications
|
|
||||||
represent, as a whole, an original work of authorship. For the purposes
|
|
||||||
of this License, Derivative Works shall not include works that remain
|
|
||||||
separable from, or merely link (or bind by name) to the interfaces of,
|
|
||||||
the Work and Derivative Works thereof.
|
|
||||||
|
|
||||||
"Contribution" shall mean any work of authorship, including
|
|
||||||
the original version of the Work and any modifications or additions
|
|
||||||
to that Work or Derivative Works thereof, that is intentionally
|
|
||||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
||||||
or by an individual or Legal Entity authorized to submit on behalf of
|
|
||||||
the copyright owner. For the purposes of this definition, "submitted"
|
|
||||||
means any form of electronic, verbal, or written communication sent
|
|
||||||
to the Licensor or its representatives, including but not limited to
|
|
||||||
communication on electronic mailing lists, source code control systems,
|
|
||||||
and issue tracking systems that are managed by, or on behalf of, the
|
|
||||||
Licensor for the purpose of discussing and improving the Work, but
|
|
||||||
excluding communication that is conspicuously marked or otherwise
|
|
||||||
designated in writing by the copyright owner as "Not a Contribution."
|
|
||||||
|
|
||||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
||||||
on behalf of whom a Contribution has been received by Licensor and
|
|
||||||
subsequently incorporated within the Work.
|
|
||||||
|
|
||||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
copyright license to reproduce, prepare Derivative Works of,
|
|
||||||
publicly display, publicly perform, sublicense, and distribute the
|
|
||||||
Work and such Derivative Works in Source or Object form.
|
|
||||||
|
|
||||||
3. Grant of Patent License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
(except as stated in this section) patent license to make, have made,
|
|
||||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
||||||
where such license applies only to those patent claims licensable
|
|
||||||
by such Contributor that are necessarily infringed by their
|
|
||||||
Contribution(s) alone or by combination of their Contribution(s)
|
|
||||||
with the Work to which such Contribution(s) was submitted. If You
|
|
||||||
institute patent litigation against any entity (including a
|
|
||||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
||||||
or a Contribution incorporated within the Work constitutes direct
|
|
||||||
or contributory patent infringement, then any patent licenses
|
|
||||||
granted to You under this License for that Work shall terminate
|
|
||||||
as of the date such litigation is filed.
|
|
||||||
|
|
||||||
4. Redistribution. You may reproduce and distribute copies of the
|
|
||||||
Work or Derivative Works thereof in any medium, with or without
|
|
||||||
modifications, and in Source or Object form, provided that You
|
|
||||||
meet the following conditions:
|
|
||||||
|
|
||||||
(a) You must give any other recipients of the Work or
|
|
||||||
Derivative Works a copy of this License; and
|
|
||||||
|
|
||||||
(b) You must cause any modified files to carry prominent notices
|
|
||||||
stating that You changed the files; and
|
|
||||||
|
|
||||||
(c) You must retain, in the Source form of any Derivative Works
|
|
||||||
that You distribute, all copyright, patent, trademark, and
|
|
||||||
attribution notices from the Source form of the Work,
|
|
||||||
excluding those notices that do not pertain to any part of
|
|
||||||
the Derivative Works; and
|
|
||||||
|
|
||||||
(d) If the Work includes a "NOTICE" text file as part of its
|
|
||||||
distribution, then any Derivative Works that You distribute must
|
|
||||||
include a readable copy of the attribution notices contained
|
|
||||||
within such NOTICE file, excluding those notices that do not
|
|
||||||
pertain to any part of the Derivative Works, in at least one
|
|
||||||
of the following places: within a NOTICE text file distributed
|
|
||||||
as part of the Derivative Works; within the Source form or
|
|
||||||
documentation, if provided along with the Derivative Works; or,
|
|
||||||
within a display generated by the Derivative Works, if and
|
|
||||||
wherever such third-party notices normally appear. The contents
|
|
||||||
of the NOTICE file are for informational purposes only and
|
|
||||||
do not modify the License. You may add Your own attribution
|
|
||||||
notices within Derivative Works that You distribute, alongside
|
|
||||||
or as an addendum to the NOTICE text from the Work, provided
|
|
||||||
that such additional attribution notices cannot be construed
|
|
||||||
as modifying the License.
|
|
||||||
|
|
||||||
You may add Your own copyright statement to Your modifications and
|
|
||||||
may provide additional or different license terms and conditions
|
|
||||||
for use, reproduction, or distribution of Your modifications, or
|
|
||||||
for any such Derivative Works as a whole, provided Your use,
|
|
||||||
reproduction, and distribution of the Work otherwise complies with
|
|
||||||
the conditions stated in this License.
|
|
||||||
|
|
||||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
||||||
any Contribution intentionally submitted for inclusion in the Work
|
|
||||||
by You to the Licensor shall be under the terms and conditions of
|
|
||||||
this License, without any additional terms or conditions.
|
|
||||||
Notwithstanding the above, nothing herein shall supersede or modify
|
|
||||||
the terms of any separate license agreement you may have executed
|
|
||||||
with Licensor regarding such Contributions.
|
|
||||||
|
|
||||||
6. Trademarks. This License does not grant permission to use the trade
|
|
||||||
names, trademarks, service marks, or product names of the Licensor,
|
|
||||||
except as required for reasonable and customary use in describing the
|
|
||||||
origin of the Work and reproducing the content of the NOTICE file.
|
|
||||||
|
|
||||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
||||||
agreed to in writing, Licensor provides the Work (and each
|
|
||||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
||||||
implied, including, without limitation, any warranties or conditions
|
|
||||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
||||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
||||||
appropriateness of using or redistributing the Work and assume any
|
|
||||||
risks associated with Your exercise of permissions under this License.
|
|
||||||
|
|
||||||
8. Limitation of Liability. In no event and under no legal theory,
|
|
||||||
whether in tort (including negligence), contract, or otherwise,
|
|
||||||
unless required by applicable law (such as deliberate and grossly
|
|
||||||
negligent acts) or agreed to in writing, shall any Contributor be
|
|
||||||
liable to You for damages, including any direct, indirect, special,
|
|
||||||
incidental, or consequential damages of any character arising as a
|
|
||||||
result of this License or out of the use or inability to use the
|
|
||||||
Work (including but not limited to damages for loss of goodwill,
|
|
||||||
work stoppage, computer failure or malfunction, or any and all
|
|
||||||
other commercial damages or losses), even if such Contributor
|
|
||||||
has been advised of the possibility of such damages.
|
|
||||||
|
|
||||||
9. Accepting Warranty or Additional Liability. While redistributing
|
|
||||||
the Work or Derivative Works thereof, You may choose to offer,
|
|
||||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
||||||
or other liability obligations and/or rights consistent with this
|
|
||||||
License. However, in accepting such obligations, You may act only
|
|
||||||
on Your own behalf and on Your sole responsibility, not on behalf
|
|
||||||
of any other Contributor, and only if You agree to indemnify,
|
|
||||||
defend, and hold each Contributor harmless for any liability
|
|
||||||
incurred by, or claims asserted against, such Contributor by reason
|
|
||||||
of your accepting any such warranty or additional liability.
|
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
|
||||||
|
|
||||||
APPENDIX: How to apply the Apache License to your work.
|
|
||||||
|
|
||||||
To apply the Apache License to your work, attach the following
|
|
||||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
|
||||||
replaced with your own identifying information. (Don't include
|
|
||||||
the brackets!) The text should be enclosed in the appropriate
|
|
||||||
comment syntax for the file format. We also recommend that a
|
|
||||||
file or class name and description of purpose be included on the
|
|
||||||
same "printed page" as the copyright notice for easier
|
|
||||||
identification within third-party archives.
|
|
||||||
|
|
||||||
Copyright 2017-NOW Nikolay Kim
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
1
actix-http/LICENSE-APACHE
Symbolic link
1
actix-http/LICENSE-APACHE
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../LICENSE-APACHE
|
@ -1,25 +0,0 @@
|
|||||||
Copyright (c) 2017 Nikolay Kim
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any
|
|
||||||
person obtaining a copy of this software and associated
|
|
||||||
documentation files (the "Software"), to deal in the
|
|
||||||
Software without restriction, including without
|
|
||||||
limitation the rights to use, copy, modify, merge,
|
|
||||||
publish, distribute, sublicense, and/or sell copies of
|
|
||||||
the Software, and to permit persons to whom the Software
|
|
||||||
is furnished to do so, subject to the following
|
|
||||||
conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice
|
|
||||||
shall be included in all copies or substantial portions
|
|
||||||
of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
|
||||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
|
||||||
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
|
||||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
|
||||||
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
||||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
||||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
|
||||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
||||||
DEALINGS IN THE SOFTWARE.
|
|
1
actix-http/LICENSE-MIT
Symbolic link
1
actix-http/LICENSE-MIT
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../LICENSE-MIT
|
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]
|
||||||
@ -729,7 +714,7 @@ mod tests {
|
|||||||
let body = resp_body.downcast_ref::<String>().unwrap();
|
let body = resp_body.downcast_ref::<String>().unwrap();
|
||||||
assert_eq!(body, "hello cast");
|
assert_eq!(body, "hello cast");
|
||||||
let body = &mut resp_body.downcast_mut::<String>().unwrap();
|
let body = &mut resp_body.downcast_mut::<String>().unwrap();
|
||||||
body.push_str("!");
|
body.push('!');
|
||||||
let body = resp_body.downcast_ref::<String>().unwrap();
|
let body = resp_body.downcast_ref::<String>().unwrap();
|
||||||
assert_eq!(body, "hello cast!");
|
assert_eq!(body, "hello cast!");
|
||||||
let not_body = resp_body.downcast_ref::<()>();
|
let not_body = resp_body.downcast_ref::<()>();
|
||||||
|
@ -46,10 +46,10 @@ pub trait Connection {
|
|||||||
|
|
||||||
pub(crate) trait ConnectionLifetime: AsyncRead + AsyncWrite + 'static {
|
pub(crate) trait ConnectionLifetime: AsyncRead + AsyncWrite + 'static {
|
||||||
/// Close connection
|
/// Close connection
|
||||||
fn close(&mut self);
|
fn close(self: Pin<&mut Self>);
|
||||||
|
|
||||||
/// Release connection to the connection pool
|
/// Release connection to the connection pool
|
||||||
fn release(&mut self);
|
fn release(self: Pin<&mut Self>);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
@ -195,11 +195,15 @@ where
|
|||||||
match self {
|
match self {
|
||||||
EitherConnection::A(con) => con
|
EitherConnection::A(con) => con
|
||||||
.open_tunnel(head)
|
.open_tunnel(head)
|
||||||
.map(|res| res.map(|(head, framed)| (head, framed.map_io(EitherIo::A))))
|
.map(|res| {
|
||||||
|
res.map(|(head, framed)| (head, framed.into_map_io(EitherIo::A)))
|
||||||
|
})
|
||||||
.boxed_local(),
|
.boxed_local(),
|
||||||
EitherConnection::B(con) => con
|
EitherConnection::B(con) => con
|
||||||
.open_tunnel(head)
|
.open_tunnel(head)
|
||||||
.map(|res| res.map(|(head, framed)| (head, framed.map_io(EitherIo::B))))
|
.map(|res| {
|
||||||
|
res.map(|(head, framed)| (head, framed.into_map_io(EitherIo::B)))
|
||||||
|
})
|
||||||
.boxed_local(),
|
.boxed_local(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -67,17 +67,17 @@ where
|
|||||||
};
|
};
|
||||||
|
|
||||||
// create Framed and send request
|
// create Framed and send request
|
||||||
let mut framed = Framed::new(io, h1::ClientCodec::default());
|
let mut framed_inner = Framed::new(io, h1::ClientCodec::default());
|
||||||
framed.send((head, body.size()).into()).await?;
|
framed_inner.send((head, body.size()).into()).await?;
|
||||||
|
|
||||||
// send request body
|
// send request body
|
||||||
match body.size() {
|
match body.size() {
|
||||||
BodySize::None | BodySize::Empty | BodySize::Sized(0) => (),
|
BodySize::None | BodySize::Empty | BodySize::Sized(0) => (),
|
||||||
_ => send_body(body, &mut framed).await?,
|
_ => send_body(body, Pin::new(&mut framed_inner)).await?,
|
||||||
};
|
};
|
||||||
|
|
||||||
// read response and init read body
|
// read response and init read body
|
||||||
let res = framed.into_future().await;
|
let res = Pin::new(&mut framed_inner).into_future().await;
|
||||||
let (head, framed) = if let (Some(result), framed) = res {
|
let (head, framed) = if let (Some(result), framed) = res {
|
||||||
let item = result.map_err(SendRequestError::from)?;
|
let item = result.map_err(SendRequestError::from)?;
|
||||||
(item, framed)
|
(item, framed)
|
||||||
@ -85,14 +85,14 @@ where
|
|||||||
return Err(SendRequestError::from(ConnectError::Disconnected));
|
return Err(SendRequestError::from(ConnectError::Disconnected));
|
||||||
};
|
};
|
||||||
|
|
||||||
match framed.get_codec().message_type() {
|
match framed.codec_ref().message_type() {
|
||||||
h1::MessageType::None => {
|
h1::MessageType::None => {
|
||||||
let force_close = !framed.get_codec().keepalive();
|
let force_close = !framed.codec_ref().keepalive();
|
||||||
release_connection(framed, force_close);
|
release_connection(framed, force_close);
|
||||||
Ok((head, Payload::None))
|
Ok((head, Payload::None))
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let pl: PayloadStream = PlStream::new(framed).boxed_local();
|
let pl: PayloadStream = PlStream::new(framed_inner).boxed_local();
|
||||||
Ok((head, pl.into()))
|
Ok((head, pl.into()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -119,35 +119,36 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// send request body to the peer
|
/// send request body to the peer
|
||||||
pub(crate) async fn send_body<I, B>(
|
pub(crate) async fn send_body<T, B>(
|
||||||
body: B,
|
body: B,
|
||||||
framed: &mut Framed<I, h1::ClientCodec>,
|
mut framed: Pin<&mut Framed<T, h1::ClientCodec>>,
|
||||||
) -> Result<(), SendRequestError>
|
) -> Result<(), SendRequestError>
|
||||||
where
|
where
|
||||||
I: ConnectionLifetime,
|
T: ConnectionLifetime + Unpin,
|
||||||
B: MessageBody,
|
B: MessageBody,
|
||||||
{
|
{
|
||||||
let mut eof = false;
|
|
||||||
pin_mut!(body);
|
pin_mut!(body);
|
||||||
|
|
||||||
|
let mut eof = false;
|
||||||
while !eof {
|
while !eof {
|
||||||
while !eof && !framed.is_write_buf_full() {
|
while !eof && !framed.as_ref().is_write_buf_full() {
|
||||||
match poll_fn(|cx| body.as_mut().poll_next(cx)).await {
|
match poll_fn(|cx| body.as_mut().poll_next(cx)).await {
|
||||||
Some(result) => {
|
Some(result) => {
|
||||||
framed.write(h1::Message::Chunk(Some(result?)))?;
|
framed.as_mut().write(h1::Message::Chunk(Some(result?)))?;
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
eof = true;
|
eof = true;
|
||||||
framed.write(h1::Message::Chunk(None))?;
|
framed.as_mut().write(h1::Message::Chunk(None))?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !framed.is_write_buf_empty() {
|
if !framed.as_ref().is_write_buf_empty() {
|
||||||
poll_fn(|cx| match framed.flush(cx) {
|
poll_fn(|cx| match framed.as_mut().flush(cx) {
|
||||||
Poll::Ready(Ok(_)) => Poll::Ready(Ok(())),
|
Poll::Ready(Ok(_)) => Poll::Ready(Ok(())),
|
||||||
Poll::Ready(Err(err)) => Poll::Ready(Err(err)),
|
Poll::Ready(Err(err)) => Poll::Ready(Err(err)),
|
||||||
Poll::Pending => {
|
Poll::Pending => {
|
||||||
if !framed.is_write_buf_full() {
|
if !framed.as_ref().is_write_buf_full() {
|
||||||
Poll::Ready(Ok(()))
|
Poll::Ready(Ok(()))
|
||||||
} else {
|
} else {
|
||||||
Poll::Pending
|
Poll::Pending
|
||||||
@ -158,13 +159,14 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SinkExt::flush(framed).await?;
|
SinkExt::flush(Pin::into_inner(framed)).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
/// HTTP client connection
|
/// HTTP client connection
|
||||||
pub struct H1Connection<T> {
|
pub struct H1Connection<T> {
|
||||||
|
/// T should be `Unpin`
|
||||||
io: Option<T>,
|
io: Option<T>,
|
||||||
created: time::Instant,
|
created: time::Instant,
|
||||||
pool: Option<Acquired<T>>,
|
pool: Option<Acquired<T>>,
|
||||||
@ -175,7 +177,7 @@ where
|
|||||||
T: AsyncRead + AsyncWrite + Unpin + 'static,
|
T: AsyncRead + AsyncWrite + Unpin + 'static,
|
||||||
{
|
{
|
||||||
/// Close connection
|
/// Close connection
|
||||||
fn close(&mut self) {
|
fn close(mut self: Pin<&mut Self>) {
|
||||||
if let Some(mut pool) = self.pool.take() {
|
if let Some(mut pool) = self.pool.take() {
|
||||||
if let Some(io) = self.io.take() {
|
if let Some(io) = self.io.take() {
|
||||||
pool.close(IoConnection::new(
|
pool.close(IoConnection::new(
|
||||||
@ -188,7 +190,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Release this connection to the connection pool
|
/// Release this connection to the connection pool
|
||||||
fn release(&mut self) {
|
fn release(mut self: Pin<&mut Self>) {
|
||||||
if let Some(mut pool) = self.pool.take() {
|
if let Some(mut pool) = self.pool.take() {
|
||||||
if let Some(io) = self.io.take() {
|
if let Some(io) = self.io.take() {
|
||||||
pool.release(IoConnection::new(
|
pool.release(IoConnection::new(
|
||||||
@ -242,14 +244,18 @@ impl<T: AsyncRead + AsyncWrite + Unpin + 'static> AsyncWrite for H1Connection<T>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[pin_project::pin_project]
|
||||||
pub(crate) struct PlStream<Io> {
|
pub(crate) struct PlStream<Io> {
|
||||||
|
#[pin]
|
||||||
framed: Option<Framed<Io, h1::ClientPayloadCodec>>,
|
framed: Option<Framed<Io, h1::ClientPayloadCodec>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Io: ConnectionLifetime> PlStream<Io> {
|
impl<Io: ConnectionLifetime> PlStream<Io> {
|
||||||
fn new(framed: Framed<Io, h1::ClientCodec>) -> Self {
|
fn new(framed: Framed<Io, h1::ClientCodec>) -> Self {
|
||||||
|
let framed = framed.into_map_codec(|codec| codec.into_payload_codec());
|
||||||
|
|
||||||
PlStream {
|
PlStream {
|
||||||
framed: Some(framed.map_codec(|codec| codec.into_payload_codec())),
|
framed: Some(framed),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -261,16 +267,16 @@ impl<Io: ConnectionLifetime> Stream for PlStream<Io> {
|
|||||||
self: Pin<&mut Self>,
|
self: Pin<&mut Self>,
|
||||||
cx: &mut Context<'_>,
|
cx: &mut Context<'_>,
|
||||||
) -> Poll<Option<Self::Item>> {
|
) -> Poll<Option<Self::Item>> {
|
||||||
let this = self.get_mut();
|
let mut this = self.project();
|
||||||
|
|
||||||
match this.framed.as_mut().unwrap().next_item(cx)? {
|
match this.framed.as_mut().as_pin_mut().unwrap().next_item(cx)? {
|
||||||
Poll::Pending => Poll::Pending,
|
Poll::Pending => Poll::Pending,
|
||||||
Poll::Ready(Some(chunk)) => {
|
Poll::Ready(Some(chunk)) => {
|
||||||
if let Some(chunk) = chunk {
|
if let Some(chunk) = chunk {
|
||||||
Poll::Ready(Some(Ok(chunk)))
|
Poll::Ready(Some(Ok(chunk)))
|
||||||
} else {
|
} else {
|
||||||
let framed = this.framed.take().unwrap();
|
let framed = this.framed.as_mut().as_pin_mut().unwrap();
|
||||||
let force_close = !framed.get_codec().keepalive();
|
let force_close = !framed.codec_ref().keepalive();
|
||||||
release_connection(framed, force_close);
|
release_connection(framed, force_close);
|
||||||
Poll::Ready(None)
|
Poll::Ready(None)
|
||||||
}
|
}
|
||||||
@ -280,14 +286,13 @@ impl<Io: ConnectionLifetime> Stream for PlStream<Io> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn release_connection<T, U>(framed: Framed<T, U>, force_close: bool)
|
fn release_connection<T, U>(framed: Pin<&mut Framed<T, U>>, force_close: bool)
|
||||||
where
|
where
|
||||||
T: ConnectionLifetime,
|
T: ConnectionLifetime,
|
||||||
{
|
{
|
||||||
let mut parts = framed.into_parts();
|
if !force_close && framed.is_read_buf_empty() && framed.is_write_buf_empty() {
|
||||||
if !force_close && parts.read_buf.is_empty() && parts.write_buf.is_empty() {
|
framed.io_pin().release()
|
||||||
parts.io.release()
|
|
||||||
} else {
|
} else {
|
||||||
parts.io.close()
|
framed.io_pin().close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
@ -115,11 +119,11 @@ where
|
|||||||
match poll_fn(|cx| Poll::Ready(inner.borrow_mut().acquire(&key, cx))).await {
|
match poll_fn(|cx| Poll::Ready(inner.borrow_mut().acquire(&key, cx))).await {
|
||||||
Acquire::Acquired(io, created) => {
|
Acquire::Acquired(io, created) => {
|
||||||
// use existing connection
|
// use existing connection
|
||||||
return Ok(IoConnection::new(
|
Ok(IoConnection::new(
|
||||||
io,
|
io,
|
||||||
created,
|
created,
|
||||||
Some(Acquired(key, Some(inner))),
|
Some(Acquired(key, Some(inner))),
|
||||||
));
|
))
|
||||||
}
|
}
|
||||||
Acquire::Available => {
|
Acquire::Available => {
|
||||||
// open tcp connection
|
// open tcp connection
|
||||||
@ -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,55 +442,57 @@ 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
|
||||||
inner.waker.register(cx.waker());
|
// and we are safe to exit.
|
||||||
|
return Poll::Ready(());
|
||||||
|
}
|
||||||
|
|
||||||
// check waiters
|
let mut inner = this.inner.borrow_mut();
|
||||||
loop {
|
inner.waker.register(cx.waker());
|
||||||
let (key, token) = {
|
|
||||||
if let Some((key, token)) = inner.waiters_queue.get_index(0) {
|
|
||||||
(key.clone(), *token)
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if inner.waiters.get(token).unwrap().is_none() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
match inner.acquire(&key, cx) {
|
// check waiters
|
||||||
Acquire::NotAvailable => break,
|
loop {
|
||||||
Acquire::Acquired(io, created) => {
|
let (key, token) = {
|
||||||
let tx = inner.waiters.get_mut(token).unwrap().take().unwrap().1;
|
if let Some((key, token)) = inner.waiters_queue.get_index(0) {
|
||||||
if let Err(conn) = tx.send(Ok(IoConnection::new(
|
(key.clone(), *token)
|
||||||
io,
|
} else {
|
||||||
created,
|
break;
|
||||||
Some(Acquired(key.clone(), Some(this_inner.clone()))),
|
|
||||||
))) {
|
|
||||||
let (io, created) = conn.unwrap().into_inner();
|
|
||||||
inner.release_conn(&key, io, created);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Acquire::Available => {
|
|
||||||
let (connect, tx) =
|
|
||||||
inner.waiters.get_mut(token).unwrap().take().unwrap();
|
|
||||||
OpenWaitingConnection::spawn(
|
|
||||||
key.clone(),
|
|
||||||
tx,
|
|
||||||
this_inner.clone(),
|
|
||||||
this.connector.call(connect),
|
|
||||||
inner.config.clone(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
let _ = inner.waiters_queue.swap_remove_index(0);
|
};
|
||||||
|
if inner.waiters.get(token).unwrap().is_none() {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
Poll::Pending
|
match inner.acquire(&key, cx) {
|
||||||
} else {
|
Acquire::NotAvailable => break,
|
||||||
Poll::Ready(())
|
Acquire::Acquired(io, created) => {
|
||||||
|
let tx = inner.waiters.get_mut(token).unwrap().take().unwrap().1;
|
||||||
|
if let Err(conn) = tx.send(Ok(IoConnection::new(
|
||||||
|
io,
|
||||||
|
created,
|
||||||
|
Some(Acquired(key.clone(), Some(this.inner.clone()))),
|
||||||
|
))) {
|
||||||
|
let (io, created) = conn.unwrap().into_inner();
|
||||||
|
inner.release_conn(&key, io, created);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Acquire::Available => {
|
||||||
|
let (connect, tx) =
|
||||||
|
inner.waiters.get_mut(token).unwrap().take().unwrap();
|
||||||
|
OpenWaitingConnection::spawn(
|
||||||
|
key.clone(),
|
||||||
|
tx,
|
||||||
|
this.inner.clone(),
|
||||||
|
this.connector.call(connect),
|
||||||
|
inner.config.clone(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let _ = inner.waiters_queue.swap_remove_index(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Poll::Pending
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ const DATE_VALUE_LENGTH: usize = 29;
|
|||||||
pub enum KeepAlive {
|
pub enum KeepAlive {
|
||||||
/// Keep alive in seconds
|
/// Keep alive in seconds
|
||||||
Timeout(usize),
|
Timeout(usize),
|
||||||
/// Relay on OS to shutdown tcp connection
|
/// Rely on OS to shutdown tcp connection
|
||||||
Os,
|
Os,
|
||||||
/// Disabled
|
/// Disabled
|
||||||
Disabled,
|
Disabled,
|
||||||
@ -209,6 +209,7 @@ impl Date {
|
|||||||
date.update();
|
date.update();
|
||||||
date
|
date
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(&mut self) {
|
fn update(&mut self) {
|
||||||
self.pos = 0;
|
self.pos = 0;
|
||||||
write!(
|
write!(
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
//! Error and Result module
|
//! Error and Result module
|
||||||
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::str::Utf8Error;
|
use std::str::Utf8Error;
|
||||||
@ -7,7 +8,7 @@ use std::{fmt, io, result};
|
|||||||
|
|
||||||
use actix_codec::{Decoder, Encoder};
|
use actix_codec::{Decoder, Encoder};
|
||||||
pub use actix_threadpool::BlockingError;
|
pub use actix_threadpool::BlockingError;
|
||||||
use actix_utils::framed::DispatcherError as FramedDispatcherError;
|
use actix_utils::dispatcher::DispatcherError as FramedDispatcherError;
|
||||||
use actix_utils::timeout::TimeoutError;
|
use actix_utils::timeout::TimeoutError;
|
||||||
use bytes::BytesMut;
|
use bytes::BytesMut;
|
||||||
use derive_more::{Display, From};
|
use derive_more::{Display, From};
|
||||||
@ -452,10 +453,10 @@ impl ResponseError for ContentTypeError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E, U: Encoder + Decoder> ResponseError for FramedDispatcherError<E, U>
|
impl<E, U: Encoder<I> + Decoder, I> ResponseError for FramedDispatcherError<E, U, I>
|
||||||
where
|
where
|
||||||
E: fmt::Debug + fmt::Display,
|
E: fmt::Debug + fmt::Display,
|
||||||
<U as Encoder>::Error: fmt::Debug,
|
<U as Encoder<I>>::Error: fmt::Debug,
|
||||||
<U as Decoder>::Error: fmt::Debug,
|
<U as Decoder>::Error: fmt::Debug,
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -964,7 +965,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]
|
||||||
|
@ -72,7 +72,7 @@ impl fmt::Debug for Extensions {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_remove() {
|
fn test_remove() {
|
||||||
let mut map = Extensions::new();
|
let mut map = Extensions::new();
|
||||||
|
@ -173,13 +173,12 @@ impl Decoder for ClientPayloadCodec {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Encoder for ClientCodec {
|
impl Encoder<Message<(RequestHeadType, BodySize)>> for ClientCodec {
|
||||||
type Item = Message<(RequestHeadType, BodySize)>;
|
|
||||||
type Error = io::Error;
|
type Error = io::Error;
|
||||||
|
|
||||||
fn encode(
|
fn encode(
|
||||||
&mut self,
|
&mut self,
|
||||||
item: Self::Item,
|
item: Message<(RequestHeadType, BodySize)>,
|
||||||
dst: &mut BytesMut,
|
dst: &mut BytesMut,
|
||||||
) -> Result<(), Self::Error> {
|
) -> Result<(), Self::Error> {
|
||||||
match item {
|
match item {
|
||||||
|
@ -144,13 +144,12 @@ impl Decoder for Codec {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Encoder for Codec {
|
impl Encoder<Message<(Response<()>, BodySize)>> for Codec {
|
||||||
type Item = Message<(Response<()>, BodySize)>;
|
|
||||||
type Error = io::Error;
|
type Error = io::Error;
|
||||||
|
|
||||||
fn encode(
|
fn encode(
|
||||||
&mut self,
|
&mut self,
|
||||||
item: Self::Item,
|
item: Message<(Response<()>, BodySize)>,
|
||||||
dst: &mut BytesMut,
|
dst: &mut BytesMut,
|
||||||
) -> Result<(), Self::Error> {
|
) -> Result<(), Self::Error> {
|
||||||
match item {
|
match item {
|
||||||
|
@ -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,12 +76,14 @@ 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 already checks header value is only visible ASCII bytes
|
||||||
|
// from_maybe_shared_unchecked contains debug assertions so they are omitted here
|
||||||
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),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
match name {
|
match name {
|
||||||
header::CONTENT_LENGTH => {
|
header::CONTENT_LENGTH => {
|
||||||
if let Ok(s) = value.to_str() {
|
if let Ok(s) = value.to_str() {
|
||||||
@ -124,12 +125,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 +154,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 +182,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 +215,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 +253,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 +312,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 +654,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 +666,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn eof(&self) -> bool {
|
fn eof(&self) -> bool {
|
||||||
match *self {
|
matches!(*self, PayloadItem::Eof)
|
||||||
PayloadItem::Eof => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -979,7 +972,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 +1033,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 +1047,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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -328,11 +314,15 @@ where
|
|||||||
Poll::Ready(Err(err)) => return Err(DispatchError::Io(err)),
|
Poll::Ready(Err(err)) => return Err(DispatchError::Io(err)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if written == write_buf.len() {
|
if written == write_buf.len() {
|
||||||
|
// SAFETY: setting length to 0 is safe
|
||||||
|
// skips one length check vs truncate
|
||||||
unsafe { write_buf.set_len(0) }
|
unsafe { write_buf.set_len(0) }
|
||||||
} else {
|
} else {
|
||||||
write_buf.advance(written);
|
write_buf.advance(written);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(false)
|
Ok(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,89 +129,133 @@ pub(crate) trait MessageType: Sized {
|
|||||||
.chain(extra_headers.inner.iter());
|
.chain(extra_headers.inner.iter());
|
||||||
|
|
||||||
// write headers
|
// write headers
|
||||||
let mut pos = 0;
|
|
||||||
let mut has_date = false;
|
let mut has_date = false;
|
||||||
let mut remaining = dst.capacity() - dst.len();
|
|
||||||
let mut buf = dst.bytes_mut().as_mut_ptr() as *mut u8;
|
let mut buf = dst.bytes_mut().as_mut_ptr() as *mut u8;
|
||||||
|
let mut remaining = dst.capacity() - dst.len();
|
||||||
|
|
||||||
|
// tracks bytes written since last buffer resize
|
||||||
|
// since buf is a raw pointer to a bytes container storage but is written to without the
|
||||||
|
// container's knowledge, this is used to sync the containers cursor after data is written
|
||||||
|
let mut pos = 0;
|
||||||
|
|
||||||
for (key, value) in headers {
|
for (key, value) in headers {
|
||||||
match *key {
|
match *key {
|
||||||
CONNECTION => continue,
|
CONNECTION => continue,
|
||||||
TRANSFER_ENCODING | CONTENT_LENGTH if skip_len => continue,
|
TRANSFER_ENCODING | CONTENT_LENGTH if skip_len => continue,
|
||||||
DATE => {
|
DATE => has_date = true,
|
||||||
has_date = true;
|
|
||||||
}
|
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
let k = key.as_str().as_bytes();
|
let k = key.as_str().as_bytes();
|
||||||
|
let k_len = k.len();
|
||||||
|
|
||||||
match value {
|
match value {
|
||||||
map::Value::One(ref val) => {
|
map::Value::One(ref val) => {
|
||||||
let v = val.as_ref();
|
let v = val.as_ref();
|
||||||
let v_len = v.len();
|
let v_len = v.len();
|
||||||
let k_len = k.len();
|
|
||||||
|
// key length + value length + colon + space + \r\n
|
||||||
let len = k_len + v_len + 4;
|
let len = k_len + v_len + 4;
|
||||||
|
|
||||||
if len > remaining {
|
if len > remaining {
|
||||||
|
// not enough room in buffer for this header; reserve more space
|
||||||
|
|
||||||
|
// SAFETY: all the bytes written up to position "pos" are initialized
|
||||||
|
// the written byte count and pointer advancement are kept in sync
|
||||||
unsafe {
|
unsafe {
|
||||||
dst.advance_mut(pos);
|
dst.advance_mut(pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
pos = 0;
|
pos = 0;
|
||||||
dst.reserve(len * 2);
|
dst.reserve(len * 2);
|
||||||
remaining = dst.capacity() - dst.len();
|
remaining = dst.capacity() - dst.len();
|
||||||
|
|
||||||
|
// re-assign buf raw pointer since it's possible that the buffer was
|
||||||
|
// reallocated and/or resized
|
||||||
buf = dst.bytes_mut().as_mut_ptr() as *mut u8;
|
buf = dst.bytes_mut().as_mut_ptr() as *mut u8;
|
||||||
}
|
}
|
||||||
// use upper Camel-Case
|
|
||||||
|
// SAFETY: on each write, it is enough to ensure that the advancement of the
|
||||||
|
// cursor matches the number of bytes written
|
||||||
unsafe {
|
unsafe {
|
||||||
|
// use upper Camel-Case
|
||||||
if camel_case {
|
if camel_case {
|
||||||
write_camel_case(k, from_raw_parts_mut(buf, k_len))
|
write_camel_case(k, from_raw_parts_mut(buf, k_len))
|
||||||
} else {
|
} else {
|
||||||
write_data(k, buf, k_len)
|
write_data(k, buf, k_len)
|
||||||
}
|
}
|
||||||
|
|
||||||
buf = buf.add(k_len);
|
buf = buf.add(k_len);
|
||||||
|
|
||||||
write_data(b": ", buf, 2);
|
write_data(b": ", buf, 2);
|
||||||
buf = buf.add(2);
|
buf = buf.add(2);
|
||||||
|
|
||||||
write_data(v, buf, v_len);
|
write_data(v, buf, v_len);
|
||||||
buf = buf.add(v_len);
|
buf = buf.add(v_len);
|
||||||
|
|
||||||
write_data(b"\r\n", buf, 2);
|
write_data(b"\r\n", buf, 2);
|
||||||
buf = buf.add(2);
|
buf = buf.add(2);
|
||||||
pos += len;
|
|
||||||
remaining -= len;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pos += len;
|
||||||
|
remaining -= len;
|
||||||
}
|
}
|
||||||
|
|
||||||
map::Value::Multi(ref vec) => {
|
map::Value::Multi(ref vec) => {
|
||||||
for val in vec {
|
for val in vec {
|
||||||
let v = val.as_ref();
|
let v = val.as_ref();
|
||||||
let v_len = v.len();
|
let v_len = v.len();
|
||||||
let k_len = k.len();
|
|
||||||
let len = k_len + v_len + 4;
|
let len = k_len + v_len + 4;
|
||||||
|
|
||||||
if len > remaining {
|
if len > remaining {
|
||||||
|
// SAFETY: all the bytes written up to position "pos" are initialized
|
||||||
|
// the written byte count and pointer advancement are kept in sync
|
||||||
unsafe {
|
unsafe {
|
||||||
dst.advance_mut(pos);
|
dst.advance_mut(pos);
|
||||||
}
|
}
|
||||||
pos = 0;
|
pos = 0;
|
||||||
dst.reserve(len * 2);
|
dst.reserve(len * 2);
|
||||||
remaining = dst.capacity() - dst.len();
|
remaining = dst.capacity() - dst.len();
|
||||||
|
|
||||||
|
// re-assign buf raw pointer since it's possible that the buffer was
|
||||||
|
// reallocated and/or resized
|
||||||
buf = dst.bytes_mut().as_mut_ptr() as *mut u8;
|
buf = dst.bytes_mut().as_mut_ptr() as *mut u8;
|
||||||
}
|
}
|
||||||
// use upper Camel-Case
|
|
||||||
|
// SAFETY: on each write, it is enough to ensure that the advancement of
|
||||||
|
// the cursor matches the number of bytes written
|
||||||
unsafe {
|
unsafe {
|
||||||
if camel_case {
|
if camel_case {
|
||||||
write_camel_case(k, from_raw_parts_mut(buf, k_len));
|
write_camel_case(k, from_raw_parts_mut(buf, k_len));
|
||||||
} else {
|
} else {
|
||||||
write_data(k, buf, k_len);
|
write_data(k, buf, k_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
buf = buf.add(k_len);
|
buf = buf.add(k_len);
|
||||||
|
|
||||||
write_data(b": ", buf, 2);
|
write_data(b": ", buf, 2);
|
||||||
buf = buf.add(2);
|
buf = buf.add(2);
|
||||||
|
|
||||||
write_data(v, buf, v_len);
|
write_data(v, buf, v_len);
|
||||||
buf = buf.add(v_len);
|
buf = buf.add(v_len);
|
||||||
|
|
||||||
write_data(b"\r\n", buf, 2);
|
write_data(b"\r\n", buf, 2);
|
||||||
buf = buf.add(2);
|
buf = buf.add(2);
|
||||||
};
|
};
|
||||||
|
|
||||||
pos += len;
|
pos += len;
|
||||||
remaining -= len;
|
remaining -= len;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// final cursor synchronization with the bytes container
|
||||||
|
//
|
||||||
|
// SAFETY: all the bytes written up to position "pos" are initialized
|
||||||
|
// the written byte count and pointer advancement are kept in sync
|
||||||
unsafe {
|
unsafe {
|
||||||
dst.advance_mut(pos);
|
dst.advance_mut(pos);
|
||||||
}
|
}
|
||||||
@ -477,7 +521,10 @@ impl<'a> io::Write for Writer<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
/// Callers must ensure that the given length matches given value length.
|
||||||
unsafe fn write_data(value: &[u8], buf: *mut u8, len: usize) {
|
unsafe fn write_data(value: &[u8], buf: *mut u8, len: usize) {
|
||||||
|
debug_assert_eq!(value.len(), len);
|
||||||
copy_nonoverlapping(value.as_ptr(), buf, len);
|
copy_nonoverlapping(value.as_ptr(), buf, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,7 +98,7 @@ mod openssl {
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use actix_tls::openssl::{Acceptor, SslAcceptor, SslStream};
|
use actix_tls::openssl::{Acceptor, SslAcceptor, SslStream};
|
||||||
use actix_tls::{openssl::HandshakeError, SslError};
|
use actix_tls::{openssl::HandshakeError, TlsError};
|
||||||
|
|
||||||
impl<S, B, X, U> H1Service<SslStream<TcpStream>, S, B, X, U>
|
impl<S, B, X, U> H1Service<SslStream<TcpStream>, S, B, X, U>
|
||||||
where
|
where
|
||||||
@ -126,19 +126,19 @@ mod openssl {
|
|||||||
Config = (),
|
Config = (),
|
||||||
Request = TcpStream,
|
Request = TcpStream,
|
||||||
Response = (),
|
Response = (),
|
||||||
Error = SslError<HandshakeError<TcpStream>, DispatchError>,
|
Error = TlsError<HandshakeError<TcpStream>, DispatchError>,
|
||||||
InitError = (),
|
InitError = (),
|
||||||
> {
|
> {
|
||||||
pipeline_factory(
|
pipeline_factory(
|
||||||
Acceptor::new(acceptor)
|
Acceptor::new(acceptor)
|
||||||
.map_err(SslError::Ssl)
|
.map_err(TlsError::Tls)
|
||||||
.map_init_err(|_| panic!()),
|
.map_init_err(|_| panic!()),
|
||||||
)
|
)
|
||||||
.and_then(|io: SslStream<TcpStream>| {
|
.and_then(|io: SslStream<TcpStream>| {
|
||||||
let peer_addr = io.get_ref().peer_addr().ok();
|
let peer_addr = io.get_ref().peer_addr().ok();
|
||||||
ok((io, peer_addr))
|
ok((io, peer_addr))
|
||||||
})
|
})
|
||||||
.and_then(self.map_err(SslError::Service))
|
.and_then(self.map_err(TlsError::Service))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -147,7 +147,7 @@ mod openssl {
|
|||||||
mod rustls {
|
mod rustls {
|
||||||
use super::*;
|
use super::*;
|
||||||
use actix_tls::rustls::{Acceptor, ServerConfig, TlsStream};
|
use actix_tls::rustls::{Acceptor, ServerConfig, TlsStream};
|
||||||
use actix_tls::SslError;
|
use actix_tls::TlsError;
|
||||||
use std::{fmt, io};
|
use std::{fmt, io};
|
||||||
|
|
||||||
impl<S, B, X, U> H1Service<TlsStream<TcpStream>, S, B, X, U>
|
impl<S, B, X, U> H1Service<TlsStream<TcpStream>, S, B, X, U>
|
||||||
@ -176,19 +176,19 @@ mod rustls {
|
|||||||
Config = (),
|
Config = (),
|
||||||
Request = TcpStream,
|
Request = TcpStream,
|
||||||
Response = (),
|
Response = (),
|
||||||
Error = SslError<io::Error, DispatchError>,
|
Error = TlsError<io::Error, DispatchError>,
|
||||||
InitError = (),
|
InitError = (),
|
||||||
> {
|
> {
|
||||||
pipeline_factory(
|
pipeline_factory(
|
||||||
Acceptor::new(config)
|
Acceptor::new(config)
|
||||||
.map_err(SslError::Ssl)
|
.map_err(TlsError::Tls)
|
||||||
.map_init_err(|_| panic!()),
|
.map_init_err(|_| panic!()),
|
||||||
)
|
)
|
||||||
.and_then(|io: TlsStream<TcpStream>| {
|
.and_then(|io: TlsStream<TcpStream>| {
|
||||||
let peer_addr = io.get_ref().0.peer_addr().ok();
|
let peer_addr = io.get_ref().0.peer_addr().ok();
|
||||||
ok((io, peer_addr))
|
ok((io, peer_addr))
|
||||||
})
|
})
|
||||||
.and_then(self.map_err(SslError::Service))
|
.and_then(self.map_err(TlsError::Service))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -548,10 +548,12 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
|
#[pin_project::pin_project]
|
||||||
pub struct OneRequestServiceResponse<T>
|
pub struct OneRequestServiceResponse<T>
|
||||||
where
|
where
|
||||||
T: AsyncRead + AsyncWrite + Unpin,
|
T: AsyncRead + AsyncWrite + Unpin,
|
||||||
{
|
{
|
||||||
|
#[pin]
|
||||||
framed: Option<Framed<T, Codec>>,
|
framed: Option<Framed<T, Codec>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -562,16 +564,18 @@ where
|
|||||||
type Output = Result<(Request, Framed<T, Codec>), ParseError>;
|
type Output = Result<(Request, Framed<T, Codec>), ParseError>;
|
||||||
|
|
||||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
match self.framed.as_mut().unwrap().next_item(cx) {
|
let this = self.as_mut().project();
|
||||||
Poll::Ready(Some(Ok(req))) => match req {
|
|
||||||
|
match ready!(this.framed.as_pin_mut().unwrap().next_item(cx)) {
|
||||||
|
Some(Ok(req)) => match req {
|
||||||
Message::Item(req) => {
|
Message::Item(req) => {
|
||||||
Poll::Ready(Ok((req, self.framed.take().unwrap())))
|
let mut this = self.as_mut().project();
|
||||||
|
Poll::Ready(Ok((req, this.framed.take().unwrap())))
|
||||||
}
|
}
|
||||||
Message::Chunk(_) => unreachable!("Something is wrong"),
|
Message::Chunk(_) => unreachable!("Something is wrong"),
|
||||||
},
|
},
|
||||||
Poll::Ready(Some(Err(err))) => Poll::Ready(Err(err)),
|
Some(Err(err)) => Poll::Ready(Err(err)),
|
||||||
Poll::Ready(None) => Poll::Ready(Err(ParseError::Incomplete)),
|
None => Poll::Ready(Err(ParseError::Incomplete)),
|
||||||
Poll::Pending => Poll::Pending,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,12 +9,13 @@ use crate::error::Error;
|
|||||||
use crate::h1::{Codec, Message};
|
use crate::h1::{Codec, Message};
|
||||||
use crate::response::Response;
|
use crate::response::Response;
|
||||||
|
|
||||||
/// Send http/1 response
|
/// Send HTTP/1 response
|
||||||
#[pin_project::pin_project]
|
#[pin_project::pin_project]
|
||||||
pub struct SendResponse<T, B> {
|
pub struct SendResponse<T, B> {
|
||||||
res: Option<Message<(Response<()>, BodySize)>>,
|
res: Option<Message<(Response<()>, BodySize)>>,
|
||||||
#[pin]
|
#[pin]
|
||||||
body: Option<ResponseBody<B>>,
|
body: Option<ResponseBody<B>>,
|
||||||
|
#[pin]
|
||||||
framed: Option<Framed<T, Codec>>,
|
framed: Option<Framed<T, Codec>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,23 +36,30 @@ where
|
|||||||
|
|
||||||
impl<T, B> Future for SendResponse<T, B>
|
impl<T, B> Future for SendResponse<T, B>
|
||||||
where
|
where
|
||||||
T: AsyncRead + AsyncWrite,
|
T: AsyncRead + AsyncWrite + Unpin,
|
||||||
B: MessageBody + Unpin,
|
B: MessageBody + Unpin,
|
||||||
{
|
{
|
||||||
type Output = Result<Framed<T, Codec>, Error>;
|
type Output = Result<Framed<T, Codec>, Error>;
|
||||||
|
|
||||||
// TODO: rethink if we need loops in polls
|
// TODO: rethink if we need loops in polls
|
||||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
let mut this = self.project();
|
let mut this = self.as_mut().project();
|
||||||
|
|
||||||
let mut body_done = this.body.is_none();
|
let mut body_done = this.body.is_none();
|
||||||
loop {
|
loop {
|
||||||
let mut body_ready = !body_done;
|
let mut body_ready = !body_done;
|
||||||
let framed = this.framed.as_mut().unwrap();
|
|
||||||
|
|
||||||
// send body
|
// send body
|
||||||
if this.res.is_none() && body_ready {
|
if this.res.is_none() && body_ready {
|
||||||
while body_ready && !body_done && !framed.is_write_buf_full() {
|
while body_ready
|
||||||
|
&& !body_done
|
||||||
|
&& !this
|
||||||
|
.framed
|
||||||
|
.as_ref()
|
||||||
|
.as_pin_ref()
|
||||||
|
.unwrap()
|
||||||
|
.is_write_buf_full()
|
||||||
|
{
|
||||||
match this.body.as_mut().as_pin_mut().unwrap().poll_next(cx)? {
|
match this.body.as_mut().as_pin_mut().unwrap().poll_next(cx)? {
|
||||||
Poll::Ready(item) => {
|
Poll::Ready(item) => {
|
||||||
// body is done when item is None
|
// body is done when item is None
|
||||||
@ -59,6 +67,7 @@ where
|
|||||||
if body_done {
|
if body_done {
|
||||||
let _ = this.body.take();
|
let _ = this.body.take();
|
||||||
}
|
}
|
||||||
|
let framed = this.framed.as_mut().as_pin_mut().unwrap();
|
||||||
framed.write(Message::Chunk(item))?;
|
framed.write(Message::Chunk(item))?;
|
||||||
}
|
}
|
||||||
Poll::Pending => body_ready = false,
|
Poll::Pending => body_ready = false,
|
||||||
@ -66,6 +75,8 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let framed = this.framed.as_mut().as_pin_mut().unwrap();
|
||||||
|
|
||||||
// flush write buffer
|
// flush write buffer
|
||||||
if !framed.is_write_buf_empty() {
|
if !framed.is_write_buf_empty() {
|
||||||
match framed.flush(cx)? {
|
match framed.flush(cx)? {
|
||||||
@ -96,6 +107,9 @@ where
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Poll::Ready(Ok(this.framed.take().unwrap()))
|
|
||||||
|
let framed = this.framed.take().unwrap();
|
||||||
|
|
||||||
|
Poll::Ready(Ok(framed))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -227,9 +227,11 @@ where
|
|||||||
if !has_date {
|
if !has_date {
|
||||||
let mut bytes = BytesMut::with_capacity(29);
|
let mut bytes = BytesMut::with_capacity(29);
|
||||||
self.config.set_date_header(&mut bytes);
|
self.config.set_date_header(&mut bytes);
|
||||||
res.headers_mut().insert(DATE, unsafe {
|
res.headers_mut().insert(
|
||||||
HeaderValue::from_maybe_shared_unchecked(bytes.freeze())
|
DATE,
|
||||||
});
|
// SAFETY: serialized date-times are known ASCII strings
|
||||||
|
unsafe { HeaderValue::from_maybe_shared_unchecked(bytes.freeze()) },
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
res
|
res
|
||||||
@ -303,55 +305,59 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ServiceResponseStateProj::SendPayload(ref mut stream, ref mut body) => loop {
|
ServiceResponseStateProj::SendPayload(ref mut stream, ref mut body) => {
|
||||||
loop {
|
loop {
|
||||||
if let Some(ref mut buffer) = this.buffer {
|
loop {
|
||||||
match stream.poll_capacity(cx) {
|
if let Some(ref mut buffer) = this.buffer {
|
||||||
Poll::Pending => return Poll::Pending,
|
match stream.poll_capacity(cx) {
|
||||||
Poll::Ready(None) => return Poll::Ready(()),
|
Poll::Pending => return Poll::Pending,
|
||||||
Poll::Ready(Some(Ok(cap))) => {
|
Poll::Ready(None) => return Poll::Ready(()),
|
||||||
let len = buffer.len();
|
Poll::Ready(Some(Ok(cap))) => {
|
||||||
let bytes = buffer.split_to(std::cmp::min(cap, len));
|
let len = buffer.len();
|
||||||
|
let bytes = buffer.split_to(std::cmp::min(cap, len));
|
||||||
|
|
||||||
if let Err(e) = stream.send_data(bytes, false) {
|
if let Err(e) = stream.send_data(bytes, false) {
|
||||||
|
warn!("{:?}", e);
|
||||||
|
return Poll::Ready(());
|
||||||
|
} else if !buffer.is_empty() {
|
||||||
|
let cap =
|
||||||
|
std::cmp::min(buffer.len(), CHUNK_SIZE);
|
||||||
|
stream.reserve_capacity(cap);
|
||||||
|
} else {
|
||||||
|
this.buffer.take();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Poll::Ready(Some(Err(e))) => {
|
||||||
warn!("{:?}", e);
|
warn!("{:?}", e);
|
||||||
return Poll::Ready(());
|
return Poll::Ready(());
|
||||||
} else if !buffer.is_empty() {
|
|
||||||
let cap = std::cmp::min(buffer.len(), CHUNK_SIZE);
|
|
||||||
stream.reserve_capacity(cap);
|
|
||||||
} else {
|
|
||||||
this.buffer.take();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Poll::Ready(Some(Err(e))) => {
|
} else {
|
||||||
warn!("{:?}", e);
|
match body.as_mut().poll_next(cx) {
|
||||||
return Poll::Ready(());
|
Poll::Pending => return Poll::Pending,
|
||||||
}
|
Poll::Ready(None) => {
|
||||||
}
|
if let Err(e) = stream.send_data(Bytes::new(), true)
|
||||||
} else {
|
{
|
||||||
match body.as_mut().poll_next(cx) {
|
warn!("{:?}", e);
|
||||||
Poll::Pending => return Poll::Pending,
|
}
|
||||||
Poll::Ready(None) => {
|
return Poll::Ready(());
|
||||||
if let Err(e) = stream.send_data(Bytes::new(), true) {
|
}
|
||||||
warn!("{:?}", e);
|
Poll::Ready(Some(Ok(chunk))) => {
|
||||||
|
stream.reserve_capacity(std::cmp::min(
|
||||||
|
chunk.len(),
|
||||||
|
CHUNK_SIZE,
|
||||||
|
));
|
||||||
|
*this.buffer = Some(chunk);
|
||||||
|
}
|
||||||
|
Poll::Ready(Some(Err(e))) => {
|
||||||
|
error!("Response payload stream error: {:?}", e);
|
||||||
|
return Poll::Ready(());
|
||||||
}
|
}
|
||||||
return Poll::Ready(());
|
|
||||||
}
|
|
||||||
Poll::Ready(Some(Ok(chunk))) => {
|
|
||||||
stream.reserve_capacity(std::cmp::min(
|
|
||||||
chunk.len(),
|
|
||||||
CHUNK_SIZE,
|
|
||||||
));
|
|
||||||
*this.buffer = Some(chunk);
|
|
||||||
}
|
|
||||||
Poll::Ready(Some(Err(e))) => {
|
|
||||||
error!("Response payload stream error: {:?}", e);
|
|
||||||
return Poll::Ready(());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -97,7 +97,7 @@ where
|
|||||||
mod openssl {
|
mod openssl {
|
||||||
use actix_service::{fn_factory, fn_service};
|
use actix_service::{fn_factory, fn_service};
|
||||||
use actix_tls::openssl::{Acceptor, SslAcceptor, SslStream};
|
use actix_tls::openssl::{Acceptor, SslAcceptor, SslStream};
|
||||||
use actix_tls::{openssl::HandshakeError, SslError};
|
use actix_tls::{openssl::HandshakeError, TlsError};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
@ -117,12 +117,12 @@ mod openssl {
|
|||||||
Config = (),
|
Config = (),
|
||||||
Request = TcpStream,
|
Request = TcpStream,
|
||||||
Response = (),
|
Response = (),
|
||||||
Error = SslError<HandshakeError<TcpStream>, DispatchError>,
|
Error = TlsError<HandshakeError<TcpStream>, DispatchError>,
|
||||||
InitError = S::InitError,
|
InitError = S::InitError,
|
||||||
> {
|
> {
|
||||||
pipeline_factory(
|
pipeline_factory(
|
||||||
Acceptor::new(acceptor)
|
Acceptor::new(acceptor)
|
||||||
.map_err(SslError::Ssl)
|
.map_err(TlsError::Tls)
|
||||||
.map_init_err(|_| panic!()),
|
.map_init_err(|_| panic!()),
|
||||||
)
|
)
|
||||||
.and_then(fn_factory(|| {
|
.and_then(fn_factory(|| {
|
||||||
@ -131,7 +131,7 @@ mod openssl {
|
|||||||
ok((io, peer_addr))
|
ok((io, peer_addr))
|
||||||
}))
|
}))
|
||||||
}))
|
}))
|
||||||
.and_then(self.map_err(SslError::Service))
|
.and_then(self.map_err(TlsError::Service))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -140,7 +140,7 @@ mod openssl {
|
|||||||
mod rustls {
|
mod rustls {
|
||||||
use super::*;
|
use super::*;
|
||||||
use actix_tls::rustls::{Acceptor, ServerConfig, TlsStream};
|
use actix_tls::rustls::{Acceptor, ServerConfig, TlsStream};
|
||||||
use actix_tls::SslError;
|
use actix_tls::TlsError;
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
impl<S, B> H2Service<TlsStream<TcpStream>, S, B>
|
impl<S, B> H2Service<TlsStream<TcpStream>, S, B>
|
||||||
@ -159,7 +159,7 @@ mod rustls {
|
|||||||
Config = (),
|
Config = (),
|
||||||
Request = TcpStream,
|
Request = TcpStream,
|
||||||
Response = (),
|
Response = (),
|
||||||
Error = SslError<io::Error, DispatchError>,
|
Error = TlsError<io::Error, DispatchError>,
|
||||||
InitError = S::InitError,
|
InitError = S::InitError,
|
||||||
> {
|
> {
|
||||||
let protos = vec!["h2".to_string().into()];
|
let protos = vec!["h2".to_string().into()];
|
||||||
@ -167,7 +167,7 @@ mod rustls {
|
|||||||
|
|
||||||
pipeline_factory(
|
pipeline_factory(
|
||||||
Acceptor::new(config)
|
Acceptor::new(config)
|
||||||
.map_err(SslError::Ssl)
|
.map_err(TlsError::Tls)
|
||||||
.map_init_err(|_| panic!()),
|
.map_init_err(|_| panic!()),
|
||||||
)
|
)
|
||||||
.and_then(fn_factory(|| {
|
.and_then(fn_factory(|| {
|
||||||
@ -176,7 +176,7 @@ mod rustls {
|
|||||||
ok((io, peer_addr))
|
ok((io, peer_addr))
|
||||||
}))
|
}))
|
||||||
}))
|
}))
|
||||||
.and_then(self.map_err(SslError::Service))
|
.and_then(self.map_err(TlsError::Service))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -283,11 +283,11 @@ impl DispositionParam {
|
|||||||
/// Some("\u{1f600}.svg".as_bytes()));
|
/// Some("\u{1f600}.svg".as_bytes()));
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// # WARN
|
/// # Security Note
|
||||||
|
///
|
||||||
/// If "filename" parameter is supplied, do not use the file name blindly, check and possibly
|
/// If "filename" parameter is supplied, do not use the file name blindly, check and possibly
|
||||||
/// change to match local file system conventions if applicable, and do not use directory path
|
/// change to match local file system conventions if applicable, and do not use directory path
|
||||||
/// information that may be present. See [RFC2183](https://tools.ietf.org/html/rfc2183#section-2.3)
|
/// information that may be present. See [RFC2183](https://tools.ietf.org/html/rfc2183#section-2.3).
|
||||||
/// .
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct ContentDisposition {
|
pub struct ContentDisposition {
|
||||||
/// The disposition type
|
/// The disposition type
|
||||||
@ -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();
|
||||||
@ -118,7 +120,7 @@ macro_rules! test_header {
|
|||||||
// Test formatting
|
// Test formatting
|
||||||
if typed.is_some() {
|
if typed.is_some() {
|
||||||
let raw = &($raw)[..];
|
let raw = &($raw)[..];
|
||||||
let mut iter = raw.iter().map(|b|str::from_utf8(&b[..]).unwrap());
|
let mut iter = raw.iter().map(|b| str::from_utf8(&b[..]).unwrap());
|
||||||
let mut joined = String::new();
|
let mut joined = String::new();
|
||||||
joined.push_str(iter.next().unwrap());
|
joined.push_str(iter.next().unwrap());
|
||||||
for s in iter {
|
for s in iter {
|
||||||
@ -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;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
//! Basic http primitives for actix-net framework.
|
//! Basic http primitives for actix-net framework.
|
||||||
#![warn(rust_2018_idioms, warnings)]
|
|
||||||
|
#![deny(rust_2018_idioms)]
|
||||||
#![allow(
|
#![allow(
|
||||||
clippy::type_complexity,
|
clippy::type_complexity,
|
||||||
clippy::too_many_arguments,
|
clippy::too_many_arguments,
|
||||||
|
@ -38,7 +38,7 @@ macro_rules! downcast {
|
|||||||
/// Downcasts generic body to a specific type.
|
/// Downcasts generic body to a specific type.
|
||||||
pub fn downcast_ref<T: $name + 'static>(&self) -> Option<&T> {
|
pub fn downcast_ref<T: $name + 'static>(&self) -> Option<&T> {
|
||||||
if self.__private_get_type_id__().0 == std::any::TypeId::of::<T>() {
|
if self.__private_get_type_id__().0 == std::any::TypeId::of::<T>() {
|
||||||
// Safety: external crates cannot override the default
|
// SAFETY: external crates cannot override the default
|
||||||
// implementation of `__private_get_type_id__`, since
|
// implementation of `__private_get_type_id__`, since
|
||||||
// it requires returning a private type. We can therefore
|
// it requires returning a private type. We can therefore
|
||||||
// rely on the returned `TypeId`, which ensures that this
|
// rely on the returned `TypeId`, which ensures that this
|
||||||
@ -48,10 +48,11 @@ macro_rules! downcast {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Downcasts a generic body to a mutable specific type.
|
/// Downcasts a generic body to a mutable specific type.
|
||||||
pub fn downcast_mut<T: $name + 'static>(&mut self) -> Option<&mut T> {
|
pub fn downcast_mut<T: $name + 'static>(&mut self) -> Option<&mut T> {
|
||||||
if self.__private_get_type_id__().0 == std::any::TypeId::of::<T>() {
|
if self.__private_get_type_id__().0 == std::any::TypeId::of::<T>() {
|
||||||
// Safety: external crates cannot override the default
|
// SAFETY: external crates cannot override the default
|
||||||
// implementation of `__private_get_type_id__`, since
|
// implementation of `__private_get_type_id__`, since
|
||||||
// it requires returning a private type. We can therefore
|
// it requires returning a private type. We can therefore
|
||||||
// rely on the returned `TypeId`, which ensures that this
|
// rely on the returned `TypeId`, which ensures that this
|
||||||
@ -86,7 +87,7 @@ mod tests {
|
|||||||
let body = resp_body.downcast_ref::<String>().unwrap();
|
let body = resp_body.downcast_ref::<String>().unwrap();
|
||||||
assert_eq!(body, "hello cast");
|
assert_eq!(body, "hello cast");
|
||||||
let body = &mut resp_body.downcast_mut::<String>().unwrap();
|
let body = &mut resp_body.downcast_mut::<String>().unwrap();
|
||||||
body.push_str("!");
|
body.push('!');
|
||||||
let body = resp_body.downcast_ref::<String>().unwrap();
|
let body = resp_body.downcast_ref::<String>().unwrap();
|
||||||
assert_eq!(body, "hello cast!");
|
assert_eq!(body, "hello cast!");
|
||||||
let not_body = resp_body.downcast_ref::<()>();
|
let not_body = resp_body.downcast_ref::<()>();
|
||||||
|
@ -195,7 +195,7 @@ where
|
|||||||
mod openssl {
|
mod openssl {
|
||||||
use super::*;
|
use super::*;
|
||||||
use actix_tls::openssl::{Acceptor, SslAcceptor, SslStream};
|
use actix_tls::openssl::{Acceptor, SslAcceptor, SslStream};
|
||||||
use actix_tls::{openssl::HandshakeError, SslError};
|
use actix_tls::{openssl::HandshakeError, TlsError};
|
||||||
|
|
||||||
impl<S, B, X, U> HttpService<SslStream<TcpStream>, S, B, X, U>
|
impl<S, B, X, U> HttpService<SslStream<TcpStream>, S, B, X, U>
|
||||||
where
|
where
|
||||||
@ -226,12 +226,12 @@ mod openssl {
|
|||||||
Config = (),
|
Config = (),
|
||||||
Request = TcpStream,
|
Request = TcpStream,
|
||||||
Response = (),
|
Response = (),
|
||||||
Error = SslError<HandshakeError<TcpStream>, DispatchError>,
|
Error = TlsError<HandshakeError<TcpStream>, DispatchError>,
|
||||||
InitError = (),
|
InitError = (),
|
||||||
> {
|
> {
|
||||||
pipeline_factory(
|
pipeline_factory(
|
||||||
Acceptor::new(acceptor)
|
Acceptor::new(acceptor)
|
||||||
.map_err(SslError::Ssl)
|
.map_err(TlsError::Tls)
|
||||||
.map_init_err(|_| panic!()),
|
.map_init_err(|_| panic!()),
|
||||||
)
|
)
|
||||||
.and_then(|io: SslStream<TcpStream>| {
|
.and_then(|io: SslStream<TcpStream>| {
|
||||||
@ -247,7 +247,7 @@ mod openssl {
|
|||||||
let peer_addr = io.get_ref().peer_addr().ok();
|
let peer_addr = io.get_ref().peer_addr().ok();
|
||||||
ok((io, proto, peer_addr))
|
ok((io, proto, peer_addr))
|
||||||
})
|
})
|
||||||
.and_then(self.map_err(SslError::Service))
|
.and_then(self.map_err(TlsError::Service))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -256,7 +256,7 @@ mod openssl {
|
|||||||
mod rustls {
|
mod rustls {
|
||||||
use super::*;
|
use super::*;
|
||||||
use actix_tls::rustls::{Acceptor, ServerConfig, Session, TlsStream};
|
use actix_tls::rustls::{Acceptor, ServerConfig, Session, TlsStream};
|
||||||
use actix_tls::SslError;
|
use actix_tls::TlsError;
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
impl<S, B, X, U> HttpService<TlsStream<TcpStream>, S, B, X, U>
|
impl<S, B, X, U> HttpService<TlsStream<TcpStream>, S, B, X, U>
|
||||||
@ -288,7 +288,7 @@ mod rustls {
|
|||||||
Config = (),
|
Config = (),
|
||||||
Request = TcpStream,
|
Request = TcpStream,
|
||||||
Response = (),
|
Response = (),
|
||||||
Error = SslError<io::Error, DispatchError>,
|
Error = TlsError<io::Error, DispatchError>,
|
||||||
InitError = (),
|
InitError = (),
|
||||||
> {
|
> {
|
||||||
let protos = vec!["h2".to_string().into(), "http/1.1".to_string().into()];
|
let protos = vec!["h2".to_string().into(), "http/1.1".to_string().into()];
|
||||||
@ -296,7 +296,7 @@ mod rustls {
|
|||||||
|
|
||||||
pipeline_factory(
|
pipeline_factory(
|
||||||
Acceptor::new(config)
|
Acceptor::new(config)
|
||||||
.map_err(SslError::Ssl)
|
.map_err(TlsError::Tls)
|
||||||
.map_init_err(|_| panic!()),
|
.map_init_err(|_| panic!()),
|
||||||
)
|
)
|
||||||
.and_then(|io: TlsStream<TcpStream>| {
|
.and_then(|io: TlsStream<TcpStream>| {
|
||||||
@ -312,7 +312,7 @@ mod rustls {
|
|||||||
let peer_addr = io.get_ref().0.peer_addr().ok();
|
let peer_addr = io.get_ref().0.peer_addr().ok();
|
||||||
ok((io, proto, peer_addr))
|
ok((io, proto, peer_addr))
|
||||||
})
|
})
|
||||||
.and_then(self.map_err(SslError::Service))
|
.and_then(self.map_err(TlsError::Service))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -91,8 +91,7 @@ impl Codec {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Encoder for Codec {
|
impl Encoder<Message> for Codec {
|
||||||
type Item = Message;
|
|
||||||
type Error = ProtocolError;
|
type Error = ProtocolError;
|
||||||
|
|
||||||
fn encode(&mut self, item: Message, dst: &mut BytesMut) -> Result<(), Self::Error> {
|
fn encode(&mut self, item: Message, dst: &mut BytesMut) -> Result<(), Self::Error> {
|
||||||
|
@ -4,16 +4,18 @@ use std::task::{Context, Poll};
|
|||||||
|
|
||||||
use actix_codec::{AsyncRead, AsyncWrite, Framed};
|
use actix_codec::{AsyncRead, AsyncWrite, Framed};
|
||||||
use actix_service::{IntoService, Service};
|
use actix_service::{IntoService, Service};
|
||||||
use actix_utils::framed;
|
use actix_utils::dispatcher::{Dispatcher as InnerDispatcher, DispatcherError};
|
||||||
|
|
||||||
use super::{Codec, Frame, Message};
|
use super::{Codec, Frame, Message};
|
||||||
|
|
||||||
|
#[pin_project::pin_project]
|
||||||
pub struct Dispatcher<S, T>
|
pub struct Dispatcher<S, T>
|
||||||
where
|
where
|
||||||
S: Service<Request = Frame, Response = Message> + 'static,
|
S: Service<Request = Frame, Response = Message> + 'static,
|
||||||
T: AsyncRead + AsyncWrite,
|
T: AsyncRead + AsyncWrite,
|
||||||
{
|
{
|
||||||
inner: framed::Dispatcher<S, T, Codec>,
|
#[pin]
|
||||||
|
inner: InnerDispatcher<S, T, Codec, Message>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S, T> Dispatcher<S, T>
|
impl<S, T> Dispatcher<S, T>
|
||||||
@ -25,13 +27,13 @@ where
|
|||||||
{
|
{
|
||||||
pub fn new<F: IntoService<S>>(io: T, service: F) -> Self {
|
pub fn new<F: IntoService<S>>(io: T, service: F) -> Self {
|
||||||
Dispatcher {
|
Dispatcher {
|
||||||
inner: framed::Dispatcher::new(Framed::new(io, Codec::new()), service),
|
inner: InnerDispatcher::new(Framed::new(io, Codec::new()), service),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with<F: IntoService<S>>(framed: Framed<T, Codec>, service: F) -> Self {
|
pub fn with<F: IntoService<S>>(framed: Framed<T, Codec>, service: F) -> Self {
|
||||||
Dispatcher {
|
Dispatcher {
|
||||||
inner: framed::Dispatcher::new(framed, service),
|
inner: InnerDispatcher::new(framed, service),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -43,9 +45,9 @@ where
|
|||||||
S::Future: 'static,
|
S::Future: 'static,
|
||||||
S::Error: 'static,
|
S::Error: 'static,
|
||||||
{
|
{
|
||||||
type Output = Result<(), framed::DispatcherError<S::Error, Codec>>;
|
type Output = Result<(), DispatcherError<S::Error, Codec, Message>>;
|
||||||
|
|
||||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
Pin::new(&mut self.inner).poll(cx)
|
self.project().inner.poll(cx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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(
|
||||||
|
@ -7,6 +7,8 @@ use std::slice;
|
|||||||
struct ShortSlice<'a>(&'a mut [u8]);
|
struct ShortSlice<'a>(&'a mut [u8]);
|
||||||
|
|
||||||
impl<'a> ShortSlice<'a> {
|
impl<'a> ShortSlice<'a> {
|
||||||
|
/// # Safety
|
||||||
|
/// Given slice must be shorter than 8 bytes.
|
||||||
unsafe fn new(slice: &'a mut [u8]) -> Self {
|
unsafe fn new(slice: &'a mut [u8]) -> Self {
|
||||||
// Sanity check for debug builds
|
// Sanity check for debug builds
|
||||||
debug_assert!(slice.len() < 8);
|
debug_assert!(slice.len() < 8);
|
||||||
@ -46,13 +48,13 @@ pub(crate) fn apply_mask(buf: &mut [u8], mask_u32: u32) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
// TODO: copy_nonoverlapping here compiles to call memcpy. While it is not so
|
// TODO: copy_nonoverlapping here compiles to call memcpy. While it is not so
|
||||||
// inefficient, it could be done better. The compiler does not understand that
|
// inefficient, it could be done better. The compiler does not understand that
|
||||||
// a `ShortSlice` must be smaller than a u64.
|
// a `ShortSlice` must be smaller than a u64.
|
||||||
|
#[inline]
|
||||||
#[allow(clippy::needless_pass_by_value)]
|
#[allow(clippy::needless_pass_by_value)]
|
||||||
fn xor_short(buf: ShortSlice<'_>, mask: u64) {
|
fn xor_short(buf: ShortSlice<'_>, mask: u64) {
|
||||||
// Unsafe: we know that a `ShortSlice` fits in a u64
|
// SAFETY: we know that a `ShortSlice` fits in a u64
|
||||||
unsafe {
|
unsafe {
|
||||||
let (ptr, len) = (buf.0.as_mut_ptr(), buf.0.len());
|
let (ptr, len) = (buf.0.as_mut_ptr(), buf.0.len());
|
||||||
let mut b: u64 = 0;
|
let mut b: u64 = 0;
|
||||||
@ -64,8 +66,9 @@ fn xor_short(buf: ShortSlice<'_>, mask: u64) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
/// Caller must ensure the buffer has the correct size and alignment.
|
||||||
#[inline]
|
#[inline]
|
||||||
// Unsafe: caller must ensure the buffer has the correct size and alignment
|
|
||||||
unsafe fn cast_slice(buf: &mut [u8]) -> &mut [u64] {
|
unsafe fn cast_slice(buf: &mut [u8]) -> &mut [u64] {
|
||||||
// Assert correct size and alignment in debug builds
|
// Assert correct size and alignment in debug builds
|
||||||
debug_assert!(buf.len().trailing_zeros() >= 3);
|
debug_assert!(buf.len().trailing_zeros() >= 3);
|
||||||
@ -74,9 +77,9 @@ unsafe fn cast_slice(buf: &mut [u8]) -> &mut [u64] {
|
|||||||
slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut u64, buf.len() >> 3)
|
slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut u64, buf.len() >> 3)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
// Splits a slice into three parts: an unaligned short head and tail, plus an aligned
|
// Splits a slice into three parts: an unaligned short head and tail, plus an aligned
|
||||||
// u64 mid section.
|
// u64 mid section.
|
||||||
|
#[inline]
|
||||||
fn align_buf(buf: &mut [u8]) -> (ShortSlice<'_>, &mut [u64], ShortSlice<'_>) {
|
fn align_buf(buf: &mut [u8]) -> (ShortSlice<'_>, &mut [u64], ShortSlice<'_>) {
|
||||||
let start_ptr = buf.as_ptr() as usize;
|
let start_ptr = buf.as_ptr() as usize;
|
||||||
let end_ptr = start_ptr + buf.len();
|
let end_ptr = start_ptr + buf.len();
|
||||||
@ -91,13 +94,13 @@ fn align_buf(buf: &mut [u8]) -> (ShortSlice<'_>, &mut [u64], ShortSlice<'_>) {
|
|||||||
let (tmp, tail) = buf.split_at_mut(end_aligned - start_ptr);
|
let (tmp, tail) = buf.split_at_mut(end_aligned - start_ptr);
|
||||||
let (head, mid) = tmp.split_at_mut(start_aligned - start_ptr);
|
let (head, mid) = tmp.split_at_mut(start_aligned - start_ptr);
|
||||||
|
|
||||||
// Unsafe: we know the middle section is correctly aligned, and the outer
|
// SAFETY: we know the middle section is correctly aligned, and the outer
|
||||||
// sections are smaller than 8 bytes
|
// sections are smaller than 8 bytes
|
||||||
unsafe { (ShortSlice::new(head), cast_slice(mid), ShortSlice(tail)) }
|
unsafe { (ShortSlice::new(head), cast_slice(mid), ShortSlice(tail)) }
|
||||||
} else {
|
} else {
|
||||||
// We didn't cross even one aligned boundary!
|
// We didn't cross even one aligned boundary!
|
||||||
|
|
||||||
// Unsafe: The outer sections are smaller than 8 bytes
|
// SAFETY: The outer sections are smaller than 8 bytes
|
||||||
unsafe { (ShortSlice::new(buf), &mut [], ShortSlice::new(&mut [])) }
|
unsafe { (ShortSlice::new(buf), &mut [], ShortSlice::new(&mut [])) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -139,7 +142,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;
|
||||||
|
@ -8,7 +8,7 @@ use actix_codec::{AsyncRead, AsyncWrite, Framed};
|
|||||||
use actix_http::{body, h1, ws, Error, HttpService, Request, Response};
|
use actix_http::{body, h1, ws, Error, HttpService, Request, Response};
|
||||||
use actix_http_test::test_server;
|
use actix_http_test::test_server;
|
||||||
use actix_service::{fn_factory, Service};
|
use actix_service::{fn_factory, Service};
|
||||||
use actix_utils::framed::Dispatcher;
|
use actix_utils::dispatcher::Dispatcher;
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use futures_util::future;
|
use futures_util::future;
|
||||||
use futures_util::task::{Context, Poll};
|
use futures_util::task::{Context, Poll};
|
||||||
@ -59,7 +59,7 @@ where
|
|||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
Dispatcher::new(framed.into_framed(ws::Codec::new()), service)
|
Dispatcher::new(framed.replace_codec(ws::Codec::new()), service)
|
||||||
.await
|
.await
|
||||||
.map_err(|_| panic!())
|
.map_err(|_| panic!())
|
||||||
};
|
};
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
# Identity service for actix web framework [](https://travis-ci.org/actix/actix-web) [](https://codecov.io/gh/actix/actix-web) [](https://crates.io/crates/actix-identity) [](https://gitter.im/actix/actix?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
|
||||||
|
|
||||||
**This crate moved to https://github.com/actix/actix-extras.**
|
|
||||||
|
|
||||||
## Documentation & community resources
|
|
||||||
|
|
||||||
* [User Guide](https://actix.rs/docs/)
|
|
||||||
* [API Documentation](https://docs.rs/actix-identity/)
|
|
||||||
* [Chat on gitter](https://gitter.im/actix/actix)
|
|
||||||
* Cargo package: [actix-session](https://crates.io/crates/actix-identity)
|
|
||||||
* Minimum supported Rust version: 1.34 or later
|
|
@ -1,15 +1,25 @@
|
|||||||
# Changes
|
# Changes
|
||||||
|
|
||||||
## [0.3.0-alpha.1] - 2020-05-25
|
## Unreleased - 2020-xx-xx
|
||||||
|
|
||||||
|
|
||||||
|
## 3.0.0 - 2020-09-11
|
||||||
|
* No significant changes from `3.0.0-beta.2`.
|
||||||
|
|
||||||
|
|
||||||
|
## 3.0.0-beta.2 - 2020-09-10
|
||||||
|
* Update `actix-*` dependencies to latest versions.
|
||||||
|
|
||||||
|
|
||||||
|
## 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"
|
||||||
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"
|
||||||
@ -16,9 +16,9 @@ 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", default-features = false }
|
||||||
actix-service = "1.0.1"
|
actix-service = "1.0.6"
|
||||||
actix-utils = "1.0.3"
|
actix-utils = "2.0.0"
|
||||||
bytes = "0.5.3"
|
bytes = "0.5.3"
|
||||||
derive_more = "0.99.2"
|
derive_more = "0.99.2"
|
||||||
httparse = "1.3"
|
httparse = "1.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"
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
//! Multipart form support for Actix web.
|
||||||
|
|
||||||
|
#![deny(rust_2018_idioms)]
|
||||||
#![allow(clippy::borrow_interior_mutable_const)]
|
#![allow(clippy::borrow_interior_mutable_const)]
|
||||||
|
|
||||||
mod error;
|
mod error;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
//! Multipart payload support
|
//! Multipart payload support
|
||||||
|
|
||||||
use std::cell::{Cell, RefCell, RefMut};
|
use std::cell::{Cell, RefCell, RefMut};
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
@ -9,8 +10,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};
|
||||||
@ -110,7 +109,7 @@ impl Stream for Multipart {
|
|||||||
|
|
||||||
fn poll_next(
|
fn poll_next(
|
||||||
mut self: Pin<&mut Self>,
|
mut self: Pin<&mut Self>,
|
||||||
cx: &mut Context,
|
cx: &mut Context<'_>,
|
||||||
) -> Poll<Option<Self::Item>> {
|
) -> Poll<Option<Self::Item>> {
|
||||||
if let Some(err) = self.error.take() {
|
if let Some(err) = self.error.take() {
|
||||||
Poll::Ready(Some(Err(err)))
|
Poll::Ready(Some(Err(err)))
|
||||||
@ -246,7 +245,7 @@ impl InnerMultipart {
|
|||||||
fn poll(
|
fn poll(
|
||||||
&mut self,
|
&mut self,
|
||||||
safety: &Safety,
|
safety: &Safety,
|
||||||
cx: &mut Context,
|
cx: &mut Context<'_>,
|
||||||
) -> Poll<Option<Result<Field, MultipartError>>> {
|
) -> Poll<Option<Result<Field, MultipartError>>> {
|
||||||
if self.state == InnerState::Eof {
|
if self.state == InnerState::Eof {
|
||||||
Poll::Ready(None)
|
Poll::Ready(None)
|
||||||
@ -418,7 +417,10 @@ impl Field {
|
|||||||
impl Stream for Field {
|
impl Stream for Field {
|
||||||
type Item = Result<Bytes, MultipartError>;
|
type Item = Result<Bytes, MultipartError>;
|
||||||
|
|
||||||
fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Option<Self::Item>> {
|
fn poll_next(
|
||||||
|
self: Pin<&mut Self>,
|
||||||
|
cx: &mut Context<'_>,
|
||||||
|
) -> Poll<Option<Self::Item>> {
|
||||||
if self.safety.current() {
|
if self.safety.current() {
|
||||||
let mut inner = self.inner.borrow_mut();
|
let mut inner = self.inner.borrow_mut();
|
||||||
if let Some(mut payload) =
|
if let Some(mut payload) =
|
||||||
@ -436,7 +438,7 @@ impl Stream for Field {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Field {
|
impl fmt::Debug for Field {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
writeln!(f, "\nField: {}", self.ct)?;
|
writeln!(f, "\nField: {}", self.ct)?;
|
||||||
writeln!(f, " boundary: {}", self.inner.borrow().boundary)?;
|
writeln!(f, " boundary: {}", self.inner.borrow().boundary)?;
|
||||||
writeln!(f, " headers:")?;
|
writeln!(f, " headers:")?;
|
||||||
@ -691,7 +693,7 @@ impl Safety {
|
|||||||
self.clean.get()
|
self.clean.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clone(&self, cx: &mut Context) -> Safety {
|
fn clone(&self, cx: &mut Context<'_>) -> Safety {
|
||||||
let payload = Rc::clone(&self.payload);
|
let payload = Rc::clone(&self.payload);
|
||||||
let s = Safety {
|
let s = Safety {
|
||||||
task: LocalWaker::new(),
|
task: LocalWaker::new(),
|
||||||
@ -736,7 +738,7 @@ impl PayloadBuffer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn poll_stream(&mut self, cx: &mut Context) -> Result<(), PayloadError> {
|
fn poll_stream(&mut self, cx: &mut Context<'_>) -> Result<(), PayloadError> {
|
||||||
loop {
|
loop {
|
||||||
match Pin::new(&mut self.stream).poll_next(cx) {
|
match Pin::new(&mut self.stream).poll_next(cx) {
|
||||||
Poll::Ready(Some(Ok(data))) => self.buf.extend_from_slice(&data),
|
Poll::Ready(Some(Ok(data))) => self.buf.extend_from_slice(&data),
|
||||||
@ -876,11 +878,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,
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -889,7 +891,7 @@ mod tests {
|
|||||||
|
|
||||||
fn poll_next(
|
fn poll_next(
|
||||||
self: Pin<&mut Self>,
|
self: Pin<&mut Self>,
|
||||||
cx: &mut Context,
|
cx: &mut Context<'_>,
|
||||||
) -> Poll<Option<Self::Item>> {
|
) -> Poll<Option<Self::Item>> {
|
||||||
let this = self.get_mut();
|
let this = self.get_mut();
|
||||||
if !this.ready {
|
if !this.ready {
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
# Session for actix web framework [](https://travis-ci.org/actix/actix-web) [](https://codecov.io/gh/actix/actix-web) [](https://crates.io/crates/actix-session) [](https://gitter.im/actix/actix?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
|
||||||
|
|
||||||
**This crate moved to https://github.com/actix/actix-extras.**
|
|
||||||
|
|
||||||
## Documentation & community resources
|
|
||||||
|
|
||||||
* [User Guide](https://actix.rs/docs/)
|
|
||||||
* [API Documentation](https://docs.rs/actix-session/)
|
|
||||||
* [Chat on gitter](https://gitter.im/actix/actix)
|
|
||||||
* Cargo package: [actix-session](https://crates.io/crates/actix-session)
|
|
||||||
* Minimum supported Rust version: 1.34 or later
|
|
@ -1,11 +1,22 @@
|
|||||||
# Changes
|
# Changes
|
||||||
|
|
||||||
## [Unreleased] - 2020-xx-xx
|
## Unreleased - 2020-xx-xx
|
||||||
|
|
||||||
|
|
||||||
|
## 3.0.0 - 2020-09-11
|
||||||
|
* No significant changes from `3.0.0-beta.2`.
|
||||||
|
|
||||||
|
|
||||||
|
## 3.0.0-beta.2 - 2020-09-10
|
||||||
|
* Update `actix-*` dependencies to latest versions.
|
||||||
|
|
||||||
|
|
||||||
|
## [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"
|
||||||
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"
|
||||||
@ -16,16 +16,16 @@ name = "actix_web_actors"
|
|||||||
path = "src/lib.rs"
|
path = "src/lib.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix = "0.10.0-alpha.2"
|
actix = "0.10.0"
|
||||||
actix-web = { version = "3.0.0-alpha.3", default-features = false }
|
actix-web = { version = "3.0.0", default-features = false }
|
||||||
actix-http = "2.0.0-alpha.4"
|
actix-http = "2.0.0"
|
||||||
actix-codec = "0.2.0"
|
actix-codec = "0.3.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 }
|
||||||
futures-core = { version = "0.3.5", default-features = false }
|
futures-core = { version = "0.3.5", default-features = false }
|
||||||
pin-project = "0.4.17"
|
pin-project = "0.4.17"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
actix-rt = "1.0.0"
|
actix-rt = "1.1.1"
|
||||||
env_logger = "0.7"
|
env_logger = "0.7"
|
||||||
futures-util = { version = "0.3.5", default-features = false }
|
futures-util = { version = "0.3.5", default-features = false }
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
#![allow(clippy::borrow_interior_mutable_const)]
|
|
||||||
//! Actix actors integration for Actix web framework
|
//! Actix actors integration for Actix web framework
|
||||||
|
|
||||||
|
#![deny(rust_2018_idioms)]
|
||||||
|
#![allow(clippy::borrow_interior_mutable_const)]
|
||||||
|
|
||||||
mod context;
|
mod context;
|
||||||
pub mod ws;
|
pub mod ws;
|
||||||
|
|
||||||
|
@ -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();
|
||||||
|
@ -3,6 +3,10 @@
|
|||||||
## Unreleased - 2020-xx-xx
|
## Unreleased - 2020-xx-xx
|
||||||
|
|
||||||
|
|
||||||
|
## 0.3.0 - 2020-09-11
|
||||||
|
* No significant changes from `0.3.0-beta.1`.
|
||||||
|
|
||||||
|
|
||||||
## 0.3.0-beta.1 - 2020-07-14
|
## 0.3.0-beta.1 - 2020-07-14
|
||||||
* Add main entry-point macro that uses re-exported runtime. [#1559]
|
* Add main entry-point macro that uses re-exported runtime. [#1559]
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "actix-web-codegen"
|
name = "actix-web-codegen"
|
||||||
version = "0.3.0-beta.1"
|
version = "0.3.0"
|
||||||
description = "Actix web proc macros"
|
description = "Actix web proc macros"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
homepage = "https://actix.rs"
|
homepage = "https://actix.rs"
|
||||||
@ -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-beta.1"
|
actix-web = "3.0.0"
|
||||||
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,28 @@
|
|||||||
# Changes
|
# Changes
|
||||||
|
|
||||||
|
## Unreleased - 2020-xx-xx
|
||||||
|
|
||||||
|
|
||||||
|
## 2.0.0 - 2020-09-11
|
||||||
|
### Changed
|
||||||
|
* `Client::build` was renamed to `Client::builder`.
|
||||||
|
|
||||||
|
|
||||||
|
## 2.0.0-beta.4 - 2020-09-09
|
||||||
|
### Changed
|
||||||
|
* Update actix-codec & actix-tls dependencies.
|
||||||
|
|
||||||
|
|
||||||
|
## 2.0.0-beta.3 - 2020-08-17
|
||||||
|
### Changed
|
||||||
|
* Update `rustls` to 0.18
|
||||||
|
|
||||||
|
|
||||||
|
## 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
|
## [2.0.0-beta.1] - 2020-07-14
|
||||||
### Changed
|
### Changed
|
||||||
* Update `actix-http` dependency to 2.0.0-beta.1
|
* Update `actix-http` dependency to 2.0.0-beta.1
|
||||||
|
@ -1,16 +1,19 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "awc"
|
name = "awc"
|
||||||
version = "2.0.0-beta.1"
|
version = "2.0.0"
|
||||||
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 = [
|
||||||
"web-programming::http-client",
|
"network-programming",
|
||||||
"web-programming::websocket"]
|
"asynchronous",
|
||||||
|
"web-programming::http-client",
|
||||||
|
"web-programming::websocket",
|
||||||
|
]
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
@ -34,9 +37,9 @@ rustls = ["rust-tls", "actix-http/rustls"]
|
|||||||
compress = ["actix-http/compress"]
|
compress = ["actix-http/compress"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix-codec = "0.2.0"
|
actix-codec = "0.3.0"
|
||||||
actix-service = "1.0.1"
|
actix-service = "1.0.6"
|
||||||
actix-http = "2.0.0-beta.1"
|
actix-http = "2.0.0"
|
||||||
actix-rt = "1.0.0"
|
actix-rt = "1.0.0"
|
||||||
|
|
||||||
base64 = "0.12"
|
base64 = "0.12"
|
||||||
@ -51,16 +54,16 @@ serde = "1.0"
|
|||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
serde_urlencoded = "0.6.1"
|
serde_urlencoded = "0.6.1"
|
||||||
open-ssl = { version = "0.10", package = "openssl", optional = true }
|
open-ssl = { version = "0.10", package = "openssl", optional = true }
|
||||||
rust-tls = { version = "0.17.0", package = "rustls", optional = true, features = ["dangerous_configuration"] }
|
rust-tls = { version = "0.18.0", package = "rustls", optional = true, features = ["dangerous_configuration"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
actix-connect = { version = "2.0.0-alpha.2", features = ["openssl"] }
|
actix-connect = { version = "2.0.0", features = ["openssl"] }
|
||||||
actix-web = { version = "3.0.0-alpha.3", features = ["openssl"] }
|
actix-web = { version = "3.0.0", features = ["openssl"] }
|
||||||
actix-http = { version = "2.0.0-beta.1", features = ["openssl"] }
|
actix-http = { version = "2.0.0", features = ["openssl"] }
|
||||||
actix-http-test = { version = "2.0.0-alpha.1", features = ["openssl"] }
|
actix-http-test = { version = "2.0.0", features = ["openssl"] }
|
||||||
actix-utils = "1.0.3"
|
actix-utils = "2.0.0"
|
||||||
actix-server = "1.0.0"
|
actix-server = "1.0.0"
|
||||||
actix-tls = { version = "2.0.0-alpha.1", features = ["openssl", "rustls"] }
|
actix-tls = { version = "2.0.0", features = ["openssl", "rustls"] }
|
||||||
brotli2 = "0.3.2"
|
brotli2 = "0.3.2"
|
||||||
flate2 = "1.0.13"
|
flate2 = "1.0.13"
|
||||||
futures-util = { version = "0.3.5", default-features = false }
|
futures-util = { version = "0.3.5", default-features = false }
|
||||||
|
@ -152,7 +152,7 @@ where
|
|||||||
let (head, framed) =
|
let (head, framed) =
|
||||||
connection.open_tunnel(RequestHeadType::from(head)).await?;
|
connection.open_tunnel(RequestHeadType::from(head)).await?;
|
||||||
|
|
||||||
let framed = framed.map_io(|io| BoxedSocket(Box::new(Socket(io))));
|
let framed = framed.into_map_io(|io| BoxedSocket(Box::new(Socket(io))));
|
||||||
Ok((head, framed))
|
Ok((head, framed))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -186,7 +186,7 @@ where
|
|||||||
.open_tunnel(RequestHeadType::Rc(head, extra_headers))
|
.open_tunnel(RequestHeadType::Rc(head, extra_headers))
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let framed = framed.map_io(|io| BoxedSocket(Box::new(Socket(io))));
|
let framed = framed.into_map_io(|io| BoxedSocket(Box::new(Socket(io))));
|
||||||
Ok((head, framed))
|
Ok((head, framed))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
111
awc/src/lib.rs
111
awc/src/lib.rs
@ -1,27 +1,96 @@
|
|||||||
#![warn(rust_2018_idioms, warnings)]
|
#![deny(rust_2018_idioms)]
|
||||||
#![allow(
|
#![allow(
|
||||||
clippy::type_complexity,
|
clippy::type_complexity,
|
||||||
clippy::borrow_interior_mutable_const,
|
clippy::borrow_interior_mutable_const,
|
||||||
clippy::needless_doctest_main
|
clippy::needless_doctest_main
|
||||||
)]
|
)]
|
||||||
//! An HTTP Client
|
|
||||||
|
//! `awc` is a HTTP and WebSocket client library built using the Actix ecosystem.
|
||||||
|
//!
|
||||||
|
//! ## Making a GET request
|
||||||
//!
|
//!
|
||||||
//! ```rust
|
//! ```rust
|
||||||
//! use actix_rt::System;
|
//! # #[actix_rt::main]
|
||||||
//! use awc::Client;
|
//! # async fn main() -> Result<(), awc::error::SendRequestError> {
|
||||||
|
//! let mut client = awc::Client::default();
|
||||||
|
//! let response = client.get("http://www.rust-lang.org") // <- Create request builder
|
||||||
|
//! .header("User-Agent", "Actix-web")
|
||||||
|
//! .send() // <- Send http request
|
||||||
|
//! .await?;
|
||||||
//!
|
//!
|
||||||
//! #[actix_rt::main]
|
//! println!("Response: {:?}", response);
|
||||||
//! async fn main() {
|
//! # Ok(())
|
||||||
//! let mut client = Client::default();
|
//! # }
|
||||||
//!
|
|
||||||
//! let response = client.get("http://www.rust-lang.org") // <- Create request builder
|
|
||||||
//! .header("User-Agent", "Actix-web")
|
|
||||||
//! .send() // <- Send http request
|
|
||||||
//! .await;
|
|
||||||
//!
|
|
||||||
//! println!("Response: {:?}", response);
|
|
||||||
//! }
|
|
||||||
//! ```
|
//! ```
|
||||||
|
//!
|
||||||
|
//! ## Making POST requests
|
||||||
|
//!
|
||||||
|
//! ### Raw body contents
|
||||||
|
//!
|
||||||
|
//! ```rust
|
||||||
|
//! # #[actix_rt::main]
|
||||||
|
//! # async fn main() -> Result<(), awc::error::SendRequestError> {
|
||||||
|
//! let mut client = awc::Client::default();
|
||||||
|
//! let response = client.post("http://httpbin.org/post")
|
||||||
|
//! .send_body("Raw body contents")
|
||||||
|
//! .await?;
|
||||||
|
//! # Ok(())
|
||||||
|
//! # }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! ### Forms
|
||||||
|
//!
|
||||||
|
//! ```rust
|
||||||
|
//! # #[actix_rt::main]
|
||||||
|
//! # async fn main() -> Result<(), awc::error::SendRequestError> {
|
||||||
|
//! let params = [("foo", "bar"), ("baz", "quux")];
|
||||||
|
//!
|
||||||
|
//! let mut client = awc::Client::default();
|
||||||
|
//! let response = client.post("http://httpbin.org/post")
|
||||||
|
//! .send_form(¶ms)
|
||||||
|
//! .await?;
|
||||||
|
//! # Ok(())
|
||||||
|
//! # }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! ### JSON
|
||||||
|
//!
|
||||||
|
//! ```rust
|
||||||
|
//! # #[actix_rt::main]
|
||||||
|
//! # async fn main() -> Result<(), awc::error::SendRequestError> {
|
||||||
|
//! let request = serde_json::json!({
|
||||||
|
//! "lang": "rust",
|
||||||
|
//! "body": "json"
|
||||||
|
//! });
|
||||||
|
//!
|
||||||
|
//! let mut client = awc::Client::default();
|
||||||
|
//! let response = client.post("http://httpbin.org/post")
|
||||||
|
//! .send_json(&request)
|
||||||
|
//! .await?;
|
||||||
|
//! # Ok(())
|
||||||
|
//! # }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! ## WebSocket support
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! # #[actix_rt::main]
|
||||||
|
//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
//! use futures_util::{sink::SinkExt, stream::StreamExt};
|
||||||
|
//! let (_resp, mut connection) = awc::Client::new()
|
||||||
|
//! .ws("ws://echo.websocket.org")
|
||||||
|
//! .connect()
|
||||||
|
//! .await?;
|
||||||
|
//!
|
||||||
|
//! connection
|
||||||
|
//! .send(awc::ws::Message::Text("Echo".to_string()))
|
||||||
|
//! .await?;
|
||||||
|
//! let response = connection.next().await.unwrap()?;
|
||||||
|
//! # assert_eq!(response, awc::ws::Frame::Text("Echo".as_bytes().into()));
|
||||||
|
//! # Ok(())
|
||||||
|
//! # }
|
||||||
|
//! ```
|
||||||
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
@ -51,7 +120,9 @@ pub use self::sender::SendClientRequest;
|
|||||||
|
|
||||||
use self::connect::{Connect, ConnectorWrapper};
|
use self::connect::{Connect, ConnectorWrapper};
|
||||||
|
|
||||||
/// An HTTP Client
|
/// An asynchronous HTTP and WebSocket client.
|
||||||
|
///
|
||||||
|
/// ## Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use awc::Client;
|
/// use awc::Client;
|
||||||
@ -95,8 +166,9 @@ impl Client {
|
|||||||
Client::default()
|
Client::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Build client instance.
|
/// Create `Client` builder.
|
||||||
pub fn build() -> ClientBuilder {
|
/// This function is equivalent of `ClientBuilder::new()`.
|
||||||
|
pub fn builder() -> ClientBuilder {
|
||||||
ClientBuilder::new()
|
ClientBuilder::new()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,7 +265,8 @@ impl Client {
|
|||||||
self.request(Method::OPTIONS, url)
|
self.request(Method::OPTIONS, url)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct WebSockets request.
|
/// Initialize a WebSocket connection.
|
||||||
|
/// Returns a WebSocket connection builder.
|
||||||
pub fn ws<U>(&self, url: U) -> ws::WebsocketsRequest
|
pub fn ws<U>(&self, url: U) -> ws::WebsocketsRequest
|
||||||
where
|
where
|
||||||
Uri: TryFrom<U>,
|
Uri: TryFrom<U>,
|
||||||
|
@ -623,7 +623,7 @@ mod tests {
|
|||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_client_header() {
|
async fn test_client_header() {
|
||||||
let req = Client::build()
|
let req = Client::builder()
|
||||||
.header(header::CONTENT_TYPE, "111")
|
.header(header::CONTENT_TYPE, "111")
|
||||||
.finish()
|
.finish()
|
||||||
.get("/");
|
.get("/");
|
||||||
@ -641,7 +641,7 @@ mod tests {
|
|||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_client_header_override() {
|
async fn test_client_header_override() {
|
||||||
let req = Client::build()
|
let req = Client::builder()
|
||||||
.header(header::CONTENT_TYPE, "111")
|
.header(header::CONTENT_TYPE, "111")
|
||||||
.finish()
|
.finish()
|
||||||
.get("/")
|
.get("/")
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -193,11 +193,7 @@ impl RequestSender {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
SendClientRequest::new(
|
SendClientRequest::new(fut, response_decompress, timeout.or(config.timeout))
|
||||||
fut,
|
|
||||||
response_decompress,
|
|
||||||
timeout.or_else(|| config.timeout),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn send_json<T: Serialize>(
|
pub(crate) fn send_json<T: Serialize>(
|
||||||
|
@ -1,4 +1,31 @@
|
|||||||
//! Websockets client
|
//! Websockets client
|
||||||
|
//!
|
||||||
|
//! Type definitions required to use [`awc::Client`](../struct.Client.html) as a WebSocket client.
|
||||||
|
//!
|
||||||
|
//! # Example
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! use awc::{Client, ws};
|
||||||
|
//! use futures_util::{sink::SinkExt, stream::StreamExt};
|
||||||
|
//!
|
||||||
|
//! #[actix_rt::main]
|
||||||
|
//! async fn main() {
|
||||||
|
//! let (_resp, mut connection) = Client::new()
|
||||||
|
//! .ws("ws://echo.websocket.org")
|
||||||
|
//! .connect()
|
||||||
|
//! .await
|
||||||
|
//! .unwrap();
|
||||||
|
//!
|
||||||
|
//! connection
|
||||||
|
//! .send(ws::Message::Text("Echo".to_string()))
|
||||||
|
//! .await
|
||||||
|
//! .unwrap();
|
||||||
|
//! let response = connection.next().await.unwrap().unwrap();
|
||||||
|
//!
|
||||||
|
//! assert_eq!(response, ws::Frame::Text("Echo".as_bytes().into()));
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
@ -366,7 +393,7 @@ impl WebsocketsRequest {
|
|||||||
// response and ws framed
|
// response and ws framed
|
||||||
Ok((
|
Ok((
|
||||||
ClientResponse::new(head, Payload::None),
|
ClientResponse::new(head, Payload::None),
|
||||||
framed.map_codec(|_| {
|
framed.into_map_codec(|_| {
|
||||||
if server_mode {
|
if server_mode {
|
||||||
ws::Codec::new().max_size(max_size)
|
ws::Codec::new().max_size(max_size)
|
||||||
} else {
|
} else {
|
||||||
@ -407,7 +434,7 @@ mod tests {
|
|||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_header_override() {
|
async fn test_header_override() {
|
||||||
let req = Client::build()
|
let req = Client::builder()
|
||||||
.header(header::CONTENT_TYPE, "111")
|
.header(header::CONTENT_TYPE, "111")
|
||||||
.finish()
|
.finish()
|
||||||
.ws("/")
|
.ws("/")
|
||||||
|
@ -120,7 +120,7 @@ async fn test_timeout() {
|
|||||||
.timeout(Duration::from_secs(15))
|
.timeout(Duration::from_secs(15))
|
||||||
.finish();
|
.finish();
|
||||||
|
|
||||||
let client = awc::Client::build()
|
let client = awc::Client::builder()
|
||||||
.connector(connector)
|
.connector(connector)
|
||||||
.timeout(Duration::from_millis(50))
|
.timeout(Duration::from_millis(50))
|
||||||
.finish();
|
.finish();
|
||||||
@ -141,7 +141,7 @@ async fn test_timeout_override() {
|
|||||||
})))
|
})))
|
||||||
});
|
});
|
||||||
|
|
||||||
let client = awc::Client::build()
|
let client = awc::Client::builder()
|
||||||
.timeout(Duration::from_millis(50000))
|
.timeout(Duration::from_millis(50000))
|
||||||
.finish();
|
.finish();
|
||||||
let request = client
|
let request = client
|
||||||
@ -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(),
|
||||||
@ -293,7 +291,7 @@ async fn test_connection_wait_queue() {
|
|||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let client = awc::Client::build()
|
let client = awc::Client::builder()
|
||||||
.connector(awc::Connector::new().limit(1).finish())
|
.connector(awc::Connector::new().limit(1).finish())
|
||||||
.finish();
|
.finish();
|
||||||
|
|
||||||
@ -342,7 +340,7 @@ async fn test_connection_wait_queue_force_close() {
|
|||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let client = awc::Client::build()
|
let client = awc::Client::builder()
|
||||||
.connector(awc::Connector::new().limit(1).finish())
|
.connector(awc::Connector::new().limit(1).finish())
|
||||||
.finish();
|
.finish();
|
||||||
|
|
||||||
|
@ -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())
|
||||||
@ -48,7 +47,7 @@ async fn test_connection_window_size() {
|
|||||||
.set_alpn_protos(b"\x02h2\x08http/1.1")
|
.set_alpn_protos(b"\x02h2\x08http/1.1")
|
||||||
.map_err(|e| log::error!("Can not set alpn protocol: {:?}", e));
|
.map_err(|e| log::error!("Can not set alpn protocol: {:?}", e));
|
||||||
|
|
||||||
let client = awc::Client::build()
|
let client = awc::Client::builder()
|
||||||
.connector(awc::Connector::new().ssl(builder.build()).finish())
|
.connector(awc::Connector::new().ssl(builder.build()).finish())
|
||||||
.initial_window_size(100)
|
.initial_window_size(100)
|
||||||
.initial_connection_window_size(100)
|
.initial_connection_window_size(100)
|
||||||
|
@ -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())
|
||||||
@ -83,7 +82,7 @@ async fn _test_connection_reuse_h2() {
|
|||||||
.dangerous()
|
.dangerous()
|
||||||
.set_certificate_verifier(Arc::new(danger::NoCertificateVerification {}));
|
.set_certificate_verifier(Arc::new(danger::NoCertificateVerification {}));
|
||||||
|
|
||||||
let client = awc::Client::build()
|
let client = awc::Client::builder()
|
||||||
.connector(awc::Connector::new().rustls(Arc::new(config)).finish())
|
.connector(awc::Connector::new().rustls(Arc::new(config)).finish())
|
||||||
.finish();
|
.finish();
|
||||||
|
|
||||||
|
@ -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())
|
||||||
@ -63,7 +62,7 @@ async fn test_connection_reuse_h2() {
|
|||||||
.set_alpn_protos(b"\x02h2\x08http/1.1")
|
.set_alpn_protos(b"\x02h2\x08http/1.1")
|
||||||
.map_err(|e| log::error!("Can not set alpn protocol: {:?}", e));
|
.map_err(|e| log::error!("Can not set alpn protocol: {:?}", e));
|
||||||
|
|
||||||
let client = awc::Client::build()
|
let client = awc::Client::builder()
|
||||||
.connector(awc::Connector::new().ssl(builder.build()).finish())
|
.connector(awc::Connector::new().ssl(builder.build()).finish())
|
||||||
.finish();
|
.finish();
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ async fn test_simple() {
|
|||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// start websocket service
|
// start websocket service
|
||||||
let framed = framed.into_framed(ws::Codec::new());
|
let framed = framed.replace_codec(ws::Codec::new());
|
||||||
ws::Dispatcher::with(framed, ws_service).await
|
ws::Dispatcher::with(framed, ws_service).await
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
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-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-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;
|
||||||
|
|
||||||
|
17
src/data.rs
17
src/data.rs
@ -200,14 +200,14 @@ 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();
|
||||||
let resp = srv.call(req).await.unwrap();
|
let resp = srv.call(req).await.unwrap();
|
||||||
@ -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()
|
||||||
},
|
},
|
||||||
)),
|
)),
|
||||||
|
12
src/lib.rs
12
src/lib.rs
@ -1,4 +1,4 @@
|
|||||||
#![warn(rust_2018_idioms, warnings)]
|
#![deny(rust_2018_idioms)]
|
||||||
#![allow(clippy::needless_doctest_main, clippy::type_complexity)]
|
#![allow(clippy::needless_doctest_main, clippy::type_complexity)]
|
||||||
|
|
||||||
//! Actix web is a powerful, pragmatic, and extremely fast web framework for Rust.
|
//! Actix web is a powerful, pragmatic, and extremely fast web framework for Rust.
|
||||||
@ -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]
|
||||||
@ -59,7 +59,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+
|
||||||
//!
|
//!
|
||||||
//! ## Crate Features
|
//! ## Crate Features
|
||||||
//!
|
//!
|
||||||
@ -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,
|
||||||
};
|
};
|
||||||
|
@ -17,7 +17,7 @@ use futures_util::future::{ok, Either, FutureExt, LocalBoxFuture};
|
|||||||
/// # fn main() {
|
/// # fn main() {
|
||||||
/// let enable_normalize = std::env::var("NORMALIZE_PATH") == Ok("true".into());
|
/// let enable_normalize = std::env::var("NORMALIZE_PATH") == Ok("true".into());
|
||||||
/// let app = App::new()
|
/// let app = App::new()
|
||||||
/// .wrap(Condition::new(enable_normalize, NormalizePath));
|
/// .wrap(Condition::new(enable_normalize, NormalizePath::default()));
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
pub struct Condition<T> {
|
pub struct Condition<T> {
|
||||||
|
@ -85,7 +85,7 @@ use crate::HttpResponse;
|
|||||||
/// [`ConnectionInfo::realip_remote_addr()`](../dev/struct.ConnectionInfo.html#method.realip_remote_addr)
|
/// [`ConnectionInfo::realip_remote_addr()`](../dev/struct.ConnectionInfo.html#method.realip_remote_addr)
|
||||||
///
|
///
|
||||||
/// If you use this value ensure that all requests come from trusted hosts, since it is trivial
|
/// If you use this value ensure that all requests come from trusted hosts, since it is trivial
|
||||||
/// for the remote client to simulate been another client.
|
/// for the remote client to simulate being another client.
|
||||||
///
|
///
|
||||||
pub struct Logger(Rc<Inner>);
|
pub struct Logger(Rc<Inner>);
|
||||||
|
|
||||||
@ -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]
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user