mirror of https://github.com/fafhrd91/actix-web
Merge branch 'master' into feat/data_factory
This commit is contained in:
commit
eae284cfe3
|
@ -1,4 +1,4 @@
|
||||||
name: Benchmark (Linux)
|
name: Benchmark
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
|
|
|
@ -1,32 +1,39 @@
|
||||||
|
name: Lint
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
types: [opened, synchronize, reopened]
|
types: [opened, synchronize, reopened]
|
||||||
|
|
||||||
name: Clippy and rustfmt Check
|
|
||||||
jobs:
|
jobs:
|
||||||
clippy_check:
|
fmt:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
- uses: actions-rs/toolchain@v1
|
- name: Install Rust
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
with:
|
with:
|
||||||
toolchain: stable
|
toolchain: stable
|
||||||
components: rustfmt
|
components: rustfmt
|
||||||
override: true
|
|
||||||
- name: Check with rustfmt
|
- name: Check with rustfmt
|
||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1
|
||||||
with:
|
with:
|
||||||
command: fmt
|
command: fmt
|
||||||
args: --all -- --check
|
args: --all -- --check
|
||||||
|
|
||||||
- uses: actions-rs/toolchain@v1
|
clippy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Install Rust
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
with:
|
with:
|
||||||
toolchain: nightly
|
toolchain: stable
|
||||||
components: clippy
|
components: clippy
|
||||||
override: true
|
override: true
|
||||||
- name: Check with Clippy
|
- name: Check with Clippy
|
||||||
uses: actions-rs/clippy-check@v1
|
uses: actions-rs/clippy-check@v1
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
args: --all-features --all --tests
|
args: --workspace --tests --all-features
|
||||||
|
|
|
@ -24,7 +24,7 @@ jobs:
|
||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1
|
||||||
with:
|
with:
|
||||||
command: doc
|
command: doc
|
||||||
args: --no-deps --workspace --all-features
|
args: --workspace --all-features --no-deps
|
||||||
|
|
||||||
- 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
|
||||||
|
|
10
CHANGES.md
10
CHANGES.md
|
@ -4,6 +4,8 @@
|
||||||
### Added
|
### Added
|
||||||
* The method `Either<web::Json<T>, web::Form<T>>::into_inner()` which returns the inner type for
|
* The method `Either<web::Json<T>, web::Form<T>>::into_inner()` which returns the inner type for
|
||||||
whichever variant was created. Also works for `Either<web::Form<T>, web::Json<T>>`. [#1894]
|
whichever variant was created. Also works for `Either<web::Form<T>, web::Json<T>>`. [#1894]
|
||||||
|
* Add `services!` macro for helping register multiple services to `App`. [#1933]
|
||||||
|
* Enable registering vector of same type of `HttpServiceFactory` to `App` [#1933]
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
* Rework `Responder` trait to be sync and returns `Response`/`HttpResponse` directly.
|
* Rework `Responder` trait to be sync and returns `Response`/`HttpResponse` directly.
|
||||||
|
@ -11,6 +13,11 @@
|
||||||
* `ServiceRequest::into_parts` and `ServiceRequest::from_parts` would not fail.
|
* `ServiceRequest::into_parts` and `ServiceRequest::from_parts` would not fail.
|
||||||
`ServiceRequest::from_request` would not fail and no payload would be generated [#1893]
|
`ServiceRequest::from_request` would not fail and no payload would be generated [#1893]
|
||||||
* Our `Either` type now uses `Left`/`Right` variants (instead of `A`/`B`) [#1894]
|
* Our `Either` type now uses `Left`/`Right` variants (instead of `A`/`B`) [#1894]
|
||||||
|
* `test::{call_service, read_response, read_response_json, send_request}` take `&Service`
|
||||||
|
in argument [#1905]
|
||||||
|
* `App::wrap_fn`, `Resource::wrap_fn` and `Scope::wrap_fn` would give `&Service` in closure
|
||||||
|
argument [#1905]
|
||||||
|
* `web::block` accept any closure that has an output bound to `Send` and `'static`. [#1957]
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
* Multiple calls `App::data` with the same type now keeps the latest call's data. [#1906]
|
* Multiple calls `App::data` with the same type now keeps the latest call's data. [#1906]
|
||||||
|
@ -27,7 +34,10 @@
|
||||||
[#1893]: https://github.com/actix/actix-web/pull/1893
|
[#1893]: https://github.com/actix/actix-web/pull/1893
|
||||||
[#1894]: https://github.com/actix/actix-web/pull/1894
|
[#1894]: https://github.com/actix/actix-web/pull/1894
|
||||||
[#1869]: https://github.com/actix/actix-web/pull/1869
|
[#1869]: https://github.com/actix/actix-web/pull/1869
|
||||||
|
[#1905]: https://github.com/actix/actix-web/pull/1905
|
||||||
[#1906]: https://github.com/actix/actix-web/pull/1906
|
[#1906]: https://github.com/actix/actix-web/pull/1906
|
||||||
|
[#1933]: https://github.com/actix/actix-web/pull/1933
|
||||||
|
[#1957]: https://github.com/actix/actix-web/pull/1957
|
||||||
|
|
||||||
|
|
||||||
## 4.0.0-beta.1 - 2021-01-07
|
## 4.0.0-beta.1 - 2021-01-07
|
||||||
|
|
28
Cargo.toml
28
Cargo.toml
|
@ -47,10 +47,10 @@ compress = ["actix-http/compress", "awc/compress"]
|
||||||
secure-cookies = ["actix-http/secure-cookies"]
|
secure-cookies = ["actix-http/secure-cookies"]
|
||||||
|
|
||||||
# openssl
|
# openssl
|
||||||
openssl = ["actix-tls/accept", "actix-tls/openssl", "awc/openssl", "open-ssl"]
|
openssl = ["tls_openssl", "actix-tls/accept", "actix-tls/openssl", "awc/openssl"]
|
||||||
|
|
||||||
# rustls
|
# rustls
|
||||||
rustls = ["actix-tls/accept", "actix-tls/rustls", "awc/rustls", "rust-tls"]
|
rustls = ["tls_rustls", "actix-tls/accept", "actix-tls/rustls", "awc/rustls"]
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "basic"
|
name = "basic"
|
||||||
|
@ -74,19 +74,19 @@ required-features = ["rustls"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix-codec = "0.4.0-beta.1"
|
actix-codec = "0.4.0-beta.1"
|
||||||
actix-macros = "0.1.0"
|
actix-macros = "0.2.0"
|
||||||
actix-router = "0.2.4"
|
actix-router = "0.2.7"
|
||||||
actix-rt = "2.0.0-beta.2"
|
actix-rt = "2"
|
||||||
actix-server = "2.0.0-beta.2"
|
actix-server = "2.0.0-beta.3"
|
||||||
actix-service = "2.0.0-beta.3"
|
actix-service = "2.0.0-beta.4"
|
||||||
actix-utils = "3.0.0-beta.1"
|
actix-utils = "3.0.0-beta.2"
|
||||||
actix-tls = { version = "3.0.0-beta.2", default-features = false, optional = true }
|
actix-tls = { version = "3.0.0-beta.3", default-features = false, optional = true }
|
||||||
|
|
||||||
actix-web-codegen = "0.4.0"
|
actix-web-codegen = "0.4.0"
|
||||||
actix-http = "3.0.0-beta.1"
|
actix-http = "3.0.0-beta.1"
|
||||||
awc = { version = "3.0.0-beta.1", default-features = false }
|
awc = { version = "3.0.0-beta.1", default-features = false }
|
||||||
|
|
||||||
ahash = "0.6"
|
ahash = "0.7"
|
||||||
bytes = "1"
|
bytes = "1"
|
||||||
derive_more = "0.99.5"
|
derive_more = "0.99.5"
|
||||||
either = "1.5.3"
|
either = "1.5.3"
|
||||||
|
@ -101,14 +101,14 @@ regex = "1.4"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
serde_urlencoded = "0.7"
|
serde_urlencoded = "0.7"
|
||||||
time = { version = "0.2.7", default-features = false, features = ["std"] }
|
time = { version = "0.2.23", default-features = false, features = ["std"] }
|
||||||
url = "2.1"
|
url = "2.1"
|
||||||
open-ssl = { package = "openssl", version = "0.10", optional = true }
|
tls_openssl = { package = "openssl", version = "0.10.9", optional = true }
|
||||||
rust-tls = { package = "rustls", version = "0.19.0", optional = true }
|
tls_rustls = { package = "rustls", version = "0.19.0", optional = true }
|
||||||
smallvec = "1.6"
|
smallvec = "1.6"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
actix = "0.11.0-beta.1"
|
actix = { version = "0.11.0-beta.2", default-features = false }
|
||||||
actix-http = { version = "3.0.0-beta.1", features = ["actors"] }
|
actix-http = { version = "3.0.0-beta.1", features = ["actors"] }
|
||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
env_logger = "0.8"
|
env_logger = "0.8"
|
||||||
|
|
|
@ -11,10 +11,9 @@
|
||||||

|

|
||||||
[](https://deps.rs/crate/actix-web/3.3.2)
|
[](https://deps.rs/crate/actix-web/3.3.2)
|
||||||
<br />
|
<br />
|
||||||
[](https://travis-ci.org/actix/actix-web)
|
[](https://github.com/actix/actix-web/actions)
|
||||||
[](https://codecov.io/gh/actix/actix-web)
|
[](https://codecov.io/gh/actix/actix-web)
|
||||||
[](https://crates.io/crates/actix-web)
|

|
||||||
[](https://gitter.im/actix/actix?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
|
||||||
[](https://discord.gg/NWpN5mmg3x)
|
[](https://discord.gg/NWpN5mmg3x)
|
||||||
|
|
||||||
</p>
|
</p>
|
||||||
|
@ -99,9 +98,9 @@ One of the fastest web frameworks available according to the
|
||||||
This project is licensed under either of
|
This project is licensed under either of
|
||||||
|
|
||||||
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||||
[http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0))
|
[http://www.apache.org/licenses/LICENSE-2.0])
|
||||||
* MIT license ([LICENSE-MIT](LICENSE-MIT) or
|
* MIT license ([LICENSE-MIT](LICENSE-MIT) or
|
||||||
[http://opensource.org/licenses/MIT](http://opensource.org/licenses/MIT))
|
[http://opensource.org/licenses/MIT])
|
||||||
|
|
||||||
at your option.
|
at your option.
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,10 @@
|
||||||
|
|
||||||
## Unreleased - 2021-xx-xx
|
## Unreleased - 2021-xx-xx
|
||||||
* Fix If-Modified-Since and If-Unmodified-Since to not compare using sub-second timestamps. [#1887]
|
* Fix If-Modified-Since and If-Unmodified-Since to not compare using sub-second timestamps. [#1887]
|
||||||
|
* Replace `v_htmlescape` with `askama_escape`. [#1953]
|
||||||
|
|
||||||
[#1887]: https://github.com/actix/actix-web/pull/1887
|
[#1887]: https://github.com/actix/actix-web/pull/1887
|
||||||
|
[#1953]: https://github.com/actix/actix-web/pull/1953
|
||||||
|
|
||||||
## 0.6.0-beta.1 - 2021-01-07
|
## 0.6.0-beta.1 - 2021-01-07
|
||||||
* `HttpRange::parse` now has its own error type.
|
* `HttpRange::parse` now has its own error type.
|
||||||
|
|
|
@ -18,7 +18,9 @@ path = "src/lib.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix-web = { version = "4.0.0-beta.1", default-features = false }
|
actix-web = { version = "4.0.0-beta.1", default-features = false }
|
||||||
actix-service = "2.0.0-beta.3"
|
actix-service = "2.0.0-beta.4"
|
||||||
|
|
||||||
|
askama_escape = "0.10"
|
||||||
bitflags = "1"
|
bitflags = "1"
|
||||||
bytes = "1"
|
bytes = "1"
|
||||||
futures-core = { version = "0.3.7", default-features = false }
|
futures-core = { version = "0.3.7", default-features = false }
|
||||||
|
@ -28,8 +30,7 @@ log = "0.4"
|
||||||
mime = "0.3"
|
mime = "0.3"
|
||||||
mime_guess = "2.0.1"
|
mime_guess = "2.0.1"
|
||||||
percent-encoding = "2.1"
|
percent-encoding = "2.1"
|
||||||
v_htmlescape = "0.12"
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
actix-rt = "2.0.0-beta.2"
|
actix-rt = "2"
|
||||||
actix-web = "4.0.0-beta.1"
|
actix-web = "4.0.0-beta.1"
|
||||||
|
|
|
@ -8,7 +8,7 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use actix_web::{
|
use actix_web::{
|
||||||
error::{Error, ErrorInternalServerError},
|
error::{BlockingError, Error},
|
||||||
rt::task::{spawn_blocking, JoinHandle},
|
rt::task::{spawn_blocking, JoinHandle},
|
||||||
};
|
};
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
|
@ -18,11 +18,26 @@ use futures_core::{ready, Stream};
|
||||||
/// A helper created from a `std::fs::File` which reads the file
|
/// A helper created from a `std::fs::File` which reads the file
|
||||||
/// chunk-by-chunk on a `ThreadPool`.
|
/// chunk-by-chunk on a `ThreadPool`.
|
||||||
pub struct ChunkedReadFile {
|
pub struct ChunkedReadFile {
|
||||||
pub(crate) size: u64,
|
size: u64,
|
||||||
pub(crate) offset: u64,
|
offset: u64,
|
||||||
pub(crate) file: Option<File>,
|
state: ChunkedReadFileState,
|
||||||
pub(crate) fut: Option<JoinHandle<Result<(File, Bytes), io::Error>>>,
|
counter: u64,
|
||||||
pub(crate) counter: u64,
|
}
|
||||||
|
|
||||||
|
enum ChunkedReadFileState {
|
||||||
|
File(Option<File>),
|
||||||
|
Future(JoinHandle<Result<(File, Bytes), io::Error>>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ChunkedReadFile {
|
||||||
|
pub(crate) fn new(size: u64, offset: u64, file: File) -> Self {
|
||||||
|
Self {
|
||||||
|
size,
|
||||||
|
offset,
|
||||||
|
state: ChunkedReadFileState::File(Some(file)),
|
||||||
|
counter: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for ChunkedReadFile {
|
impl fmt::Debug for ChunkedReadFile {
|
||||||
|
@ -38,49 +53,52 @@ impl Stream for ChunkedReadFile {
|
||||||
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 {
|
let this = self.as_mut().get_mut();
|
||||||
let res = match ready!(Pin::new(fut).poll(cx)) {
|
match this.state {
|
||||||
Ok(Ok((file, bytes))) => {
|
ChunkedReadFileState::File(ref mut file) => {
|
||||||
self.fut.take();
|
let size = this.size;
|
||||||
self.file = Some(file);
|
let offset = this.offset;
|
||||||
|
let counter = this.counter;
|
||||||
self.offset += bytes.len() as u64;
|
|
||||||
self.counter += bytes.len() as u64;
|
|
||||||
|
|
||||||
Ok(bytes)
|
|
||||||
}
|
|
||||||
Ok(Err(e)) => Err(e.into()),
|
|
||||||
Err(_) => Err(ErrorInternalServerError("Unexpected error")),
|
|
||||||
};
|
|
||||||
return Poll::Ready(Some(res));
|
|
||||||
}
|
|
||||||
|
|
||||||
let size = self.size;
|
|
||||||
let offset = self.offset;
|
|
||||||
let counter = self.counter;
|
|
||||||
|
|
||||||
if size == counter {
|
if size == counter {
|
||||||
Poll::Ready(None)
|
Poll::Ready(None)
|
||||||
} else {
|
} else {
|
||||||
let mut file = self.file.take().expect("Use after completion");
|
let mut file = file
|
||||||
|
.take()
|
||||||
|
.expect("ChunkedReadFile polled after completion");
|
||||||
|
|
||||||
self.fut = Some(spawn_blocking(move || {
|
let fut = spawn_blocking(move || {
|
||||||
let max_bytes = cmp::min(size.saturating_sub(counter), 65_536) as usize;
|
let max_bytes =
|
||||||
|
cmp::min(size.saturating_sub(counter), 65_536) as usize;
|
||||||
|
|
||||||
let mut buf = Vec::with_capacity(max_bytes);
|
let mut buf = Vec::with_capacity(max_bytes);
|
||||||
file.seek(io::SeekFrom::Start(offset))?;
|
file.seek(io::SeekFrom::Start(offset))?;
|
||||||
|
|
||||||
let n_bytes =
|
let n_bytes = file
|
||||||
file.by_ref().take(max_bytes as u64).read_to_end(&mut buf)?;
|
.by_ref()
|
||||||
|
.take(max_bytes as u64)
|
||||||
|
.read_to_end(&mut buf)?;
|
||||||
|
|
||||||
if n_bytes == 0 {
|
if n_bytes == 0 {
|
||||||
return Err(io::ErrorKind::UnexpectedEof.into());
|
return Err(io::ErrorKind::UnexpectedEof.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok((file, Bytes::from(buf)))
|
Ok((file, Bytes::from(buf)))
|
||||||
}));
|
});
|
||||||
|
this.state = ChunkedReadFileState::Future(fut);
|
||||||
self.poll_next(cx)
|
self.poll_next(cx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ChunkedReadFileState::Future(ref mut fut) => {
|
||||||
|
let (file, bytes) =
|
||||||
|
ready!(Pin::new(fut).poll(cx)).map_err(|_| BlockingError)??;
|
||||||
|
this.state = ChunkedReadFileState::File(Some(file));
|
||||||
|
|
||||||
|
this.offset += bytes.len() as u64;
|
||||||
|
this.counter += bytes.len() as u64;
|
||||||
|
|
||||||
|
Poll::Ready(Some(Ok(bytes)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use std::{fmt::Write, fs::DirEntry, io, path::Path, path::PathBuf};
|
use std::{fmt::Write, fs::DirEntry, io, path::Path, path::PathBuf};
|
||||||
|
|
||||||
use actix_web::{dev::ServiceResponse, HttpRequest, HttpResponse};
|
use actix_web::{dev::ServiceResponse, HttpRequest, HttpResponse};
|
||||||
|
use askama_escape::{escape as escape_html_entity, Html};
|
||||||
use percent_encoding::{utf8_percent_encode, CONTROLS};
|
use percent_encoding::{utf8_percent_encode, CONTROLS};
|
||||||
use v_htmlescape::escape as escape_html_entity;
|
|
||||||
|
|
||||||
/// A directory; responds with the generated directory listing.
|
/// A directory; responds with the generated directory listing.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -50,7 +50,7 @@ macro_rules! encode_file_url {
|
||||||
// " -- " & -- & ' -- ' < -- < > -- > / -- /
|
// " -- " & -- & ' -- ' < -- < > -- > / -- /
|
||||||
macro_rules! encode_file_name {
|
macro_rules! encode_file_name {
|
||||||
($entry:ident) => {
|
($entry:ident) => {
|
||||||
escape_html_entity(&$entry.file_name().to_string_lossy())
|
escape_html_entity(&$entry.file_name().to_string_lossy(), Html)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -73,7 +73,8 @@ mod tests {
|
||||||
},
|
},
|
||||||
middleware::Compress,
|
middleware::Compress,
|
||||||
test::{self, TestRequest},
|
test::{self, TestRequest},
|
||||||
web, App, HttpResponse, Responder,
|
web::{self, Bytes},
|
||||||
|
App, HttpResponse, Responder,
|
||||||
};
|
};
|
||||||
use futures_util::future::ok;
|
use futures_util::future::ok;
|
||||||
|
|
||||||
|
@ -365,7 +366,7 @@ mod tests {
|
||||||
DispositionType::Attachment
|
DispositionType::Attachment
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut srv = test::init_service(
|
let srv = test::init_service(
|
||||||
App::new().service(
|
App::new().service(
|
||||||
Files::new("/", ".")
|
Files::new("/", ".")
|
||||||
.mime_override(all_attachment)
|
.mime_override(all_attachment)
|
||||||
|
@ -375,7 +376,7 @@ mod tests {
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let request = TestRequest::get().uri("/").to_request();
|
let request = TestRequest::get().uri("/").to_request();
|
||||||
let response = test::call_service(&mut srv, request).await;
|
let response = test::call_service(&srv, request).await;
|
||||||
assert_eq!(response.status(), StatusCode::OK);
|
assert_eq!(response.status(), StatusCode::OK);
|
||||||
|
|
||||||
let content_disposition = response
|
let content_disposition = response
|
||||||
|
@ -390,7 +391,7 @@ mod tests {
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_named_file_ranges_status_code() {
|
async fn test_named_file_ranges_status_code() {
|
||||||
let mut srv = test::init_service(
|
let srv = test::init_service(
|
||||||
App::new().service(Files::new("/test", ".").index_file("Cargo.toml")),
|
App::new().service(Files::new("/test", ".").index_file("Cargo.toml")),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
@ -400,7 +401,7 @@ mod tests {
|
||||||
.uri("/t%65st/Cargo.toml")
|
.uri("/t%65st/Cargo.toml")
|
||||||
.insert_header((header::RANGE, "bytes=10-20"))
|
.insert_header((header::RANGE, "bytes=10-20"))
|
||||||
.to_request();
|
.to_request();
|
||||||
let response = test::call_service(&mut srv, request).await;
|
let response = test::call_service(&srv, request).await;
|
||||||
assert_eq!(response.status(), StatusCode::PARTIAL_CONTENT);
|
assert_eq!(response.status(), StatusCode::PARTIAL_CONTENT);
|
||||||
|
|
||||||
// Invalid range header
|
// Invalid range header
|
||||||
|
@ -408,7 +409,7 @@ mod tests {
|
||||||
.uri("/t%65st/Cargo.toml")
|
.uri("/t%65st/Cargo.toml")
|
||||||
.insert_header((header::RANGE, "bytes=1-0"))
|
.insert_header((header::RANGE, "bytes=1-0"))
|
||||||
.to_request();
|
.to_request();
|
||||||
let response = test::call_service(&mut srv, request).await;
|
let response = test::call_service(&srv, request).await;
|
||||||
|
|
||||||
assert_eq!(response.status(), StatusCode::RANGE_NOT_SATISFIABLE);
|
assert_eq!(response.status(), StatusCode::RANGE_NOT_SATISFIABLE);
|
||||||
}
|
}
|
||||||
|
@ -495,14 +496,14 @@ mod tests {
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_static_files_with_spaces() {
|
async fn test_static_files_with_spaces() {
|
||||||
let mut srv = test::init_service(
|
let srv = test::init_service(
|
||||||
App::new().service(Files::new("/", ".").index_file("Cargo.toml")),
|
App::new().service(Files::new("/", ".").index_file("Cargo.toml")),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
let request = TestRequest::get()
|
let request = TestRequest::get()
|
||||||
.uri("/tests/test%20space.binary")
|
.uri("/tests/test%20space.binary")
|
||||||
.to_request();
|
.to_request();
|
||||||
let response = test::call_service(&mut srv, request).await;
|
let response = test::call_service(&srv, request).await;
|
||||||
assert_eq!(response.status(), StatusCode::OK);
|
assert_eq!(response.status(), StatusCode::OK);
|
||||||
|
|
||||||
let bytes = test::read_body(response).await;
|
let bytes = test::read_body(response).await;
|
||||||
|
@ -512,28 +513,28 @@ mod tests {
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_files_not_allowed() {
|
async fn test_files_not_allowed() {
|
||||||
let mut srv = test::init_service(App::new().service(Files::new("/", "."))).await;
|
let srv = test::init_service(App::new().service(Files::new("/", "."))).await;
|
||||||
|
|
||||||
let req = TestRequest::default()
|
let req = TestRequest::default()
|
||||||
.uri("/Cargo.toml")
|
.uri("/Cargo.toml")
|
||||||
.method(Method::POST)
|
.method(Method::POST)
|
||||||
.to_request();
|
.to_request();
|
||||||
|
|
||||||
let resp = test::call_service(&mut srv, req).await;
|
let resp = test::call_service(&srv, req).await;
|
||||||
assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED);
|
assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED);
|
||||||
|
|
||||||
let mut srv = test::init_service(App::new().service(Files::new("/", "."))).await;
|
let srv = test::init_service(App::new().service(Files::new("/", "."))).await;
|
||||||
let req = TestRequest::default()
|
let req = TestRequest::default()
|
||||||
.method(Method::PUT)
|
.method(Method::PUT)
|
||||||
.uri("/Cargo.toml")
|
.uri("/Cargo.toml")
|
||||||
.to_request();
|
.to_request();
|
||||||
let resp = test::call_service(&mut srv, req).await;
|
let resp = test::call_service(&srv, req).await;
|
||||||
assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED);
|
assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_files_guards() {
|
async fn test_files_guards() {
|
||||||
let mut srv = test::init_service(
|
let srv = test::init_service(
|
||||||
App::new().service(Files::new("/", ".").use_guards(guard::Post())),
|
App::new().service(Files::new("/", ".").use_guards(guard::Post())),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
@ -543,13 +544,13 @@ mod tests {
|
||||||
.method(Method::POST)
|
.method(Method::POST)
|
||||||
.to_request();
|
.to_request();
|
||||||
|
|
||||||
let resp = test::call_service(&mut srv, req).await;
|
let resp = test::call_service(&srv, req).await;
|
||||||
assert_eq!(resp.status(), StatusCode::OK);
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[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 srv = test::init_service(App::new().wrap(Compress::default()).service(
|
||||||
web::resource("/").to(|| async {
|
web::resource("/").to(|| async {
|
||||||
NamedFile::open("Cargo.toml")
|
NamedFile::open("Cargo.toml")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
@ -562,14 +563,14 @@ mod tests {
|
||||||
.uri("/")
|
.uri("/")
|
||||||
.insert_header((header::ACCEPT_ENCODING, "gzip"))
|
.insert_header((header::ACCEPT_ENCODING, "gzip"))
|
||||||
.to_request();
|
.to_request();
|
||||||
let res = test::call_service(&mut srv, request).await;
|
let res = test::call_service(&srv, request).await;
|
||||||
assert_eq!(res.status(), StatusCode::OK);
|
assert_eq!(res.status(), StatusCode::OK);
|
||||||
assert!(!res.headers().contains_key(header::CONTENT_ENCODING));
|
assert!(!res.headers().contains_key(header::CONTENT_ENCODING));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[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 srv = test::init_service(App::new().wrap(Compress::default()).service(
|
||||||
web::resource("/").to(|| async {
|
web::resource("/").to(|| async {
|
||||||
NamedFile::open("Cargo.toml")
|
NamedFile::open("Cargo.toml")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
@ -582,7 +583,7 @@ mod tests {
|
||||||
.uri("/")
|
.uri("/")
|
||||||
.insert_header((header::ACCEPT_ENCODING, "gzip"))
|
.insert_header((header::ACCEPT_ENCODING, "gzip"))
|
||||||
.to_request();
|
.to_request();
|
||||||
let res = test::call_service(&mut srv, request).await;
|
let res = test::call_service(&srv, request).await;
|
||||||
assert_eq!(res.status(), StatusCode::OK);
|
assert_eq!(res.status(), StatusCode::OK);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
res.headers()
|
res.headers()
|
||||||
|
@ -604,27 +605,27 @@ mod tests {
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_static_files() {
|
async fn test_static_files() {
|
||||||
let mut srv = test::init_service(
|
let srv = test::init_service(
|
||||||
App::new().service(Files::new("/", ".").show_files_listing()),
|
App::new().service(Files::new("/", ".").show_files_listing()),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
let req = TestRequest::with_uri("/missing").to_request();
|
let req = TestRequest::with_uri("/missing").to_request();
|
||||||
|
|
||||||
let resp = test::call_service(&mut srv, req).await;
|
let resp = test::call_service(&srv, req).await;
|
||||||
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
|
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
|
||||||
|
|
||||||
let mut srv = test::init_service(App::new().service(Files::new("/", "."))).await;
|
let srv = test::init_service(App::new().service(Files::new("/", "."))).await;
|
||||||
|
|
||||||
let req = TestRequest::default().to_request();
|
let req = TestRequest::default().to_request();
|
||||||
let resp = test::call_service(&mut srv, req).await;
|
let resp = test::call_service(&srv, req).await;
|
||||||
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
|
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
|
||||||
|
|
||||||
let mut srv = test::init_service(
|
let srv = test::init_service(
|
||||||
App::new().service(Files::new("/", ".").show_files_listing()),
|
App::new().service(Files::new("/", ".").show_files_listing()),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
let req = TestRequest::with_uri("/tests").to_request();
|
let req = TestRequest::with_uri("/tests").to_request();
|
||||||
let resp = test::call_service(&mut srv, req).await;
|
let resp = test::call_service(&srv, req).await;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
resp.headers().get(header::CONTENT_TYPE).unwrap(),
|
resp.headers().get(header::CONTENT_TYPE).unwrap(),
|
||||||
"text/html; charset=utf-8"
|
"text/html; charset=utf-8"
|
||||||
|
@ -637,16 +638,16 @@ mod tests {
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_redirect_to_slash_directory() {
|
async fn test_redirect_to_slash_directory() {
|
||||||
// should not redirect if no index
|
// should not redirect if no index
|
||||||
let mut srv = test::init_service(
|
let srv = test::init_service(
|
||||||
App::new().service(Files::new("/", ".").redirect_to_slash_directory()),
|
App::new().service(Files::new("/", ".").redirect_to_slash_directory()),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
let req = TestRequest::with_uri("/tests").to_request();
|
let req = TestRequest::with_uri("/tests").to_request();
|
||||||
let resp = test::call_service(&mut srv, req).await;
|
let resp = test::call_service(&srv, req).await;
|
||||||
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
|
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
|
||||||
|
|
||||||
// should redirect if index present
|
// should redirect if index present
|
||||||
let mut srv = test::init_service(
|
let srv = test::init_service(
|
||||||
App::new().service(
|
App::new().service(
|
||||||
Files::new("/", ".")
|
Files::new("/", ".")
|
||||||
.index_file("test.png")
|
.index_file("test.png")
|
||||||
|
@ -655,12 +656,12 @@ mod tests {
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
let req = TestRequest::with_uri("/tests").to_request();
|
let req = TestRequest::with_uri("/tests").to_request();
|
||||||
let resp = test::call_service(&mut srv, req).await;
|
let resp = test::call_service(&srv, req).await;
|
||||||
assert_eq!(resp.status(), StatusCode::FOUND);
|
assert_eq!(resp.status(), StatusCode::FOUND);
|
||||||
|
|
||||||
// should not redirect if the path is wrong
|
// should not redirect if the path is wrong
|
||||||
let req = TestRequest::with_uri("/not_existing").to_request();
|
let req = TestRequest::with_uri("/not_existing").to_request();
|
||||||
let resp = test::call_service(&mut srv, req).await;
|
let resp = test::call_service(&srv, req).await;
|
||||||
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
|
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -672,7 +673,7 @@ mod tests {
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_default_handler_file_missing() {
|
async fn test_default_handler_file_missing() {
|
||||||
let mut st = Files::new("/", ".")
|
let st = Files::new("/", ".")
|
||||||
.default_handler(|req: ServiceRequest| {
|
.default_handler(|req: ServiceRequest| {
|
||||||
ok(req.into_response(HttpResponse::Ok().body("default content")))
|
ok(req.into_response(HttpResponse::Ok().body("default content")))
|
||||||
})
|
})
|
||||||
|
@ -681,7 +682,7 @@ mod tests {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let req = TestRequest::with_uri("/missing").to_srv_request();
|
let req = TestRequest::with_uri("/missing").to_srv_request();
|
||||||
|
|
||||||
let resp = test::call_service(&mut st, req).await;
|
let resp = test::call_service(&st, req).await;
|
||||||
assert_eq!(resp.status(), StatusCode::OK);
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
let bytes = test::read_body(resp).await;
|
let bytes = test::read_body(resp).await;
|
||||||
assert_eq!(bytes, web::Bytes::from_static(b"default content"));
|
assert_eq!(bytes, web::Bytes::from_static(b"default content"));
|
||||||
|
@ -750,54 +751,49 @@ mod tests {
|
||||||
// );
|
// );
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// #[actix_rt::test]
|
#[actix_rt::test]
|
||||||
// fn integration_serve_index() {
|
async fn integration_serve_index() {
|
||||||
// let mut srv = test::TestServer::with_factory(|| {
|
let srv = test::init_service(
|
||||||
// App::new().handler(
|
App::new().service(Files::new("test", ".").index_file("Cargo.toml")),
|
||||||
// "test",
|
)
|
||||||
// Files::new(".").index_file("Cargo.toml"),
|
.await;
|
||||||
// )
|
|
||||||
// });
|
|
||||||
|
|
||||||
// let request = srv.get().uri(srv.url("/test")).finish().unwrap();
|
let req = TestRequest::get().uri("/test").to_request();
|
||||||
// let response = srv.execute(request.send()).unwrap();
|
let res = test::call_service(&srv, req).await;
|
||||||
// assert_eq!(response.status(), StatusCode::OK);
|
assert_eq!(res.status(), StatusCode::OK);
|
||||||
// let bytes = srv.execute(response.body()).unwrap();
|
|
||||||
// let data = Bytes::from(fs::read("Cargo.toml").unwrap());
|
|
||||||
// assert_eq!(bytes, data);
|
|
||||||
|
|
||||||
// let request = srv.get().uri(srv.url("/test/")).finish().unwrap();
|
let bytes = test::read_body(res).await;
|
||||||
// let response = srv.execute(request.send()).unwrap();
|
|
||||||
// assert_eq!(response.status(), StatusCode::OK);
|
|
||||||
// let bytes = srv.execute(response.body()).unwrap();
|
|
||||||
// let data = Bytes::from(fs::read("Cargo.toml").unwrap());
|
|
||||||
// assert_eq!(bytes, data);
|
|
||||||
|
|
||||||
// // nonexistent index file
|
let data = Bytes::from(fs::read("Cargo.toml").unwrap());
|
||||||
// let request = srv.get().uri(srv.url("/test/unknown")).finish().unwrap();
|
assert_eq!(bytes, data);
|
||||||
// let response = srv.execute(request.send()).unwrap();
|
|
||||||
// assert_eq!(response.status(), StatusCode::NOT_FOUND);
|
|
||||||
|
|
||||||
// let request = srv.get().uri(srv.url("/test/unknown/")).finish().unwrap();
|
let req = TestRequest::get().uri("/test/").to_request();
|
||||||
// let response = srv.execute(request.send()).unwrap();
|
let res = test::call_service(&srv, req).await;
|
||||||
// assert_eq!(response.status(), StatusCode::NOT_FOUND);
|
assert_eq!(res.status(), StatusCode::OK);
|
||||||
// }
|
|
||||||
|
|
||||||
// #[actix_rt::test]
|
let bytes = test::read_body(res).await;
|
||||||
// fn integration_percent_encoded() {
|
let data = Bytes::from(fs::read("Cargo.toml").unwrap());
|
||||||
// let mut srv = test::TestServer::with_factory(|| {
|
assert_eq!(bytes, data);
|
||||||
// App::new().handler(
|
|
||||||
// "test",
|
|
||||||
// Files::new(".").index_file("Cargo.toml"),
|
|
||||||
// )
|
|
||||||
// });
|
|
||||||
|
|
||||||
// let request = srv
|
// nonexistent index file
|
||||||
// .get()
|
let req = TestRequest::get().uri("/test/unknown").to_request();
|
||||||
// .uri(srv.url("/test/%43argo.toml"))
|
let res = test::call_service(&srv, req).await;
|
||||||
// .finish()
|
assert_eq!(res.status(), StatusCode::NOT_FOUND);
|
||||||
// .unwrap();
|
|
||||||
// let response = srv.execute(request.send()).unwrap();
|
let req = TestRequest::get().uri("/test/unknown/").to_request();
|
||||||
// assert_eq!(response.status(), StatusCode::OK);
|
let res = test::call_service(&srv, req).await;
|
||||||
// }
|
assert_eq!(res.status(), StatusCode::NOT_FOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn integration_percent_encoded() {
|
||||||
|
let srv = test::init_service(
|
||||||
|
App::new().service(Files::new("test", ".").index_file("Cargo.toml")),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let req = TestRequest::get().uri("/test/%43argo.toml").to_request();
|
||||||
|
let res = test::call_service(&srv, req).await;
|
||||||
|
assert_eq!(res.status(), StatusCode::OK);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -298,13 +298,7 @@ impl NamedFile {
|
||||||
res.encoding(current_encoding);
|
res.encoding(current_encoding);
|
||||||
}
|
}
|
||||||
|
|
||||||
let reader = ChunkedReadFile {
|
let reader = ChunkedReadFile::new(self.md.len(), 0, self.file);
|
||||||
size: self.md.len(),
|
|
||||||
offset: 0,
|
|
||||||
file: Some(self.file),
|
|
||||||
fut: None,
|
|
||||||
counter: 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
return res.streaming(reader);
|
return res.streaming(reader);
|
||||||
}
|
}
|
||||||
|
@ -426,13 +420,7 @@ impl NamedFile {
|
||||||
return resp.status(StatusCode::NOT_MODIFIED).finish();
|
return resp.status(StatusCode::NOT_MODIFIED).finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
let reader = ChunkedReadFile {
|
let reader = ChunkedReadFile::new(length, offset, self.file);
|
||||||
offset,
|
|
||||||
size: length,
|
|
||||||
file: Some(self.file),
|
|
||||||
fut: None,
|
|
||||||
counter: 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
if offset != 0 || length != self.md.len() {
|
if offset != 0 || length != self.md.len() {
|
||||||
resp.status(StatusCode::PARTIAL_CONTENT);
|
resp.status(StatusCode::PARTIAL_CONTENT);
|
||||||
|
|
|
@ -1,9 +1,4 @@
|
||||||
use std::{
|
use std::{fmt, io, path::PathBuf, rc::Rc, task::Poll};
|
||||||
fmt, io,
|
|
||||||
path::PathBuf,
|
|
||||||
rc::Rc,
|
|
||||||
task::{Context, Poll},
|
|
||||||
};
|
|
||||||
|
|
||||||
use actix_service::Service;
|
use actix_service::Service;
|
||||||
use actix_web::{
|
use actix_web::{
|
||||||
|
@ -40,10 +35,10 @@ type FilesServiceFuture = Either<
|
||||||
>;
|
>;
|
||||||
|
|
||||||
impl FilesService {
|
impl FilesService {
|
||||||
fn handle_err(&mut self, e: io::Error, req: ServiceRequest) -> FilesServiceFuture {
|
fn handle_err(&self, e: io::Error, req: ServiceRequest) -> FilesServiceFuture {
|
||||||
log::debug!("Failed to handle {}: {}", req.path(), e);
|
log::debug!("Failed to handle {}: {}", req.path(), e);
|
||||||
|
|
||||||
if let Some(ref mut default) = self.default {
|
if let Some(ref default) = self.default {
|
||||||
Either::Right(default.call(req))
|
Either::Right(default.call(req))
|
||||||
} else {
|
} else {
|
||||||
Either::Left(ok(req.error_response(e)))
|
Either::Left(ok(req.error_response(e)))
|
||||||
|
@ -62,11 +57,9 @@ impl Service<ServiceRequest> for FilesService {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Future = FilesServiceFuture;
|
type Future = FilesServiceFuture;
|
||||||
|
|
||||||
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
actix_service::always_ready!();
|
||||||
Poll::Ready(Ok(()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn call(&mut self, req: ServiceRequest) -> Self::Future {
|
fn call(&self, req: ServiceRequest) -> Self::Future {
|
||||||
let is_method_valid = if let Some(guard) = &self.guards {
|
let is_method_valid = if let Some(guard) = &self.guards {
|
||||||
// execute user defined guards
|
// execute user defined guards
|
||||||
(**guard).check(req.head())
|
(**guard).check(req.head())
|
||||||
|
|
|
@ -11,11 +11,10 @@ use actix_web::{
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_utf8_file_contents() {
|
async fn test_utf8_file_contents() {
|
||||||
// use default ISO-8859-1 encoding
|
// use default ISO-8859-1 encoding
|
||||||
let mut srv =
|
let srv = test::init_service(App::new().service(Files::new("/", "./tests"))).await;
|
||||||
test::init_service(App::new().service(Files::new("/", "./tests"))).await;
|
|
||||||
|
|
||||||
let req = TestRequest::with_uri("/utf8.txt").to_request();
|
let req = TestRequest::with_uri("/utf8.txt").to_request();
|
||||||
let res = test::call_service(&mut srv, req).await;
|
let res = test::call_service(&srv, req).await;
|
||||||
|
|
||||||
assert_eq!(res.status(), StatusCode::OK);
|
assert_eq!(res.status(), StatusCode::OK);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -24,13 +23,13 @@ async fn test_utf8_file_contents() {
|
||||||
);
|
);
|
||||||
|
|
||||||
// prefer UTF-8 encoding
|
// prefer UTF-8 encoding
|
||||||
let mut srv = test::init_service(
|
let srv = test::init_service(
|
||||||
App::new().service(Files::new("/", "./tests").prefer_utf8(true)),
|
App::new().service(Files::new("/", "./tests").prefer_utf8(true)),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let req = TestRequest::with_uri("/utf8.txt").to_request();
|
let req = TestRequest::with_uri("/utf8.txt").to_request();
|
||||||
let res = test::call_service(&mut srv, req).await;
|
let res = test::call_service(&srv, req).await;
|
||||||
|
|
||||||
assert_eq!(res.status(), StatusCode::OK);
|
assert_eq!(res.status(), StatusCode::OK);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|
|
@ -26,15 +26,15 @@ path = "src/lib.rs"
|
||||||
default = []
|
default = []
|
||||||
|
|
||||||
# openssl
|
# openssl
|
||||||
openssl = ["open-ssl", "awc/openssl"]
|
openssl = ["tls-openssl", "awc/openssl"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix-service = "2.0.0-beta.3"
|
actix-service = "2.0.0-beta.4"
|
||||||
actix-codec = "0.4.0-beta.1"
|
actix-codec = "0.4.0-beta.1"
|
||||||
actix-tls = "3.0.0-beta.2"
|
actix-tls = "3.0.0-beta.3"
|
||||||
actix-utils = "3.0.0-beta.1"
|
actix-utils = "3.0.0-beta.2"
|
||||||
actix-rt = "2.0.0-beta.2"
|
actix-rt = "2"
|
||||||
actix-server = "2.0.0-beta.2"
|
actix-server = "2.0.0-beta.3"
|
||||||
awc = "3.0.0-beta.1"
|
awc = "3.0.0-beta.1"
|
||||||
|
|
||||||
base64 = "0.13"
|
base64 = "0.13"
|
||||||
|
@ -47,8 +47,8 @@ serde = "1.0"
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
slab = "0.4"
|
slab = "0.4"
|
||||||
serde_urlencoded = "0.7"
|
serde_urlencoded = "0.7"
|
||||||
time = { version = "0.2.7", default-features = false, features = ["std"] }
|
time = { version = "0.2.23", default-features = false, features = ["std"] }
|
||||||
open-ssl = { version = "0.10", package = "openssl", optional = true }
|
tls-openssl = { version = "0.10.9", package = "openssl", optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
actix-web = "4.0.0-beta.1"
|
actix-web = "4.0.0-beta.1"
|
||||||
|
|
|
@ -4,6 +4,9 @@
|
||||||
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
||||||
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
||||||
|
|
||||||
|
#[cfg(feature = "openssl")]
|
||||||
|
extern crate tls_openssl as openssl;
|
||||||
|
|
||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
use std::{net, thread, time};
|
use std::{net, thread, time};
|
||||||
|
|
||||||
|
@ -60,7 +63,7 @@ pub async fn test_server_with_addr<F: ServiceFactory<TcpStream>>(
|
||||||
|
|
||||||
// run server in separate thread
|
// run server in separate thread
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
let sys = System::new("actix-test-server");
|
let sys = System::new();
|
||||||
let local_addr = tcp.local_addr().unwrap();
|
let local_addr = tcp.local_addr().unwrap();
|
||||||
|
|
||||||
let srv = Server::build()
|
let srv = Server::build()
|
||||||
|
@ -69,7 +72,7 @@ pub async fn test_server_with_addr<F: ServiceFactory<TcpStream>>(
|
||||||
.disable_signals();
|
.disable_signals();
|
||||||
|
|
||||||
sys.block_on(async {
|
sys.block_on(async {
|
||||||
srv.start();
|
srv.run();
|
||||||
tx.send((System::current(), local_addr)).unwrap();
|
tx.send((System::current(), local_addr)).unwrap();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -82,7 +85,7 @@ pub async fn test_server_with_addr<F: ServiceFactory<TcpStream>>(
|
||||||
let connector = {
|
let connector = {
|
||||||
#[cfg(feature = "openssl")]
|
#[cfg(feature = "openssl")]
|
||||||
{
|
{
|
||||||
use open_ssl::ssl::{SslConnector, SslMethod, SslVerifyMode};
|
use openssl::ssl::{SslConnector, SslMethod, SslVerifyMode};
|
||||||
|
|
||||||
let mut builder = SslConnector::builder(SslMethod::tls()).unwrap();
|
let mut builder = SslConnector::builder(SslMethod::tls()).unwrap();
|
||||||
builder.set_verify(SslVerifyMode::NONE);
|
builder.set_verify(SslVerifyMode::NONE);
|
||||||
|
@ -106,7 +109,6 @@ pub async fn test_server_with_addr<F: ServiceFactory<TcpStream>>(
|
||||||
|
|
||||||
Client::builder().connector(connector).finish()
|
Client::builder().connector(connector).finish()
|
||||||
};
|
};
|
||||||
actix_tls::connect::start_default_resolver().await.unwrap();
|
|
||||||
|
|
||||||
TestServer {
|
TestServer {
|
||||||
addr,
|
addr,
|
||||||
|
|
|
@ -16,6 +16,9 @@
|
||||||
* `Extensions::insert` returns Option of replaced item. [#1904]
|
* `Extensions::insert` returns Option of replaced item. [#1904]
|
||||||
* Remove `HttpResponseBuilder::json2()` and make `HttpResponseBuilder::json()` take a value by
|
* Remove `HttpResponseBuilder::json2()` and make `HttpResponseBuilder::json()` take a value by
|
||||||
reference. [#1903]
|
reference. [#1903]
|
||||||
|
* `client::error::ConnectError` Resolver variant contains `Box<dyn std::error::Error>` type [#1905]
|
||||||
|
* `client::ConnectorConfig` default timeout changed to 5 seconds. [#1905]
|
||||||
|
* Simplify `BlockingError` type to a struct. It's only triggered with blocking thread pool is dead. [#1957]
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
* `ResponseBuilder::set`; use `ResponseBuilder::insert_header`. [#1869]
|
* `ResponseBuilder::set`; use `ResponseBuilder::insert_header`. [#1869]
|
||||||
|
@ -28,7 +31,9 @@
|
||||||
[#1894]: https://github.com/actix/actix-web/pull/1894
|
[#1894]: https://github.com/actix/actix-web/pull/1894
|
||||||
[#1903]: https://github.com/actix/actix-web/pull/1903
|
[#1903]: https://github.com/actix/actix-web/pull/1903
|
||||||
[#1904]: https://github.com/actix/actix-web/pull/1904
|
[#1904]: https://github.com/actix/actix-web/pull/1904
|
||||||
|
[#1905]: https://github.com/actix/actix-web/pull/1905
|
||||||
[#1912]: https://github.com/actix/actix-web/pull/1912
|
[#1912]: https://github.com/actix/actix-web/pull/1912
|
||||||
|
[#1957]: https://github.com/actix/actix-web/pull/1957
|
||||||
|
|
||||||
|
|
||||||
## 3.0.0-beta.1 - 2021-01-07
|
## 3.0.0-beta.1 - 2021-01-07
|
||||||
|
|
|
@ -40,12 +40,12 @@ secure-cookies = ["cookie/secure"]
|
||||||
actors = ["actix"]
|
actors = ["actix"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix-service = "2.0.0-beta.3"
|
actix-service = "2.0.0-beta.4"
|
||||||
actix-codec = "0.4.0-beta.1"
|
actix-codec = "0.4.0-beta.1"
|
||||||
actix-utils = "3.0.0-beta.1"
|
actix-utils = "3.0.0-beta.2"
|
||||||
actix-rt = "2.0.0-beta.2"
|
actix-rt = "2"
|
||||||
actix-tls = "3.0.0-beta.2"
|
actix-tls = "3.0.0-beta.2"
|
||||||
actix = { version = "0.11.0-beta.1", optional = true }
|
actix = { version = "0.11.0-beta.2", default-features = false, optional = true }
|
||||||
|
|
||||||
base64 = "0.13"
|
base64 = "0.13"
|
||||||
bitflags = "1.2"
|
bitflags = "1.2"
|
||||||
|
@ -55,10 +55,10 @@ cookie = { version = "0.14.1", features = ["percent-encode"] }
|
||||||
derive_more = "0.99.5"
|
derive_more = "0.99.5"
|
||||||
either = "1.5.3"
|
either = "1.5.3"
|
||||||
encoding_rs = "0.8"
|
encoding_rs = "0.8"
|
||||||
futures-channel = { version = "0.3.7", default-features = false }
|
futures-channel = { version = "0.3.7", default-features = false, features = ["alloc"] }
|
||||||
futures-core = { version = "0.3.7", default-features = false }
|
futures-core = { version = "0.3.7", default-features = false, features = ["alloc"] }
|
||||||
futures-util = { version = "0.3.7", default-features = false, features = ["sink"] }
|
futures-util = { version = "0.3.7", default-features = false, features = ["alloc", "sink"] }
|
||||||
ahash = "0.6"
|
ahash = "0.7"
|
||||||
h2 = "0.3.0"
|
h2 = "0.3.0"
|
||||||
http = "0.2.2"
|
http = "0.2.2"
|
||||||
httparse = "1.3"
|
httparse = "1.3"
|
||||||
|
@ -78,21 +78,21 @@ sha-1 = "0.9"
|
||||||
smallvec = "1.6"
|
smallvec = "1.6"
|
||||||
slab = "0.4"
|
slab = "0.4"
|
||||||
serde_urlencoded = "0.7"
|
serde_urlencoded = "0.7"
|
||||||
time = { version = "0.2.7", default-features = false, features = ["std"] }
|
time = { version = "0.2.23", default-features = false, features = ["std"] }
|
||||||
|
|
||||||
# compression
|
# compression
|
||||||
brotli2 = { version="0.3.2", optional = true }
|
brotli2 = { version="0.3.2", optional = true }
|
||||||
flate2 = { version = "1.0.13", optional = true }
|
flate2 = { version = "1.0.13", optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
actix-server = "2.0.0-beta.2"
|
actix-server = "2.0.0-beta.3"
|
||||||
actix-http-test = { version = "3.0.0-beta.1", features = ["openssl"] }
|
actix-http-test = { version = "3.0.0-beta.1", features = ["openssl"] }
|
||||||
actix-tls = { version = "3.0.0-beta.2", features = ["openssl"] }
|
actix-tls = { version = "3.0.0-beta.2", features = ["openssl"] }
|
||||||
criterion = "0.3"
|
criterion = "0.3"
|
||||||
env_logger = "0.7"
|
env_logger = "0.8"
|
||||||
serde_derive = "1.0"
|
serde_derive = "1.0"
|
||||||
open-ssl = { version="0.10", package = "openssl" }
|
tls-openssl = { version = "0.10", package = "openssl" }
|
||||||
rust-tls = { version="0.19", package = "rustls" }
|
tls-rustls = { version = "0.19", package = "rustls" }
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "write-camel-case"
|
name = "write-camel-case"
|
||||||
|
|
|
@ -18,7 +18,7 @@ pub(crate) struct ConnectorConfig {
|
||||||
impl Default for ConnectorConfig {
|
impl Default for ConnectorConfig {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
timeout: Duration::from_secs(1),
|
timeout: Duration::from_secs(5),
|
||||||
conn_lifetime: Duration::from_secs(75),
|
conn_lifetime: Duration::from_secs(75),
|
||||||
conn_keep_alive: Duration::from_secs(15),
|
conn_keep_alive: Duration::from_secs(15),
|
||||||
disconnect_timeout: Some(Duration::from_millis(3000)),
|
disconnect_timeout: Some(Duration::from_millis(3000)),
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
|
use std::ops::{Deref, DerefMut};
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::task::{Context, Poll};
|
use std::task::{Context, Poll};
|
||||||
use std::{fmt, io, time};
|
use std::{fmt, io, time};
|
||||||
|
|
||||||
use actix_codec::{AsyncRead, AsyncWrite, Framed, ReadBuf};
|
use actix_codec::{AsyncRead, AsyncWrite, Framed, ReadBuf};
|
||||||
|
use actix_rt::task::JoinHandle;
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use futures_util::future::{err, Either, FutureExt, LocalBoxFuture, Ready};
|
use futures_core::future::LocalBoxFuture;
|
||||||
|
use futures_util::future::{err, Either, FutureExt, Ready};
|
||||||
use h2::client::SendRequest;
|
use h2::client::SendRequest;
|
||||||
use pin_project::pin_project;
|
use pin_project::pin_project;
|
||||||
|
|
||||||
|
@ -20,7 +23,53 @@ use super::{h1proto, h2proto};
|
||||||
|
|
||||||
pub(crate) enum ConnectionType<Io> {
|
pub(crate) enum ConnectionType<Io> {
|
||||||
H1(Io),
|
H1(Io),
|
||||||
H2(SendRequest<Bytes>),
|
H2(H2Connection),
|
||||||
|
}
|
||||||
|
|
||||||
|
// h2 connection has two parts: SendRequest and Connection.
|
||||||
|
// Connection is spawned as async task on runtime and H2Connection would hold a handle for
|
||||||
|
// this task. So it can wake up and quit the task when SendRequest is dropped.
|
||||||
|
pub(crate) struct H2Connection {
|
||||||
|
handle: JoinHandle<()>,
|
||||||
|
sender: SendRequest<Bytes>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl H2Connection {
|
||||||
|
pub(crate) fn new<Io>(
|
||||||
|
sender: SendRequest<Bytes>,
|
||||||
|
connection: h2::client::Connection<Io>,
|
||||||
|
) -> Self
|
||||||
|
where
|
||||||
|
Io: AsyncRead + AsyncWrite + Unpin + 'static,
|
||||||
|
{
|
||||||
|
let handle = actix_rt::spawn(async move {
|
||||||
|
let _ = connection.await;
|
||||||
|
});
|
||||||
|
|
||||||
|
Self { handle, sender }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// wake up waker when drop
|
||||||
|
impl Drop for H2Connection {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.handle.abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// only expose sender type to public.
|
||||||
|
impl Deref for H2Connection {
|
||||||
|
type Target = SendRequest<Bytes>;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.sender
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for H2Connection {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.sender
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Connection {
|
pub trait Connection {
|
||||||
|
@ -265,3 +314,35 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use std::net;
|
||||||
|
|
||||||
|
use actix_rt::net::TcpStream;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn test_h2_connection_drop() {
|
||||||
|
let addr = "127.0.0.1:0".parse::<net::SocketAddr>().unwrap();
|
||||||
|
let listener = net::TcpListener::bind(addr).unwrap();
|
||||||
|
let local = listener.local_addr().unwrap();
|
||||||
|
|
||||||
|
std::thread::spawn(move || while listener.accept().is_ok() {});
|
||||||
|
|
||||||
|
let tcp = TcpStream::connect(local).await.unwrap();
|
||||||
|
let (sender, connection) = h2::client::handshake(tcp).await.unwrap();
|
||||||
|
let conn = H2Connection::new(sender.clone(), connection);
|
||||||
|
|
||||||
|
assert!(sender.clone().ready().await.is_ok());
|
||||||
|
assert!(h2::client::SendRequest::clone(&*conn).ready().await.is_ok());
|
||||||
|
|
||||||
|
drop(conn);
|
||||||
|
|
||||||
|
match sender.ready().await {
|
||||||
|
Ok(_) => panic!("connection should be gone and can not be ready"),
|
||||||
|
Err(e) => assert!(e.is_io()),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -100,9 +100,9 @@ impl Connector<(), ()> {
|
||||||
fn build_ssl(protocols: Vec<Vec<u8>>) -> SslConnector {
|
fn build_ssl(protocols: Vec<Vec<u8>>) -> SslConnector {
|
||||||
let mut config = ClientConfig::new();
|
let mut config = ClientConfig::new();
|
||||||
config.set_protocols(&protocols);
|
config.set_protocols(&protocols);
|
||||||
config
|
config.root_store.add_server_trust_anchors(
|
||||||
.root_store
|
&actix_tls::connect::ssl::rustls::TLS_SERVER_ROOTS,
|
||||||
.add_server_trust_anchors(&actix_tls::accept::rustls::TLS_SERVER_ROOTS);
|
);
|
||||||
SslConnector::Rustls(Arc::new(config))
|
SslConnector::Rustls(Arc::new(config))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -392,11 +392,11 @@ mod connect_impl {
|
||||||
Ready<Result<IoConnection<Io>, ConnectError>>,
|
Ready<Result<IoConnection<Io>, ConnectError>>,
|
||||||
>;
|
>;
|
||||||
|
|
||||||
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||||
self.tcp_pool.poll_ready(cx)
|
self.tcp_pool.poll_ready(cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call(&mut self, req: Connect) -> Self::Future {
|
fn call(&self, req: Connect) -> Self::Future {
|
||||||
match req.uri.scheme_str() {
|
match req.uri.scheme_str() {
|
||||||
Some("https") | Some("wss") => {
|
Some("https") | Some("wss") => {
|
||||||
Either::Right(err(ConnectError::SslIsNotSupported))
|
Either::Right(err(ConnectError::SslIsNotSupported))
|
||||||
|
@ -460,11 +460,11 @@ mod connect_impl {
|
||||||
InnerConnectorResponseB<T2, Io1, Io2>,
|
InnerConnectorResponseB<T2, Io1, Io2>,
|
||||||
>;
|
>;
|
||||||
|
|
||||||
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||||
self.tcp_pool.poll_ready(cx)
|
self.tcp_pool.poll_ready(cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call(&mut self, req: Connect) -> Self::Future {
|
fn call(&self, req: Connect) -> Self::Future {
|
||||||
match req.uri.scheme_str() {
|
match req.uri.scheme_str() {
|
||||||
Some("https") | Some("wss") => Either::Right(InnerConnectorResponseB {
|
Some("https") | Some("wss") => Either::Right(InnerConnectorResponseB {
|
||||||
fut: self.ssl_pool.call(req),
|
fut: self.ssl_pool.call(req),
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
use actix_tls::connect::resolver::ResolveError;
|
|
||||||
use derive_more::{Display, From};
|
use derive_more::{Display, From};
|
||||||
|
|
||||||
#[cfg(feature = "openssl")]
|
#[cfg(feature = "openssl")]
|
||||||
|
@ -23,7 +22,7 @@ pub enum ConnectError {
|
||||||
|
|
||||||
/// Failed to resolve the hostname
|
/// Failed to resolve the hostname
|
||||||
#[display(fmt = "Failed resolving hostname: {}", _0)]
|
#[display(fmt = "Failed resolving hostname: {}", _0)]
|
||||||
Resolver(ResolveError),
|
Resolver(Box<dyn std::error::Error>),
|
||||||
|
|
||||||
/// No dns records
|
/// No dns records
|
||||||
#[display(fmt = "No dns records found for the input")]
|
#[display(fmt = "No dns records found for the input")]
|
||||||
|
|
|
@ -22,9 +22,10 @@ use super::config::ConnectorConfig;
|
||||||
use super::connection::{ConnectionType, IoConnection};
|
use super::connection::{ConnectionType, IoConnection};
|
||||||
use super::error::SendRequestError;
|
use super::error::SendRequestError;
|
||||||
use super::pool::Acquired;
|
use super::pool::Acquired;
|
||||||
|
use crate::client::connection::H2Connection;
|
||||||
|
|
||||||
pub(crate) async fn send_request<T, B>(
|
pub(crate) async fn send_request<T, B>(
|
||||||
mut io: SendRequest<Bytes>,
|
mut io: H2Connection,
|
||||||
head: RequestHeadType,
|
head: RequestHeadType,
|
||||||
body: B,
|
body: B,
|
||||||
created: time::Instant,
|
created: time::Instant,
|
||||||
|
@ -173,7 +174,7 @@ async fn send_body<B: MessageBody>(
|
||||||
|
|
||||||
/// release SendRequest object
|
/// release SendRequest object
|
||||||
fn release<T: AsyncRead + AsyncWrite + Unpin + 'static>(
|
fn release<T: AsyncRead + AsyncWrite + Unpin + 'static>(
|
||||||
io: SendRequest<Bytes>,
|
io: H2Connection,
|
||||||
pool: Option<Acquired<T>>,
|
pool: Option<Acquired<T>>,
|
||||||
created: time::Instant,
|
created: time::Instant,
|
||||||
close: bool,
|
close: bool,
|
||||||
|
|
|
@ -13,7 +13,8 @@ use actix_utils::task::LocalWaker;
|
||||||
use ahash::AHashMap;
|
use ahash::AHashMap;
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use futures_channel::oneshot;
|
use futures_channel::oneshot;
|
||||||
use futures_util::future::{poll_fn, FutureExt, LocalBoxFuture};
|
use futures_core::future::LocalBoxFuture;
|
||||||
|
use futures_util::future::{poll_fn, FutureExt};
|
||||||
use h2::client::{Connection, SendRequest};
|
use h2::client::{Connection, SendRequest};
|
||||||
use http::uri::Authority;
|
use http::uri::Authority;
|
||||||
use indexmap::IndexSet;
|
use indexmap::IndexSet;
|
||||||
|
@ -25,6 +26,7 @@ use super::connection::{ConnectionType, IoConnection};
|
||||||
use super::error::ConnectError;
|
use super::error::ConnectError;
|
||||||
use super::h2proto::handshake;
|
use super::h2proto::handshake;
|
||||||
use super::Connect;
|
use super::Connect;
|
||||||
|
use crate::client::connection::H2Connection;
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq)]
|
#[derive(Clone, Copy, PartialEq)]
|
||||||
/// Protocol version
|
/// Protocol version
|
||||||
|
@ -45,7 +47,7 @@ impl From<Authority> for Key {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Connections pool
|
/// Connections pool
|
||||||
pub(crate) struct ConnectionPool<T, Io: 'static>(Rc<RefCell<T>>, Rc<RefCell<Inner<Io>>>);
|
pub(crate) struct ConnectionPool<T, Io: 'static>(Rc<T>, Rc<RefCell<Inner<Io>>>);
|
||||||
|
|
||||||
impl<T, Io> ConnectionPool<T, Io>
|
impl<T, Io> ConnectionPool<T, Io>
|
||||||
where
|
where
|
||||||
|
@ -53,7 +55,7 @@ where
|
||||||
T: Service<Connect, Response = (Io, Protocol), Error = ConnectError> + 'static,
|
T: Service<Connect, Response = (Io, Protocol), Error = ConnectError> + 'static,
|
||||||
{
|
{
|
||||||
pub(crate) fn new(connector: T, config: ConnectorConfig) -> Self {
|
pub(crate) fn new(connector: T, config: ConnectorConfig) -> Self {
|
||||||
let connector_rc = Rc::new(RefCell::new(connector));
|
let connector_rc = Rc::new(connector);
|
||||||
let inner_rc = Rc::new(RefCell::new(Inner {
|
let inner_rc = Rc::new(RefCell::new(Inner {
|
||||||
config,
|
config,
|
||||||
acquired: 0,
|
acquired: 0,
|
||||||
|
@ -98,12 +100,12 @@ where
|
||||||
type Error = ConnectError;
|
type Error = ConnectError;
|
||||||
type Future = LocalBoxFuture<'static, Result<IoConnection<Io>, ConnectError>>;
|
type Future = LocalBoxFuture<'static, Result<IoConnection<Io>, ConnectError>>;
|
||||||
|
|
||||||
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||||
self.0.poll_ready(cx)
|
self.0.poll_ready(cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call(&mut self, req: Connect) -> Self::Future {
|
fn call(&self, req: Connect) -> Self::Future {
|
||||||
let mut connector = self.0.clone();
|
let connector = self.0.clone();
|
||||||
let inner = self.1.clone();
|
let inner = self.1.clone();
|
||||||
|
|
||||||
let fut = async move {
|
let fut = async move {
|
||||||
|
@ -138,10 +140,9 @@ where
|
||||||
Some(guard.consume()),
|
Some(guard.consume()),
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
let (snd, connection) = handshake(io, &config).await?;
|
let (sender, connection) = handshake(io, &config).await?;
|
||||||
actix_rt::spawn(connection.map(|_| ()));
|
|
||||||
Ok(IoConnection::new(
|
Ok(IoConnection::new(
|
||||||
ConnectionType::H2(snd),
|
ConnectionType::H2(H2Connection::new(sender, connection)),
|
||||||
Instant::now(),
|
Instant::now(),
|
||||||
Some(guard.consume()),
|
Some(guard.consume()),
|
||||||
))
|
))
|
||||||
|
@ -325,7 +326,7 @@ where
|
||||||
{
|
{
|
||||||
if let Some(timeout) = self.config.disconnect_timeout {
|
if let Some(timeout) = self.config.disconnect_timeout {
|
||||||
if let ConnectionType::H1(io) = conn.io {
|
if let ConnectionType::H1(io) = conn.io {
|
||||||
actix_rt::spawn(CloseConnection::new(io, timeout))
|
actix_rt::spawn(CloseConnection::new(io, timeout));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -340,7 +341,7 @@ where
|
||||||
if let ConnectionType::H1(io) = io {
|
if let ConnectionType::H1(io) = io {
|
||||||
actix_rt::spawn(CloseConnection::new(
|
actix_rt::spawn(CloseConnection::new(
|
||||||
io, timeout,
|
io, timeout,
|
||||||
))
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
|
@ -372,7 +373,7 @@ where
|
||||||
self.acquired -= 1;
|
self.acquired -= 1;
|
||||||
if let Some(timeout) = self.config.disconnect_timeout {
|
if let Some(timeout) = self.config.disconnect_timeout {
|
||||||
if let ConnectionType::H1(io) = io {
|
if let ConnectionType::H1(io) = io {
|
||||||
actix_rt::spawn(CloseConnection::new(io, timeout))
|
actix_rt::spawn(CloseConnection::new(io, timeout));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.check_availability();
|
self.check_availability();
|
||||||
|
@ -428,7 +429,7 @@ struct ConnectorPoolSupport<T, Io>
|
||||||
where
|
where
|
||||||
Io: AsyncRead + AsyncWrite + Unpin + 'static,
|
Io: AsyncRead + AsyncWrite + Unpin + 'static,
|
||||||
{
|
{
|
||||||
connector: T,
|
connector: Rc<T>,
|
||||||
inner: Rc<RefCell<Inner<Io>>>,
|
inner: Rc<RefCell<Inner<Io>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -535,7 +536,7 @@ where
|
||||||
rx: Some(rx),
|
rx: Some(rx),
|
||||||
inner: Some(inner),
|
inner: Some(inner),
|
||||||
config,
|
config,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -565,11 +566,10 @@ where
|
||||||
|
|
||||||
if let Some(ref mut h2) = this.h2 {
|
if let Some(ref mut h2) = this.h2 {
|
||||||
return match Pin::new(h2).poll(cx) {
|
return match Pin::new(h2).poll(cx) {
|
||||||
Poll::Ready(Ok((snd, connection))) => {
|
Poll::Ready(Ok((sender, connection))) => {
|
||||||
actix_rt::spawn(connection.map(|_| ()));
|
|
||||||
let rx = this.rx.take().unwrap();
|
let rx = this.rx.take().unwrap();
|
||||||
let _ = rx.send(Ok(IoConnection::new(
|
let _ = rx.send(Ok(IoConnection::new(
|
||||||
ConnectionType::H2(snd),
|
ConnectionType::H2(H2Connection::new(sender, connection)),
|
||||||
Instant::now(),
|
Instant::now(),
|
||||||
Some(Acquired(this.key.clone(), this.inner.take())),
|
Some(Acquired(this.key.clone(), this.inner.take())),
|
||||||
)));
|
)));
|
||||||
|
|
|
@ -79,15 +79,8 @@ where
|
||||||
) -> Poll<Option<Self::Item>> {
|
) -> Poll<Option<Self::Item>> {
|
||||||
loop {
|
loop {
|
||||||
if let Some(ref mut fut) = self.fut {
|
if let Some(ref mut fut) = self.fut {
|
||||||
let (chunk, decoder) = match ready!(Pin::new(fut).poll(cx)) {
|
let (chunk, decoder) =
|
||||||
Ok(Ok(item)) => item,
|
ready!(Pin::new(fut).poll(cx)).map_err(|_| BlockingError)??;
|
||||||
Ok(Err(e)) => {
|
|
||||||
return Poll::Ready(Some(Err(BlockingError::Error(e).into())))
|
|
||||||
}
|
|
||||||
Err(_) => {
|
|
||||||
return Poll::Ready(Some(Err(BlockingError::Canceled.into())))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
self.decoder = Some(decoder);
|
self.decoder = Some(decoder);
|
||||||
self.fut.take();
|
self.fut.take();
|
||||||
if let Some(chunk) = chunk {
|
if let Some(chunk) = chunk {
|
||||||
|
|
|
@ -136,17 +136,8 @@ impl<B: MessageBody> MessageBody for Encoder<B> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(ref mut fut) = this.fut {
|
if let Some(ref mut fut) = this.fut {
|
||||||
let mut encoder = match ready!(Pin::new(fut).poll(cx)) {
|
let mut encoder =
|
||||||
Ok(Ok(item)) => item,
|
ready!(Pin::new(fut).poll(cx)).map_err(|_| BlockingError)??;
|
||||||
Ok(Err(e)) => {
|
|
||||||
return Poll::Ready(Some(Err(BlockingError::Error(e).into())))
|
|
||||||
}
|
|
||||||
Err(_) => {
|
|
||||||
return Poll::Ready(Some(Err(
|
|
||||||
BlockingError::<io::Error>::Canceled.into(),
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let chunk = encoder.take();
|
let chunk = encoder.take();
|
||||||
*this.encoder = Some(encoder);
|
*this.encoder = Some(encoder);
|
||||||
this.fut.take();
|
this.fut.take();
|
||||||
|
|
|
@ -147,7 +147,10 @@ impl From<ResponseBuilder> for Error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return `GATEWAY_TIMEOUT` for `TimeoutError`
|
/// Inspects the underlying enum and returns an appropriate status code.
|
||||||
|
///
|
||||||
|
/// If the variant is [`TimeoutError::Service`], the error code of the service is returned.
|
||||||
|
/// Otherwise, [`StatusCode::GATEWAY_TIMEOUT`] is returned.
|
||||||
impl<E: ResponseError> ResponseError for TimeoutError<E> {
|
impl<E: ResponseError> ResponseError for TimeoutError<E> {
|
||||||
fn status_code(&self) -> StatusCode {
|
fn status_code(&self) -> StatusCode {
|
||||||
match self {
|
match self {
|
||||||
|
@ -161,41 +164,44 @@ impl<E: ResponseError> ResponseError for TimeoutError<E> {
|
||||||
#[display(fmt = "UnknownError")]
|
#[display(fmt = "UnknownError")]
|
||||||
struct UnitError;
|
struct UnitError;
|
||||||
|
|
||||||
/// `InternalServerError` for `UnitError`
|
/// Returns [`StatusCode::INTERNAL_SERVER_ERROR`] for [`UnitError`].
|
||||||
impl ResponseError for UnitError {}
|
impl ResponseError for UnitError {}
|
||||||
|
|
||||||
/// `InternalServerError` for `JsonError`
|
/// Returns [`StatusCode::INTERNAL_SERVER_ERROR`] for [`JsonError`].
|
||||||
impl ResponseError for JsonError {}
|
impl ResponseError for JsonError {}
|
||||||
|
|
||||||
/// `InternalServerError` for `FormError`
|
/// Returns [`StatusCode::INTERNAL_SERVER_ERROR`] for [`FormError`].
|
||||||
impl ResponseError for FormError {}
|
impl ResponseError for FormError {}
|
||||||
|
|
||||||
#[cfg(feature = "openssl")]
|
#[cfg(feature = "openssl")]
|
||||||
/// `InternalServerError` for `openssl::ssl::Error`
|
/// Returns [`StatusCode::INTERNAL_SERVER_ERROR`] for [`actix_tls::accept::openssl::SslError`].
|
||||||
impl ResponseError for actix_tls::accept::openssl::SslError {}
|
impl ResponseError for actix_tls::accept::openssl::SslError {}
|
||||||
|
|
||||||
/// Return `BAD_REQUEST` for `de::value::Error`
|
/// Returns [`StatusCode::BAD_REQUEST`] for [`DeError`].
|
||||||
impl ResponseError for DeError {
|
impl ResponseError for DeError {
|
||||||
fn status_code(&self) -> StatusCode {
|
fn status_code(&self) -> StatusCode {
|
||||||
StatusCode::BAD_REQUEST
|
StatusCode::BAD_REQUEST
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `InternalServerError` for `Canceled`
|
/// Returns [`StatusCode::INTERNAL_SERVER_ERROR`] for [`Canceled`].
|
||||||
impl ResponseError for Canceled {}
|
impl ResponseError for Canceled {}
|
||||||
|
|
||||||
/// Return `BAD_REQUEST` for `Utf8Error`
|
/// Returns [`StatusCode::BAD_REQUEST`] for [`Utf8Error`].
|
||||||
impl ResponseError for Utf8Error {
|
impl ResponseError for Utf8Error {
|
||||||
fn status_code(&self) -> StatusCode {
|
fn status_code(&self) -> StatusCode {
|
||||||
StatusCode::BAD_REQUEST
|
StatusCode::BAD_REQUEST
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return `InternalServerError` for `HttpError`,
|
/// Returns [`StatusCode::INTERNAL_SERVER_ERROR`] for [`HttpError`].
|
||||||
/// Response generation can return `HttpError`, so it is internal error
|
|
||||||
impl ResponseError for HttpError {}
|
impl ResponseError for HttpError {}
|
||||||
|
|
||||||
/// Return `InternalServerError` for `io::Error`
|
/// Inspects the underlying [`io::ErrorKind`] and returns an appropriate status code.
|
||||||
|
///
|
||||||
|
/// If the error is [`io::ErrorKind::NotFound`], [`StatusCode::NOT_FOUND`] is returned. If the
|
||||||
|
/// error is [`io::ErrorKind::PermissionDenied`], [`StatusCode::FORBIDDEN`] is returned. Otherwise,
|
||||||
|
/// [`StatusCode::INTERNAL_SERVER_ERROR`] is returned.
|
||||||
impl ResponseError for io::Error {
|
impl ResponseError for io::Error {
|
||||||
fn status_code(&self) -> StatusCode {
|
fn status_code(&self) -> StatusCode {
|
||||||
match self.kind() {
|
match self.kind() {
|
||||||
|
@ -206,7 +212,7 @@ impl ResponseError for io::Error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `BadRequest` for `InvalidHeaderValue`
|
/// Returns [`StatusCode::BAD_REQUEST`] for [`header::InvalidHeaderValue`].
|
||||||
impl ResponseError for header::InvalidHeaderValue {
|
impl ResponseError for header::InvalidHeaderValue {
|
||||||
fn status_code(&self) -> StatusCode {
|
fn status_code(&self) -> StatusCode {
|
||||||
StatusCode::BAD_REQUEST
|
StatusCode::BAD_REQUEST
|
||||||
|
@ -297,17 +303,13 @@ impl From<httparse::Error> for ParseError {
|
||||||
|
|
||||||
/// A set of errors that can occur running blocking tasks in thread pool.
|
/// A set of errors that can occur running blocking tasks in thread pool.
|
||||||
#[derive(Debug, Display)]
|
#[derive(Debug, Display)]
|
||||||
pub enum BlockingError<E: fmt::Debug> {
|
#[display(fmt = "Blocking thread pool is gone")]
|
||||||
#[display(fmt = "{:?}", _0)]
|
pub struct BlockingError;
|
||||||
Error(E),
|
|
||||||
#[display(fmt = "Thread pool is gone")]
|
|
||||||
Canceled,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E: fmt::Debug> std::error::Error for BlockingError<E> {}
|
impl std::error::Error for BlockingError {}
|
||||||
|
|
||||||
/// `InternalServerError` for `BlockingError`
|
/// `InternalServerError` for `BlockingError`
|
||||||
impl<E: fmt::Debug> ResponseError for BlockingError<E> {}
|
impl ResponseError for BlockingError {}
|
||||||
|
|
||||||
#[derive(Display, Debug)]
|
#[derive(Display, Debug)]
|
||||||
/// A set of errors that can occur during payload parsing
|
/// A set of errors that can occur during payload parsing
|
||||||
|
@ -372,15 +374,12 @@ impl From<io::Error> for PayloadError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<BlockingError<io::Error>> for PayloadError {
|
impl From<BlockingError> for PayloadError {
|
||||||
fn from(err: BlockingError<io::Error>) -> Self {
|
fn from(_: BlockingError) -> Self {
|
||||||
match err {
|
PayloadError::Io(io::Error::new(
|
||||||
BlockingError::Error(e) => PayloadError::Io(e),
|
|
||||||
BlockingError::Canceled => PayloadError::Io(io::Error::new(
|
|
||||||
io::ErrorKind::Other,
|
io::ErrorKind::Other,
|
||||||
"Operation is canceled",
|
"Operation is canceled",
|
||||||
)),
|
))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -970,8 +969,9 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "actors")]
|
#[cfg(feature = "actors")]
|
||||||
/// `InternalServerError` for `actix::MailboxError`
|
/// Returns [`StatusCode::INTERNAL_SERVER_ERROR`] for [`actix::MailboxError`].
|
||||||
/// This is supported on feature=`actors` only
|
///
|
||||||
|
/// This is only supported when the feature `actors` is enabled.
|
||||||
impl ResponseError for actix::MailboxError {}
|
impl ResponseError for actix::MailboxError {}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -223,15 +223,3 @@ impl Encoder<Message<(RequestHeadType, BodySize)>> for ClientCodec {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Writer<'a>(pub &'a mut BytesMut);
|
|
||||||
|
|
||||||
impl<'a> io::Write for Writer<'a> {
|
|
||||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
|
||||||
self.0.extend_from_slice(buf);
|
|
||||||
Ok(buf.len())
|
|
||||||
}
|
|
||||||
fn flush(&mut self) -> io::Result<()> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ use crate::header::HeaderMap;
|
||||||
use crate::message::{ConnectionType, ResponseHead};
|
use crate::message::{ConnectionType, ResponseHead};
|
||||||
use crate::request::Request;
|
use crate::request::Request;
|
||||||
|
|
||||||
const MAX_BUFFER_SIZE: usize = 131_072;
|
pub(crate) const MAX_BUFFER_SIZE: usize = 131_072;
|
||||||
const MAX_HEADERS: usize = 96;
|
const MAX_HEADERS: usize = 96;
|
||||||
|
|
||||||
/// Incoming message decoder
|
/// Incoming message decoder
|
||||||
|
@ -203,7 +203,15 @@ impl MessageType for Request {
|
||||||
|
|
||||||
(len, method, uri, version, req.headers.len())
|
(len, method, uri, version, req.headers.len())
|
||||||
}
|
}
|
||||||
httparse::Status::Partial => return Ok(None),
|
httparse::Status::Partial => {
|
||||||
|
return if src.len() >= MAX_BUFFER_SIZE {
|
||||||
|
trace!("MAX_BUFFER_SIZE unprocessed data reached, closing");
|
||||||
|
Err(ParseError::TooLarge)
|
||||||
|
} else {
|
||||||
|
// Return None to notify more read are needed for parsing request
|
||||||
|
Ok(None)
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -222,9 +230,6 @@ impl MessageType for Request {
|
||||||
PayloadLength::None => {
|
PayloadLength::None => {
|
||||||
if method == Method::CONNECT {
|
if method == Method::CONNECT {
|
||||||
PayloadType::Stream(PayloadDecoder::eof())
|
PayloadType::Stream(PayloadDecoder::eof())
|
||||||
} else if src.len() >= MAX_BUFFER_SIZE {
|
|
||||||
trace!("MAX_BUFFER_SIZE unprocessed data reached, closing");
|
|
||||||
return Err(ParseError::TooLarge);
|
|
||||||
} else {
|
} else {
|
||||||
PayloadType::None
|
PayloadType::None
|
||||||
}
|
}
|
||||||
|
@ -273,7 +278,14 @@ impl MessageType for ResponseHead {
|
||||||
|
|
||||||
(len, version, status, res.headers.len())
|
(len, version, status, res.headers.len())
|
||||||
}
|
}
|
||||||
httparse::Status::Partial => return Ok(None),
|
httparse::Status::Partial => {
|
||||||
|
return if src.len() >= MAX_BUFFER_SIZE {
|
||||||
|
error!("MAX_BUFFER_SIZE unprocessed data reached, closing");
|
||||||
|
Err(ParseError::TooLarge)
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -289,9 +301,6 @@ impl MessageType for ResponseHead {
|
||||||
} else if status == StatusCode::SWITCHING_PROTOCOLS {
|
} else if status == StatusCode::SWITCHING_PROTOCOLS {
|
||||||
// switching protocol or connect
|
// switching protocol or connect
|
||||||
PayloadType::Stream(PayloadDecoder::eof())
|
PayloadType::Stream(PayloadDecoder::eof())
|
||||||
} else if src.len() >= MAX_BUFFER_SIZE {
|
|
||||||
error!("MAX_BUFFER_SIZE unprocessed data reached, closing");
|
|
||||||
return Err(ParseError::TooLarge);
|
|
||||||
} else {
|
} else {
|
||||||
// for HTTP/1.0 read to eof and close connection
|
// for HTTP/1.0 read to eof and close connection
|
||||||
if msg.version == Version::HTTP_10 {
|
if msg.version == Version::HTTP_10 {
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use std::{
|
use std::{
|
||||||
cell::RefCell,
|
|
||||||
collections::VecDeque,
|
collections::VecDeque,
|
||||||
fmt,
|
fmt,
|
||||||
future::Future,
|
future::Future,
|
||||||
|
@ -30,8 +29,8 @@ use super::codec::Codec;
|
||||||
use super::payload::{Payload, PayloadSender, PayloadStatus};
|
use super::payload::{Payload, PayloadSender, PayloadStatus};
|
||||||
use super::{Message, MessageType};
|
use super::{Message, MessageType};
|
||||||
|
|
||||||
const LW_BUFFER_SIZE: usize = 4096;
|
const LW_BUFFER_SIZE: usize = 1024;
|
||||||
const HW_BUFFER_SIZE: usize = 32_768;
|
const HW_BUFFER_SIZE: usize = 1024 * 8;
|
||||||
const MAX_PIPELINED_MESSAGES: usize = 16;
|
const MAX_PIPELINED_MESSAGES: usize = 16;
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
|
@ -91,7 +90,7 @@ where
|
||||||
U: Service<(Request, Framed<T, Codec>), Response = ()>,
|
U: Service<(Request, Framed<T, Codec>), Response = ()>,
|
||||||
U::Error: fmt::Display,
|
U::Error: fmt::Display,
|
||||||
{
|
{
|
||||||
flow: Rc<RefCell<HttpFlow<S, X, U>>>,
|
flow: Rc<HttpFlow<S, X, U>>,
|
||||||
on_connect_data: OnConnectData,
|
on_connect_data: OnConnectData,
|
||||||
flags: Flags,
|
flags: Flags,
|
||||||
peer_addr: Option<net::SocketAddr>,
|
peer_addr: Option<net::SocketAddr>,
|
||||||
|
@ -177,7 +176,7 @@ where
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
stream: T,
|
stream: T,
|
||||||
config: ServiceConfig,
|
config: ServiceConfig,
|
||||||
services: Rc<RefCell<HttpFlow<S, X, U>>>,
|
services: Rc<HttpFlow<S, X, U>>,
|
||||||
on_connect_data: OnConnectData,
|
on_connect_data: OnConnectData,
|
||||||
peer_addr: Option<net::SocketAddr>,
|
peer_addr: Option<net::SocketAddr>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
@ -200,7 +199,7 @@ where
|
||||||
config: ServiceConfig,
|
config: ServiceConfig,
|
||||||
read_buf: BytesMut,
|
read_buf: BytesMut,
|
||||||
timeout: Option<Sleep>,
|
timeout: Option<Sleep>,
|
||||||
services: Rc<RefCell<HttpFlow<S, X, U>>>,
|
services: Rc<HttpFlow<S, X, U>>,
|
||||||
on_connect_data: OnConnectData,
|
on_connect_data: OnConnectData,
|
||||||
peer_addr: Option<net::SocketAddr>,
|
peer_addr: Option<net::SocketAddr>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
@ -377,7 +376,7 @@ where
|
||||||
Poll::Ready(Ok(req)) => {
|
Poll::Ready(Ok(req)) => {
|
||||||
self.as_mut().send_continue();
|
self.as_mut().send_continue();
|
||||||
this = self.as_mut().project();
|
this = self.as_mut().project();
|
||||||
let fut = this.flow.borrow_mut().service.call(req);
|
let fut = this.flow.service.call(req);
|
||||||
this.state.set(State::ServiceCall(fut));
|
this.state.set(State::ServiceCall(fut));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -405,7 +404,7 @@ where
|
||||||
},
|
},
|
||||||
StateProj::SendPayload(mut stream) => {
|
StateProj::SendPayload(mut stream) => {
|
||||||
loop {
|
loop {
|
||||||
if this.write_buf.len() < HW_BUFFER_SIZE {
|
if this.write_buf.len() < super::payload::MAX_BUFFER_SIZE {
|
||||||
match stream.as_mut().poll_next(cx) {
|
match stream.as_mut().poll_next(cx) {
|
||||||
Poll::Ready(Some(Ok(item))) => {
|
Poll::Ready(Some(Ok(item))) => {
|
||||||
this.codec.encode(
|
this.codec.encode(
|
||||||
|
@ -467,12 +466,12 @@ where
|
||||||
if req.head().expect() {
|
if req.head().expect() {
|
||||||
// set dispatcher state so the future is pinned.
|
// set dispatcher state so the future is pinned.
|
||||||
let mut this = self.as_mut().project();
|
let mut this = self.as_mut().project();
|
||||||
let task = this.flow.borrow_mut().expect.call(req);
|
let task = this.flow.expect.call(req);
|
||||||
this.state.set(State::ExpectCall(task));
|
this.state.set(State::ExpectCall(task));
|
||||||
} else {
|
} else {
|
||||||
// the same as above.
|
// the same as above.
|
||||||
let mut this = self.as_mut().project();
|
let mut this = self.as_mut().project();
|
||||||
let task = this.flow.borrow_mut().service.call(req);
|
let task = this.flow.service.call(req);
|
||||||
this.state.set(State::ServiceCall(task));
|
this.state.set(State::ServiceCall(task));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -485,7 +484,7 @@ where
|
||||||
Poll::Ready(Ok(req)) => {
|
Poll::Ready(Ok(req)) => {
|
||||||
self.as_mut().send_continue();
|
self.as_mut().send_continue();
|
||||||
let mut this = self.as_mut().project();
|
let mut this = self.as_mut().project();
|
||||||
let task = this.flow.borrow_mut().service.call(req);
|
let task = this.flow.service.call(req);
|
||||||
this.state.set(State::ServiceCall(task));
|
this.state.set(State::ServiceCall(task));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -556,9 +555,7 @@ where
|
||||||
// merge on_connect_ext data into request extensions
|
// merge on_connect_ext data into request extensions
|
||||||
this.on_connect_data.merge_into(&mut req);
|
this.on_connect_data.merge_into(&mut req);
|
||||||
|
|
||||||
if pl == MessageType::Stream
|
if pl == MessageType::Stream && this.flow.upgrade.is_some() {
|
||||||
&& this.flow.borrow().upgrade.is_some()
|
|
||||||
{
|
|
||||||
this.messages.push_back(DispatcherMessage::Upgrade(req));
|
this.messages.push_back(DispatcherMessage::Upgrade(req));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -608,6 +605,8 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// decode is partial and buffer is not full yet.
|
||||||
|
// break and wait for more read.
|
||||||
Ok(None) => break,
|
Ok(None) => break,
|
||||||
Err(ParseError::Io(e)) => {
|
Err(ParseError::Io(e)) => {
|
||||||
self.as_mut().client_disconnected();
|
self.as_mut().client_disconnected();
|
||||||
|
@ -615,6 +614,18 @@ where
|
||||||
*this.error = Some(DispatchError::Io(e));
|
*this.error = Some(DispatchError::Io(e));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
Err(ParseError::TooLarge) => {
|
||||||
|
if let Some(mut payload) = this.payload.take() {
|
||||||
|
payload.set_error(PayloadError::Overflow);
|
||||||
|
}
|
||||||
|
// Requests overflow buffer size should be responded with 413
|
||||||
|
this.messages.push_back(DispatcherMessage::Error(
|
||||||
|
Response::PayloadTooLarge().finish().drop_body(),
|
||||||
|
));
|
||||||
|
this.flags.insert(Flags::READ_DISCONNECT);
|
||||||
|
*this.error = Some(ParseError::TooLarge.into());
|
||||||
|
break;
|
||||||
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
if let Some(mut payload) = this.payload.take() {
|
if let Some(mut payload) = this.payload.take() {
|
||||||
payload.set_error(PayloadError::EncodingCorrupted);
|
payload.set_error(PayloadError::EncodingCorrupted);
|
||||||
|
@ -723,6 +734,78 @@ where
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true when io stream can be disconnected after write to it.
|
||||||
|
///
|
||||||
|
/// It covers these conditions:
|
||||||
|
///
|
||||||
|
/// - `std::io::ErrorKind::ConnectionReset` after partial read.
|
||||||
|
/// - all data read done.
|
||||||
|
#[inline(always)]
|
||||||
|
fn read_available(
|
||||||
|
self: Pin<&mut Self>,
|
||||||
|
cx: &mut Context<'_>,
|
||||||
|
) -> Result<bool, DispatchError> {
|
||||||
|
let this = self.project();
|
||||||
|
|
||||||
|
if this.flags.contains(Flags::READ_DISCONNECT) {
|
||||||
|
return Ok(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut io = Pin::new(this.io.as_mut().unwrap());
|
||||||
|
|
||||||
|
let mut read_some = false;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
// grow buffer if necessary.
|
||||||
|
let remaining = this.read_buf.capacity() - this.read_buf.len();
|
||||||
|
if remaining < LW_BUFFER_SIZE {
|
||||||
|
this.read_buf.reserve(HW_BUFFER_SIZE - remaining);
|
||||||
|
}
|
||||||
|
|
||||||
|
match actix_codec::poll_read_buf(io.as_mut(), cx, this.read_buf) {
|
||||||
|
Poll::Pending => return Ok(false),
|
||||||
|
Poll::Ready(Ok(n)) => {
|
||||||
|
if n == 0 {
|
||||||
|
return Ok(true);
|
||||||
|
} else {
|
||||||
|
// Return early when read buf exceed decoder's max buffer size.
|
||||||
|
if this.read_buf.len() >= super::decoder::MAX_BUFFER_SIZE {
|
||||||
|
// at this point it's not known io is still scheduled to
|
||||||
|
// be waked up. so force wake up dispatcher just in case.
|
||||||
|
// TODO: figure out the overhead.
|
||||||
|
cx.waker().wake_by_ref();
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
read_some = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Poll::Ready(Err(err)) => {
|
||||||
|
return if err.kind() == io::ErrorKind::WouldBlock {
|
||||||
|
Ok(false)
|
||||||
|
} else if err.kind() == io::ErrorKind::ConnectionReset && read_some {
|
||||||
|
Ok(true)
|
||||||
|
} else {
|
||||||
|
Err(DispatchError::Io(err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// call upgrade service with request.
|
||||||
|
fn upgrade(self: Pin<&mut Self>, req: Request) -> U::Future {
|
||||||
|
let this = self.project();
|
||||||
|
let mut parts = FramedParts::with_read_buf(
|
||||||
|
this.io.take().unwrap(),
|
||||||
|
mem::take(this.codec),
|
||||||
|
mem::take(this.read_buf),
|
||||||
|
);
|
||||||
|
parts.write_buf = mem::take(this.write_buf);
|
||||||
|
let framed = Framed::from_parts(parts);
|
||||||
|
this.flow.upgrade.as_ref().unwrap().call((req, framed))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, S, B, X, U> Future for Dispatcher<T, S, B, X, U>
|
impl<T, S, B, X, U> Future for Dispatcher<T, S, B, X, U>
|
||||||
|
@ -767,61 +850,36 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// read socket into a buf
|
// read from io stream and fill read buffer.
|
||||||
let should_disconnect =
|
let should_disconnect = inner.as_mut().read_available(cx)?;
|
||||||
if !inner.flags.contains(Flags::READ_DISCONNECT) {
|
|
||||||
let mut inner_p = inner.as_mut().project();
|
|
||||||
read_available(
|
|
||||||
cx,
|
|
||||||
inner_p.io.as_mut().unwrap(),
|
|
||||||
&mut inner_p.read_buf,
|
|
||||||
)?
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
inner.as_mut().poll_request(cx)?;
|
inner.as_mut().poll_request(cx)?;
|
||||||
if let Some(true) = should_disconnect {
|
|
||||||
let inner_p = inner.as_mut().project();
|
// io stream should to be closed.
|
||||||
inner_p.flags.insert(Flags::READ_DISCONNECT);
|
if should_disconnect {
|
||||||
if let Some(mut payload) = inner_p.payload.take() {
|
let inner = inner.as_mut().project();
|
||||||
|
inner.flags.insert(Flags::READ_DISCONNECT);
|
||||||
|
if let Some(mut payload) = inner.payload.take() {
|
||||||
payload.feed_eof();
|
payload.feed_eof();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let inner_p = inner.as_mut().project();
|
// poll_response and populate write buffer.
|
||||||
let remaining =
|
// drain indicate if write buffer should be emptied before next run.
|
||||||
inner_p.write_buf.capacity() - inner_p.write_buf.len();
|
let drain = match inner.as_mut().poll_response(cx)? {
|
||||||
if remaining < LW_BUFFER_SIZE {
|
PollResponse::DrainWriteBuf => true,
|
||||||
inner_p.write_buf.reserve(HW_BUFFER_SIZE - remaining);
|
PollResponse::DoNothing => false,
|
||||||
}
|
// upgrade request and goes Upgrade variant of DispatcherState.
|
||||||
let result = inner.as_mut().poll_response(cx)?;
|
PollResponse::Upgrade(req) => {
|
||||||
let drain = result == PollResponse::DrainWriteBuf;
|
let upgrade = inner.upgrade(req);
|
||||||
|
|
||||||
// switch to upgrade handler
|
|
||||||
if let PollResponse::Upgrade(req) = result {
|
|
||||||
let inner_p = inner.as_mut().project();
|
|
||||||
let mut parts = FramedParts::with_read_buf(
|
|
||||||
inner_p.io.take().unwrap(),
|
|
||||||
mem::take(inner_p.codec),
|
|
||||||
mem::take(inner_p.read_buf),
|
|
||||||
);
|
|
||||||
parts.write_buf = mem::take(inner_p.write_buf);
|
|
||||||
let framed = Framed::from_parts(parts);
|
|
||||||
let upgrade = inner_p
|
|
||||||
.flow
|
|
||||||
.borrow_mut()
|
|
||||||
.upgrade
|
|
||||||
.take()
|
|
||||||
.unwrap()
|
|
||||||
.call((req, framed));
|
|
||||||
self.as_mut()
|
self.as_mut()
|
||||||
.project()
|
.project()
|
||||||
.inner
|
.inner
|
||||||
.set(DispatcherState::Upgrade(upgrade));
|
.set(DispatcherState::Upgrade(upgrade));
|
||||||
return self.poll(cx);
|
return self.poll(cx);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// we didn't get WouldBlock from write operation,
|
// we didn't get WouldBlock from write operation,
|
||||||
// so data get written to kernel completely (macOS)
|
// so data get written to kernel completely (macOS)
|
||||||
|
@ -878,61 +936,6 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns either:
|
|
||||||
/// - `Ok(Some(true))` - data was read and done reading all data.
|
|
||||||
/// - `Ok(Some(false))` - data was read but there should be more to read.
|
|
||||||
/// - `Ok(None)` - no data was read but there should be more to read later.
|
|
||||||
/// - Unhandled Errors
|
|
||||||
fn read_available<T>(
|
|
||||||
cx: &mut Context<'_>,
|
|
||||||
io: &mut T,
|
|
||||||
buf: &mut BytesMut,
|
|
||||||
) -> Result<Option<bool>, io::Error>
|
|
||||||
where
|
|
||||||
T: AsyncRead + Unpin,
|
|
||||||
{
|
|
||||||
let mut read_some = false;
|
|
||||||
|
|
||||||
loop {
|
|
||||||
// If buf is full return but do not disconnect since
|
|
||||||
// there is more reading to be done
|
|
||||||
if buf.len() >= HW_BUFFER_SIZE {
|
|
||||||
return Ok(Some(false));
|
|
||||||
}
|
|
||||||
|
|
||||||
let remaining = buf.capacity() - buf.len();
|
|
||||||
if remaining < LW_BUFFER_SIZE {
|
|
||||||
buf.reserve(HW_BUFFER_SIZE - remaining);
|
|
||||||
}
|
|
||||||
|
|
||||||
match actix_codec::poll_read_buf(Pin::new(io), cx, buf) {
|
|
||||||
Poll::Pending => {
|
|
||||||
return if read_some { Ok(Some(false)) } else { Ok(None) };
|
|
||||||
}
|
|
||||||
Poll::Ready(Ok(n)) => {
|
|
||||||
if n == 0 {
|
|
||||||
return Ok(Some(true));
|
|
||||||
} else {
|
|
||||||
read_some = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Poll::Ready(Err(err)) => {
|
|
||||||
return if err.kind() == io::ErrorKind::WouldBlock {
|
|
||||||
if read_some {
|
|
||||||
Ok(Some(false))
|
|
||||||
} else {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
} else if err.kind() == io::ErrorKind::ConnectionReset && read_some {
|
|
||||||
Ok(Some(true))
|
|
||||||
} else {
|
|
||||||
Err(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::str;
|
use std::str;
|
||||||
|
|
|
@ -337,7 +337,7 @@ impl MessageType for RequestHeadType {
|
||||||
let head = self.as_ref();
|
let head = self.as_ref();
|
||||||
dst.reserve(256 + head.headers.len() * AVERAGE_HEADER_SIZE);
|
dst.reserve(256 + head.headers.len() * AVERAGE_HEADER_SIZE);
|
||||||
write!(
|
write!(
|
||||||
Writer(dst),
|
helpers::Writer(dst),
|
||||||
"{} {} {}",
|
"{} {} {}",
|
||||||
head.method,
|
head.method,
|
||||||
head.uri.path_and_query().map(|u| u.as_str()).unwrap_or("/"),
|
head.uri.path_and_query().map(|u| u.as_str()).unwrap_or("/"),
|
||||||
|
@ -470,7 +470,7 @@ impl TransferEncoding {
|
||||||
*eof = true;
|
*eof = true;
|
||||||
buf.extend_from_slice(b"0\r\n\r\n");
|
buf.extend_from_slice(b"0\r\n\r\n");
|
||||||
} else {
|
} else {
|
||||||
writeln!(Writer(buf), "{:X}\r", msg.len())
|
writeln!(helpers::Writer(buf), "{:X}\r", msg.len())
|
||||||
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
|
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
|
||||||
|
|
||||||
buf.reserve(msg.len() + 2);
|
buf.reserve(msg.len() + 2);
|
||||||
|
@ -520,18 +520,6 @@ impl TransferEncoding {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Writer<'a>(pub &'a mut BytesMut);
|
|
||||||
|
|
||||||
impl<'a> io::Write for Writer<'a> {
|
|
||||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
|
||||||
self.0.extend_from_slice(buf);
|
|
||||||
Ok(buf.len())
|
|
||||||
}
|
|
||||||
fn flush(&mut self) -> io::Result<()> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// Callers must ensure that the given length matches given value length.
|
/// 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) {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use std::task::{Context, Poll};
|
use std::task::Poll;
|
||||||
|
|
||||||
use actix_service::{Service, ServiceFactory};
|
use actix_service::{Service, ServiceFactory};
|
||||||
use futures_util::future::{ready, Ready};
|
use futures_util::future::{ready, Ready};
|
||||||
|
@ -26,11 +26,9 @@ impl Service<Request> for ExpectHandler {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Future = Ready<Result<Self::Response, Self::Error>>;
|
type Future = Ready<Result<Self::Response, Self::Error>>;
|
||||||
|
|
||||||
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
actix_service::always_ready!();
|
||||||
Poll::Ready(Ok(()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn call(&mut self, req: Request) -> Self::Future {
|
fn call(&self, req: Request) -> Self::Future {
|
||||||
ready(Ok(req))
|
ready(Ok(req))
|
||||||
// TODO: add some way to trigger error
|
// TODO: add some way to trigger error
|
||||||
// Err(error::ErrorExpectationFailed("test"))
|
// Err(error::ErrorExpectationFailed("test"))
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
@ -367,7 +366,7 @@ where
|
||||||
X: Service<Request>,
|
X: Service<Request>,
|
||||||
U: Service<(Request, Framed<T, Codec>)>,
|
U: Service<(Request, Framed<T, Codec>)>,
|
||||||
{
|
{
|
||||||
flow: Rc<RefCell<HttpFlow<S, X, U>>>,
|
flow: Rc<HttpFlow<S, X, U>>,
|
||||||
on_connect_ext: Option<Rc<ConnectCallback<T>>>,
|
on_connect_ext: Option<Rc<ConnectCallback<T>>>,
|
||||||
cfg: ServiceConfig,
|
cfg: ServiceConfig,
|
||||||
_phantom: PhantomData<B>,
|
_phantom: PhantomData<B>,
|
||||||
|
@ -417,9 +416,9 @@ where
|
||||||
type Error = DispatchError;
|
type Error = DispatchError;
|
||||||
type Future = Dispatcher<T, S, B, X, U>;
|
type Future = Dispatcher<T, S, B, X, U>;
|
||||||
|
|
||||||
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||||
let mut flow = self.flow.borrow_mut();
|
let ready = self
|
||||||
let ready = flow
|
.flow
|
||||||
.expect
|
.expect
|
||||||
.poll_ready(cx)
|
.poll_ready(cx)
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
|
@ -429,7 +428,8 @@ where
|
||||||
})?
|
})?
|
||||||
.is_ready();
|
.is_ready();
|
||||||
|
|
||||||
let ready = flow
|
let ready = self
|
||||||
|
.flow
|
||||||
.service
|
.service
|
||||||
.poll_ready(cx)
|
.poll_ready(cx)
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
|
@ -440,7 +440,7 @@ where
|
||||||
.is_ready()
|
.is_ready()
|
||||||
&& ready;
|
&& ready;
|
||||||
|
|
||||||
let ready = if let Some(ref mut upg) = flow.upgrade {
|
let ready = if let Some(ref upg) = self.flow.upgrade {
|
||||||
upg.poll_ready(cx)
|
upg.poll_ready(cx)
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
let e = e.into();
|
let e = e.into();
|
||||||
|
@ -460,7 +460,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call(&mut self, (io, addr): (T, Option<net::SocketAddr>)) -> Self::Future {
|
fn call(&self, (io, addr): (T, Option<net::SocketAddr>)) -> Self::Future {
|
||||||
let on_connect_data =
|
let on_connect_data =
|
||||||
OnConnectData::from_io(&io, self.on_connect_ext.as_deref());
|
OnConnectData::from_io(&io, self.on_connect_ext.as_deref());
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use std::task::{Context, Poll};
|
use std::task::Poll;
|
||||||
|
|
||||||
use actix_codec::Framed;
|
use actix_codec::Framed;
|
||||||
use actix_service::{Service, ServiceFactory};
|
use actix_service::{Service, ServiceFactory};
|
||||||
|
@ -28,11 +28,9 @@ impl<T> Service<(Request, Framed<T, Codec>)> for UpgradeHandler {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Future = Ready<Result<Self::Response, Self::Error>>;
|
type Future = Ready<Result<Self::Response, Self::Error>>;
|
||||||
|
|
||||||
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
actix_service::always_ready!();
|
||||||
Poll::Ready(Ok(()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn call(&mut self, _: (Request, Framed<T, Codec>)) -> Self::Future {
|
fn call(&self, _: (Request, Framed<T, Codec>)) -> Self::Future {
|
||||||
ready(Ok(()))
|
ready(Ok(()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::net;
|
use std::net;
|
||||||
|
@ -37,7 +36,7 @@ where
|
||||||
S: Service<Request>,
|
S: Service<Request>,
|
||||||
B: MessageBody,
|
B: MessageBody,
|
||||||
{
|
{
|
||||||
flow: Rc<RefCell<HttpFlow<S, X, U>>>,
|
flow: Rc<HttpFlow<S, X, U>>,
|
||||||
connection: Connection<T, Bytes>,
|
connection: Connection<T, Bytes>,
|
||||||
on_connect_data: OnConnectData,
|
on_connect_data: OnConnectData,
|
||||||
config: ServiceConfig,
|
config: ServiceConfig,
|
||||||
|
@ -56,7 +55,7 @@ where
|
||||||
B: MessageBody,
|
B: MessageBody,
|
||||||
{
|
{
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
services: Rc<RefCell<HttpFlow<S, X, U>>>,
|
flow: Rc<HttpFlow<S, X, U>>,
|
||||||
connection: Connection<T, Bytes>,
|
connection: Connection<T, Bytes>,
|
||||||
on_connect_data: OnConnectData,
|
on_connect_data: OnConnectData,
|
||||||
config: ServiceConfig,
|
config: ServiceConfig,
|
||||||
|
@ -80,7 +79,7 @@ where
|
||||||
};
|
};
|
||||||
|
|
||||||
Dispatcher {
|
Dispatcher {
|
||||||
flow: services,
|
flow,
|
||||||
config,
|
config,
|
||||||
peer_addr,
|
peer_addr,
|
||||||
connection,
|
connection,
|
||||||
|
@ -138,7 +137,7 @@ where
|
||||||
|
|
||||||
let svc = ServiceResponse::<S::Future, S::Response, S::Error, B> {
|
let svc = ServiceResponse::<S::Future, S::Response, S::Error, B> {
|
||||||
state: ServiceResponseState::ServiceCall(
|
state: ServiceResponseState::ServiceCall(
|
||||||
this.flow.borrow_mut().service.call(req),
|
this.flow.service.call(req),
|
||||||
Some(res),
|
Some(res),
|
||||||
),
|
),
|
||||||
config: this.config.clone(),
|
config: this.config.clone(),
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
@ -249,7 +248,7 @@ pub struct H2ServiceHandler<T, S, B>
|
||||||
where
|
where
|
||||||
S: Service<Request>,
|
S: Service<Request>,
|
||||||
{
|
{
|
||||||
flow: Rc<RefCell<HttpFlow<S, (), ()>>>,
|
flow: Rc<HttpFlow<S, (), ()>>,
|
||||||
cfg: ServiceConfig,
|
cfg: ServiceConfig,
|
||||||
on_connect_ext: Option<Rc<ConnectCallback<T>>>,
|
on_connect_ext: Option<Rc<ConnectCallback<T>>>,
|
||||||
_phantom: PhantomData<B>,
|
_phantom: PhantomData<B>,
|
||||||
|
@ -290,15 +289,15 @@ where
|
||||||
type Error = DispatchError;
|
type Error = DispatchError;
|
||||||
type Future = H2ServiceHandlerResponse<T, S, B>;
|
type Future = H2ServiceHandlerResponse<T, S, B>;
|
||||||
|
|
||||||
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||||
self.flow.borrow_mut().service.poll_ready(cx).map_err(|e| {
|
self.flow.service.poll_ready(cx).map_err(|e| {
|
||||||
let e = e.into();
|
let e = e.into();
|
||||||
error!("Service readiness error: {:?}", e);
|
error!("Service readiness error: {:?}", e);
|
||||||
DispatchError::Service(e)
|
DispatchError::Service(e)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call(&mut self, (io, addr): (T, Option<net::SocketAddr>)) -> Self::Future {
|
fn call(&self, (io, addr): (T, Option<net::SocketAddr>)) -> Self::Future {
|
||||||
let on_connect_data =
|
let on_connect_data =
|
||||||
OnConnectData::from_io(&io, self.on_connect_ext.as_deref());
|
OnConnectData::from_io(&io, self.on_connect_ext.as_deref());
|
||||||
|
|
||||||
|
@ -321,7 +320,7 @@ where
|
||||||
{
|
{
|
||||||
Incoming(Dispatcher<T, S, B, (), ()>),
|
Incoming(Dispatcher<T, S, B, (), ()>),
|
||||||
Handshake(
|
Handshake(
|
||||||
Option<Rc<RefCell<HttpFlow<S, (), ()>>>>,
|
Option<Rc<HttpFlow<S, (), ()>>>,
|
||||||
Option<ServiceConfig>,
|
Option<ServiceConfig>,
|
||||||
Option<net::SocketAddr>,
|
Option<net::SocketAddr>,
|
||||||
OnConnectData,
|
OnConnectData,
|
||||||
|
|
|
@ -126,6 +126,6 @@ impl IntoHeaderValue for Mime {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn try_into_value(self) -> Result<HeaderValue, Self::Error> {
|
fn try_into_value(self) -> Result<HeaderValue, Self::Error> {
|
||||||
HeaderValue::try_from(format!("{}", self))
|
HeaderValue::from_str(self.as_ref())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -343,6 +343,8 @@ impl ResponseHead {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Message<T: Head> {
|
pub struct Message<T: Head> {
|
||||||
|
// Rc here should not be cloned by anyone.
|
||||||
|
// It's used to reuse allocation of T and no shared ownership is allowed.
|
||||||
head: Rc<T>,
|
head: Rc<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -353,14 +355,6 @@ impl<T: Head> Message<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Head> Clone for Message<T> {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
Message {
|
|
||||||
head: self.head.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Head> std::ops::Deref for Message<T> {
|
impl<T: Head> std::ops::Deref for Message<T> {
|
||||||
type Target = T;
|
type Target = T;
|
||||||
|
|
||||||
|
@ -377,10 +371,8 @@ impl<T: Head> std::ops::DerefMut for Message<T> {
|
||||||
|
|
||||||
impl<T: Head> Drop for Message<T> {
|
impl<T: Head> Drop for Message<T> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
if Rc::strong_count(&self.head) == 1 {
|
|
||||||
T::with_pool(|p| p.release(self.head.clone()))
|
T::with_pool(|p| p.release(self.head.clone()))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct BoxedResponseHead {
|
pub(crate) struct BoxedResponseHead {
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::task::{Context, Poll};
|
use std::task::{Context, Poll};
|
||||||
|
@ -440,7 +439,7 @@ where
|
||||||
X: Service<Request>,
|
X: Service<Request>,
|
||||||
U: Service<(Request, Framed<T, h1::Codec>)>,
|
U: Service<(Request, Framed<T, h1::Codec>)>,
|
||||||
{
|
{
|
||||||
flow: Rc<RefCell<HttpFlow<S, X, U>>>,
|
flow: Rc<HttpFlow<S, X, U>>,
|
||||||
cfg: ServiceConfig,
|
cfg: ServiceConfig,
|
||||||
on_connect_ext: Option<Rc<ConnectCallback<T>>>,
|
on_connect_ext: Option<Rc<ConnectCallback<T>>>,
|
||||||
_phantom: PhantomData<B>,
|
_phantom: PhantomData<B>,
|
||||||
|
@ -454,12 +453,12 @@ pub(super) struct HttpFlow<S, X, U> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S, X, U> HttpFlow<S, X, U> {
|
impl<S, X, U> HttpFlow<S, X, U> {
|
||||||
pub(super) fn new(service: S, expect: X, upgrade: Option<U>) -> Rc<RefCell<Self>> {
|
pub(super) fn new(service: S, expect: X, upgrade: Option<U>) -> Rc<Self> {
|
||||||
Rc::new(RefCell::new(Self {
|
Rc::new(Self {
|
||||||
service,
|
service,
|
||||||
expect,
|
expect,
|
||||||
upgrade,
|
upgrade,
|
||||||
}))
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -509,9 +508,9 @@ where
|
||||||
type Error = DispatchError;
|
type Error = DispatchError;
|
||||||
type Future = HttpServiceHandlerResponse<T, S, B, X, U>;
|
type Future = HttpServiceHandlerResponse<T, S, B, X, U>;
|
||||||
|
|
||||||
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||||
let mut flow = self.flow.borrow_mut();
|
let ready = self
|
||||||
let ready = flow
|
.flow
|
||||||
.expect
|
.expect
|
||||||
.poll_ready(cx)
|
.poll_ready(cx)
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
|
@ -521,7 +520,8 @@ where
|
||||||
})?
|
})?
|
||||||
.is_ready();
|
.is_ready();
|
||||||
|
|
||||||
let ready = flow
|
let ready = self
|
||||||
|
.flow
|
||||||
.service
|
.service
|
||||||
.poll_ready(cx)
|
.poll_ready(cx)
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
|
@ -532,7 +532,7 @@ where
|
||||||
.is_ready()
|
.is_ready()
|
||||||
&& ready;
|
&& ready;
|
||||||
|
|
||||||
let ready = if let Some(ref mut upg) = flow.upgrade {
|
let ready = if let Some(ref upg) = self.flow.upgrade {
|
||||||
upg.poll_ready(cx)
|
upg.poll_ready(cx)
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
let e = e.into();
|
let e = e.into();
|
||||||
|
@ -553,7 +553,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call(
|
fn call(
|
||||||
&mut self,
|
&self,
|
||||||
(io, proto, peer_addr): (T, Protocol, Option<net::SocketAddr>),
|
(io, proto, peer_addr): (T, Protocol, Option<net::SocketAddr>),
|
||||||
) -> Self::Future {
|
) -> Self::Future {
|
||||||
let on_connect_data =
|
let on_connect_data =
|
||||||
|
@ -604,7 +604,7 @@ where
|
||||||
Option<(
|
Option<(
|
||||||
Handshake<T, Bytes>,
|
Handshake<T, Bytes>,
|
||||||
ServiceConfig,
|
ServiceConfig,
|
||||||
Rc<RefCell<HttpFlow<S, X, U>>>,
|
Rc<HttpFlow<S, X, U>>,
|
||||||
OnConnectData,
|
OnConnectData,
|
||||||
Option<net::SocketAddr>,
|
Option<net::SocketAddr>,
|
||||||
)>,
|
)>,
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
#![cfg(feature = "openssl")]
|
#![cfg(feature = "openssl")]
|
||||||
|
|
||||||
|
extern crate tls_openssl as openssl;
|
||||||
|
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
use actix_http::error::{ErrorBadRequest, PayloadError};
|
use actix_http::error::{ErrorBadRequest, PayloadError};
|
||||||
|
@ -11,7 +14,7 @@ use actix_service::{fn_service, ServiceFactoryExt};
|
||||||
use bytes::{Bytes, BytesMut};
|
use bytes::{Bytes, BytesMut};
|
||||||
use futures_util::future::{err, ok, ready};
|
use futures_util::future::{err, ok, ready};
|
||||||
use futures_util::stream::{once, Stream, StreamExt};
|
use futures_util::stream::{once, Stream, StreamExt};
|
||||||
use open_ssl::ssl::{AlpnError, SslAcceptor, SslFiletype, SslMethod};
|
use openssl::ssl::{AlpnError, SslAcceptor, SslFiletype, SslMethod};
|
||||||
|
|
||||||
async fn load_body<S>(stream: S) -> Result<BytesMut, PayloadError>
|
async fn load_body<S>(stream: S) -> Result<BytesMut, PayloadError>
|
||||||
where
|
where
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
#![cfg(feature = "rustls")]
|
#![cfg(feature = "rustls")]
|
||||||
|
|
||||||
|
extern crate tls_rustls as rustls;
|
||||||
|
|
||||||
use actix_http::error::PayloadError;
|
use actix_http::error::PayloadError;
|
||||||
use actix_http::http::header::{self, HeaderName, HeaderValue};
|
use actix_http::http::header::{self, HeaderName, HeaderValue};
|
||||||
use actix_http::http::{Method, StatusCode, Version};
|
use actix_http::http::{Method, StatusCode, Version};
|
||||||
|
@ -9,7 +12,7 @@ use actix_service::{fn_factory_with_config, fn_service};
|
||||||
use bytes::{Bytes, BytesMut};
|
use bytes::{Bytes, BytesMut};
|
||||||
use futures_util::future::{self, err, ok};
|
use futures_util::future::{self, err, ok};
|
||||||
use futures_util::stream::{once, Stream, StreamExt};
|
use futures_util::stream::{once, Stream, StreamExt};
|
||||||
use rust_tls::{
|
use rustls::{
|
||||||
internal::pemfile::{certs, pkcs8_private_keys},
|
internal::pemfile::{certs, pkcs8_private_keys},
|
||||||
NoClientAuth, ServerConfig as RustlsServerConfig,
|
NoClientAuth, ServerConfig as RustlsServerConfig,
|
||||||
};
|
};
|
||||||
|
|
|
@ -21,7 +21,7 @@ impl<T> WsService<T> {
|
||||||
WsService(Arc::new(Mutex::new((PhantomData, Cell::new(false)))))
|
WsService(Arc::new(Mutex::new((PhantomData, Cell::new(false)))))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_polled(&mut self) {
|
fn set_polled(&self) {
|
||||||
*self.0.lock().unwrap().1.get_mut() = true;
|
*self.0.lock().unwrap().1.get_mut() = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,15 +44,12 @@ where
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Future = Pin<Box<dyn Future<Output = Result<(), Error>>>>;
|
type Future = Pin<Box<dyn Future<Output = Result<(), Error>>>>;
|
||||||
|
|
||||||
fn poll_ready(&mut self, _ctx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
fn poll_ready(&self, _ctx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||||
self.set_polled();
|
self.set_polled();
|
||||||
Poll::Ready(Ok(()))
|
Poll::Ready(Ok(()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call(
|
fn call(&self, (req, mut framed): (Request, Framed<T, h1::Codec>)) -> Self::Future {
|
||||||
&mut self,
|
|
||||||
(req, mut framed): (Request, Framed<T, h1::Codec>),
|
|
||||||
) -> Self::Future {
|
|
||||||
let fut = async move {
|
let fut = async move {
|
||||||
let res = ws::handshake(req.head()).unwrap().message_body(());
|
let res = ws::handshake(req.head()).unwrap().message_body(());
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ path = "src/lib.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix-web = { version = "4.0.0-beta.1", default-features = false }
|
actix-web = { version = "4.0.0-beta.1", default-features = false }
|
||||||
actix-utils = "3.0.0-beta.1"
|
actix-utils = "3.0.0-beta.2"
|
||||||
|
|
||||||
bytes = "1"
|
bytes = "1"
|
||||||
derive_more = "0.99.5"
|
derive_more = "0.99.5"
|
||||||
|
@ -28,5 +28,5 @@ mime = "0.3"
|
||||||
twoway = "0.2"
|
twoway = "0.2"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
actix-rt = "2.0.0-beta.2"
|
actix-rt = "2"
|
||||||
actix-http = "3.0.0-beta.1"
|
actix-http = "3.0.0-beta.1"
|
||||||
|
|
|
@ -16,7 +16,7 @@ name = "actix_web_actors"
|
||||||
path = "src/lib.rs"
|
path = "src/lib.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix = "0.11.0-beta.1"
|
actix = { version = "0.11.0-beta.2", default-features = false }
|
||||||
actix-codec = "0.4.0-beta.1"
|
actix-codec = "0.4.0-beta.1"
|
||||||
actix-http = "3.0.0-beta.1"
|
actix-http = "3.0.0-beta.1"
|
||||||
actix-web = { version = "4.0.0-beta.1", default-features = false }
|
actix-web = { version = "4.0.0-beta.1", default-features = false }
|
||||||
|
@ -28,6 +28,6 @@ pin-project = "1.0.0"
|
||||||
tokio = { version = "1", features = ["sync"] }
|
tokio = { version = "1", features = ["sync"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
actix-rt = "2.0.0-beta.2"
|
actix-rt = "2"
|
||||||
env_logger = "0.7"
|
env_logger = "0.8"
|
||||||
futures-util = { version = "0.3.7", default-features = false }
|
futures-util = { version = "0.3.7", default-features = false }
|
||||||
|
|
|
@ -233,14 +233,13 @@ mod tests {
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_default_resource() {
|
async fn test_default_resource() {
|
||||||
let mut srv =
|
let srv = init_service(App::new().service(web::resource("/test").to(|| {
|
||||||
init_service(App::new().service(web::resource("/test").to(|| {
|
|
||||||
HttpResponse::Ok().streaming(HttpContext::create(MyActor { count: 0 }))
|
HttpResponse::Ok().streaming(HttpContext::create(MyActor { count: 0 }))
|
||||||
})))
|
})))
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let req = TestRequest::with_uri("/test").to_request();
|
let req = TestRequest::with_uri("/test").to_request();
|
||||||
let resp = call_service(&mut srv, req).await;
|
let resp = call_service(&srv, req).await;
|
||||||
assert_eq!(resp.status(), StatusCode::OK);
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
|
|
||||||
let body = read_body(resp).await;
|
let body = read_body(resp).await;
|
||||||
|
|
|
@ -19,7 +19,7 @@ syn = { version = "1", features = ["full", "parsing"] }
|
||||||
proc-macro2 = "1"
|
proc-macro2 = "1"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
actix-rt = "2.0.0-beta.2"
|
actix-rt = "2"
|
||||||
actix-web = "4.0.0-beta.1"
|
actix-web = "4.0.0-beta.1"
|
||||||
futures-util = { version = "0.3.7", default-features = false }
|
futures-util = { version = "0.3.7", default-features = false }
|
||||||
trybuild = "1"
|
trybuild = "1"
|
||||||
|
|
|
@ -175,7 +175,6 @@ pub fn main(_: TokenStream, item: TokenStream) -> TokenStream {
|
||||||
let vis = &input.vis;
|
let vis = &input.vis;
|
||||||
let sig = &mut input.sig;
|
let sig = &mut input.sig;
|
||||||
let body = &input.block;
|
let body = &input.block;
|
||||||
let name = &sig.ident;
|
|
||||||
|
|
||||||
if sig.asyncness.is_none() {
|
if sig.asyncness.is_none() {
|
||||||
return syn::Error::new_spanned(sig.fn_token, "only async fn is supported")
|
return syn::Error::new_spanned(sig.fn_token, "only async fn is supported")
|
||||||
|
@ -188,7 +187,7 @@ pub fn main(_: TokenStream, item: TokenStream) -> TokenStream {
|
||||||
(quote! {
|
(quote! {
|
||||||
#(#attrs)*
|
#(#attrs)*
|
||||||
#vis #sig {
|
#vis #sig {
|
||||||
actix_web::rt::System::new(stringify!(#name))
|
actix_web::rt::System::new()
|
||||||
.block_on(async move { #body })
|
.block_on(async move { #body })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::pin::Pin;
|
|
||||||
use std::task::{Context, Poll};
|
use std::task::{Context, Poll};
|
||||||
|
|
||||||
use actix_web::dev::{Service, ServiceRequest, ServiceResponse, Transform};
|
use actix_web::dev::{Service, ServiceRequest, ServiceResponse, Transform};
|
||||||
|
@ -8,7 +7,7 @@ use actix_web::{http, test, web::Path, App, Error, HttpResponse, Responder};
|
||||||
use actix_web_codegen::{
|
use actix_web_codegen::{
|
||||||
connect, delete, get, head, options, patch, post, put, route, trace,
|
connect, delete, get, head, options, patch, post, put, route, trace,
|
||||||
};
|
};
|
||||||
use futures_util::future;
|
use futures_util::future::{self, LocalBoxFuture};
|
||||||
|
|
||||||
// Make sure that we can name function as 'config'
|
// Make sure that we can name function as 'config'
|
||||||
#[get("/config")]
|
#[get("/config")]
|
||||||
|
@ -117,14 +116,13 @@ where
|
||||||
{
|
{
|
||||||
type Response = ServiceResponse<B>;
|
type Response = ServiceResponse<B>;
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
#[allow(clippy::type_complexity)]
|
type Future = LocalBoxFuture<'static, 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(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||||
self.service.poll_ready(cx)
|
self.service.poll_ready(cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call(&mut self, req: ServiceRequest) -> Self::Future {
|
fn call(&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 {
|
||||||
|
|
|
@ -5,6 +5,9 @@
|
||||||
* `ClientRequest::insert_header` method which allows using typed headers. [#1869]
|
* `ClientRequest::insert_header` method which allows using typed headers. [#1869]
|
||||||
* `ClientRequest::append_header` method which allows using typed headers. [#1869]
|
* `ClientRequest::append_header` method which allows using typed headers. [#1869]
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
* Relax default timeout for `Connector` to 5 seconds(original 1 second). [#1905]
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
* `ClientRequest::set`; use `ClientRequest::insert_header`. [#1869]
|
* `ClientRequest::set`; use `ClientRequest::insert_header`. [#1869]
|
||||||
* `ClientRequest::set_header`; use `ClientRequest::insert_header`. [#1869]
|
* `ClientRequest::set_header`; use `ClientRequest::insert_header`. [#1869]
|
||||||
|
@ -12,6 +15,7 @@
|
||||||
* `ClientRequest::header`; use `ClientRequest::append_header`. [#1869]
|
* `ClientRequest::header`; use `ClientRequest::append_header`. [#1869]
|
||||||
|
|
||||||
[#1869]: https://github.com/actix/actix-web/pull/1869
|
[#1869]: https://github.com/actix/actix-web/pull/1869
|
||||||
|
[#1905]: https://github.com/actix/actix-web/pull/1905
|
||||||
|
|
||||||
|
|
||||||
## 3.0.0-beta.1 - 2021-01-07
|
## 3.0.0-beta.1 - 2021-01-07
|
||||||
|
|
|
@ -28,19 +28,19 @@ features = ["openssl", "rustls", "compress"]
|
||||||
default = ["compress"]
|
default = ["compress"]
|
||||||
|
|
||||||
# openssl
|
# openssl
|
||||||
openssl = ["open-ssl", "actix-http/openssl"]
|
openssl = ["tls-openssl", "actix-http/openssl"]
|
||||||
|
|
||||||
# rustls
|
# rustls
|
||||||
rustls = ["rust-tls", "actix-http/rustls"]
|
rustls = ["tls-rustls", "actix-http/rustls"]
|
||||||
|
|
||||||
# content-encoding support
|
# content-encoding support
|
||||||
compress = ["actix-http/compress"]
|
compress = ["actix-http/compress"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix-codec = "0.4.0-beta.1"
|
actix-codec = "0.4.0-beta.1"
|
||||||
actix-service = "2.0.0-beta.3"
|
actix-service = "2.0.0-beta.4"
|
||||||
actix-http = "3.0.0-beta.1"
|
actix-http = "3.0.0-beta.1"
|
||||||
actix-rt = "2.0.0-beta.2"
|
actix-rt = "2"
|
||||||
|
|
||||||
base64 = "0.13"
|
base64 = "0.13"
|
||||||
bytes = "1"
|
bytes = "1"
|
||||||
|
@ -54,23 +54,20 @@ rand = "0.8"
|
||||||
serde = "1.0"
|
serde = "1.0"
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
serde_urlencoded = "0.7"
|
serde_urlencoded = "0.7"
|
||||||
open-ssl = { version = "0.10", package = "openssl", optional = true }
|
tls-openssl = { version = "0.10.9", package = "openssl", optional = true }
|
||||||
rust-tls = { version = "0.19.0", package = "rustls", optional = true, features = ["dangerous_configuration"] }
|
tls-rustls = { version = "0.19.0", package = "rustls", optional = true, features = ["dangerous_configuration"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
# TODO: actix is temporary added as dev dep for actix-macro reason.
|
|
||||||
# Can be removed when it does not impact tests.
|
|
||||||
actix = "0.11.0-beta.1"
|
|
||||||
actix-web = { version = "4.0.0-beta.1", features = ["openssl"] }
|
actix-web = { version = "4.0.0-beta.1", features = ["openssl"] }
|
||||||
actix-http = { version = "3.0.0-beta.1", features = ["openssl"] }
|
actix-http = { version = "3.0.0-beta.1", features = ["openssl"] }
|
||||||
actix-http-test = { version = "3.0.0-beta.1", features = ["openssl"] }
|
actix-http-test = { version = "3.0.0-beta.1", features = ["openssl"] }
|
||||||
actix-utils = "3.0.0-beta.1"
|
actix-utils = "3.0.0-beta.1"
|
||||||
actix-server = "2.0.0-beta.2"
|
actix-server = "2.0.0-beta.3"
|
||||||
actix-tls = { version = "3.0.0-beta.2", features = ["openssl", "rustls"] }
|
actix-tls = { version = "3.0.0-beta.3", features = ["openssl", "rustls"] }
|
||||||
|
|
||||||
brotli2 = "0.3.2"
|
brotli2 = "0.3.2"
|
||||||
flate2 = "1.0.13"
|
flate2 = "1.0.13"
|
||||||
futures-util = { version = "0.3.7", default-features = false }
|
futures-util = { version = "0.3.7", default-features = false }
|
||||||
env_logger = "0.7"
|
env_logger = "0.8"
|
||||||
rcgen = "0.8"
|
rcgen = "0.8"
|
||||||
webpki = "0.21"
|
webpki = "0.21"
|
||||||
|
|
|
@ -21,7 +21,7 @@ use actix_rt::System;
|
||||||
use awc::Client;
|
use awc::Client;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
System::new("test").block_on(async {
|
System::new().block_on(async {
|
||||||
let client = Client::default();
|
let client = Client::default();
|
||||||
|
|
||||||
let res = client
|
let res = client
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
@ -24,7 +23,7 @@ pub struct ClientBuilder {
|
||||||
conn_window_size: Option<u32>,
|
conn_window_size: Option<u32>,
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
timeout: Option<Duration>,
|
timeout: Option<Duration>,
|
||||||
connector: Option<RefCell<Box<dyn Connect>>>,
|
connector: Option<Box<dyn Connect>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ClientBuilder {
|
impl Default for ClientBuilder {
|
||||||
|
@ -56,7 +55,7 @@ impl ClientBuilder {
|
||||||
<T::Response as Connection>::Future: 'static,
|
<T::Response as Connection>::Future: 'static,
|
||||||
T::Future: 'static,
|
T::Future: 'static,
|
||||||
{
|
{
|
||||||
self.connector = Some(RefCell::new(Box::new(ConnectorWrapper(connector))));
|
self.connector = Some(Box::new(ConnectorWrapper(connector)));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,9 +181,7 @@ impl ClientBuilder {
|
||||||
if let Some(val) = self.stream_window_size {
|
if let Some(val) = self.stream_window_size {
|
||||||
connector = connector.initial_window_size(val)
|
connector = connector.initial_window_size(val)
|
||||||
};
|
};
|
||||||
RefCell::new(
|
Box::new(ConnectorWrapper(connector.finish())) as _
|
||||||
Box::new(ConnectorWrapper(connector.finish())) as Box<dyn Connect>
|
|
||||||
)
|
|
||||||
};
|
};
|
||||||
let config = ClientConfig {
|
let config = ClientConfig {
|
||||||
headers: self.headers,
|
headers: self.headers,
|
||||||
|
|
|
@ -1,71 +1,39 @@
|
||||||
use std::future::Future;
|
use std::{
|
||||||
use std::pin::Pin;
|
fmt, io, net,
|
||||||
use std::rc::Rc;
|
pin::Pin,
|
||||||
use std::task::{Context, Poll};
|
task::{Context, Poll},
|
||||||
use std::{fmt, io, net};
|
};
|
||||||
|
|
||||||
use actix_codec::{AsyncRead, AsyncWrite, Framed, ReadBuf};
|
use actix_codec::{AsyncRead, AsyncWrite, Framed, ReadBuf};
|
||||||
use actix_http::body::Body;
|
use actix_http::{
|
||||||
use actix_http::client::{
|
body::Body,
|
||||||
Connect as ClientConnect, ConnectError, Connection, SendRequestError,
|
client::{Connect as ClientConnect, ConnectError, Connection, SendRequestError},
|
||||||
|
h1::ClientCodec,
|
||||||
|
RequestHead, RequestHeadType, ResponseHead,
|
||||||
};
|
};
|
||||||
use actix_http::h1::ClientCodec;
|
|
||||||
use actix_http::http::HeaderMap;
|
|
||||||
use actix_http::{RequestHead, RequestHeadType, ResponseHead};
|
|
||||||
use actix_service::Service;
|
use actix_service::Service;
|
||||||
|
use futures_core::future::LocalBoxFuture;
|
||||||
|
|
||||||
use crate::response::ClientResponse;
|
use crate::response::ClientResponse;
|
||||||
|
|
||||||
pub(crate) struct ConnectorWrapper<T>(pub T);
|
pub(crate) struct ConnectorWrapper<T>(pub T);
|
||||||
|
|
||||||
|
type TunnelResponse = (ResponseHead, Framed<BoxedSocket, ClientCodec>);
|
||||||
|
|
||||||
pub(crate) trait Connect {
|
pub(crate) trait Connect {
|
||||||
fn send_request(
|
fn send_request(
|
||||||
&mut self,
|
&self,
|
||||||
head: RequestHead,
|
head: RequestHeadType,
|
||||||
body: Body,
|
body: Body,
|
||||||
addr: Option<net::SocketAddr>,
|
addr: Option<net::SocketAddr>,
|
||||||
) -> Pin<Box<dyn Future<Output = Result<ClientResponse, SendRequestError>>>>;
|
) -> LocalBoxFuture<'static, Result<ClientResponse, SendRequestError>>;
|
||||||
|
|
||||||
fn send_request_extra(
|
|
||||||
&mut self,
|
|
||||||
head: Rc<RequestHead>,
|
|
||||||
extra_headers: Option<HeaderMap>,
|
|
||||||
body: Body,
|
|
||||||
addr: Option<net::SocketAddr>,
|
|
||||||
) -> Pin<Box<dyn Future<Output = Result<ClientResponse, SendRequestError>>>>;
|
|
||||||
|
|
||||||
/// Send request, returns Response and Framed
|
/// Send request, returns Response and Framed
|
||||||
fn open_tunnel(
|
fn open_tunnel(
|
||||||
&mut self,
|
&self,
|
||||||
head: RequestHead,
|
head: RequestHead,
|
||||||
addr: Option<net::SocketAddr>,
|
addr: Option<net::SocketAddr>,
|
||||||
) -> Pin<
|
) -> LocalBoxFuture<'static, Result<TunnelResponse, SendRequestError>>;
|
||||||
Box<
|
|
||||||
dyn Future<
|
|
||||||
Output = Result<
|
|
||||||
(ResponseHead, Framed<BoxedSocket, ClientCodec>),
|
|
||||||
SendRequestError,
|
|
||||||
>,
|
|
||||||
>,
|
|
||||||
>,
|
|
||||||
>;
|
|
||||||
|
|
||||||
/// Send request and extra headers, returns Response and Framed
|
|
||||||
fn open_tunnel_extra(
|
|
||||||
&mut self,
|
|
||||||
head: Rc<RequestHead>,
|
|
||||||
extra_headers: Option<HeaderMap>,
|
|
||||||
addr: Option<net::SocketAddr>,
|
|
||||||
) -> Pin<
|
|
||||||
Box<
|
|
||||||
dyn Future<
|
|
||||||
Output = Result<
|
|
||||||
(ResponseHead, Framed<BoxedSocket, ClientCodec>),
|
|
||||||
SendRequestError,
|
|
||||||
>,
|
|
||||||
>,
|
|
||||||
>,
|
|
||||||
>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Connect for ConnectorWrapper<T>
|
impl<T> Connect for ConnectorWrapper<T>
|
||||||
|
@ -78,14 +46,14 @@ where
|
||||||
T::Future: 'static,
|
T::Future: 'static,
|
||||||
{
|
{
|
||||||
fn send_request(
|
fn send_request(
|
||||||
&mut self,
|
&self,
|
||||||
head: RequestHead,
|
head: RequestHeadType,
|
||||||
body: Body,
|
body: Body,
|
||||||
addr: Option<net::SocketAddr>,
|
addr: Option<net::SocketAddr>,
|
||||||
) -> Pin<Box<dyn Future<Output = Result<ClientResponse, SendRequestError>>>> {
|
) -> LocalBoxFuture<'static, Result<ClientResponse, SendRequestError>> {
|
||||||
// connect to the host
|
// connect to the host
|
||||||
let fut = self.0.call(ClientConnect {
|
let fut = self.0.call(ClientConnect {
|
||||||
uri: head.uri.clone(),
|
uri: head.as_ref().uri.clone(),
|
||||||
addr,
|
addr,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -93,52 +61,17 @@ where
|
||||||
let connection = fut.await?;
|
let connection = fut.await?;
|
||||||
|
|
||||||
// send request
|
// send request
|
||||||
connection
|
let (head, payload) = connection.send_request(head, body).await?;
|
||||||
.send_request(RequestHeadType::from(head), body)
|
|
||||||
.await
|
|
||||||
.map(|(head, payload)| ClientResponse::new(head, payload))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn send_request_extra(
|
|
||||||
&mut self,
|
|
||||||
head: Rc<RequestHead>,
|
|
||||||
extra_headers: Option<HeaderMap>,
|
|
||||||
body: Body,
|
|
||||||
addr: Option<net::SocketAddr>,
|
|
||||||
) -> Pin<Box<dyn Future<Output = Result<ClientResponse, SendRequestError>>>> {
|
|
||||||
// connect to the host
|
|
||||||
let fut = self.0.call(ClientConnect {
|
|
||||||
uri: head.uri.clone(),
|
|
||||||
addr,
|
|
||||||
});
|
|
||||||
|
|
||||||
Box::pin(async move {
|
|
||||||
let connection = fut.await?;
|
|
||||||
|
|
||||||
// send request
|
|
||||||
let (head, payload) = connection
|
|
||||||
.send_request(RequestHeadType::Rc(head, extra_headers), body)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(ClientResponse::new(head, payload))
|
Ok(ClientResponse::new(head, payload))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn open_tunnel(
|
fn open_tunnel(
|
||||||
&mut self,
|
&self,
|
||||||
head: RequestHead,
|
head: RequestHead,
|
||||||
addr: Option<net::SocketAddr>,
|
addr: Option<net::SocketAddr>,
|
||||||
) -> Pin<
|
) -> LocalBoxFuture<'static, Result<TunnelResponse, SendRequestError>> {
|
||||||
Box<
|
|
||||||
dyn Future<
|
|
||||||
Output = Result<
|
|
||||||
(ResponseHead, Framed<BoxedSocket, ClientCodec>),
|
|
||||||
SendRequestError,
|
|
||||||
>,
|
|
||||||
>,
|
|
||||||
>,
|
|
||||||
> {
|
|
||||||
// connect to the host
|
// connect to the host
|
||||||
let fut = self.0.call(ClientConnect {
|
let fut = self.0.call(ClientConnect {
|
||||||
uri: head.uri.clone(),
|
uri: head.uri.clone(),
|
||||||
|
@ -156,40 +89,6 @@ where
|
||||||
Ok((head, framed))
|
Ok((head, framed))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn open_tunnel_extra(
|
|
||||||
&mut self,
|
|
||||||
head: Rc<RequestHead>,
|
|
||||||
extra_headers: Option<HeaderMap>,
|
|
||||||
addr: Option<net::SocketAddr>,
|
|
||||||
) -> Pin<
|
|
||||||
Box<
|
|
||||||
dyn Future<
|
|
||||||
Output = Result<
|
|
||||||
(ResponseHead, Framed<BoxedSocket, ClientCodec>),
|
|
||||||
SendRequestError,
|
|
||||||
>,
|
|
||||||
>,
|
|
||||||
>,
|
|
||||||
> {
|
|
||||||
// connect to the host
|
|
||||||
let fut = self.0.call(ClientConnect {
|
|
||||||
uri: head.uri.clone(),
|
|
||||||
addr,
|
|
||||||
});
|
|
||||||
|
|
||||||
Box::pin(async move {
|
|
||||||
let connection = fut.await?;
|
|
||||||
|
|
||||||
// send request
|
|
||||||
let (head, framed) = connection
|
|
||||||
.open_tunnel(RequestHeadType::Rc(head, extra_headers))
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let framed = framed.into_map_io(|io| BoxedSocket(Box::new(Socket(io))));
|
|
||||||
Ok((head, framed))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
trait AsyncSocket {
|
trait AsyncSocket {
|
||||||
|
|
|
@ -93,7 +93,6 @@
|
||||||
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
||||||
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
||||||
|
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
@ -145,7 +144,7 @@ use self::connect::{Connect, ConnectorWrapper};
|
||||||
pub struct Client(Rc<ClientConfig>);
|
pub struct Client(Rc<ClientConfig>);
|
||||||
|
|
||||||
pub(crate) struct ClientConfig {
|
pub(crate) struct ClientConfig {
|
||||||
pub(crate) connector: RefCell<Box<dyn Connect>>,
|
pub(crate) connector: Box<dyn Connect>,
|
||||||
pub(crate) headers: HeaderMap,
|
pub(crate) headers: HeaderMap,
|
||||||
pub(crate) timeout: Option<Duration>,
|
pub(crate) timeout: Option<Duration>,
|
||||||
}
|
}
|
||||||
|
@ -153,9 +152,7 @@ pub(crate) struct ClientConfig {
|
||||||
impl Default for Client {
|
impl Default for Client {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Client(Rc::new(ClientConfig {
|
Client(Rc::new(ClientConfig {
|
||||||
connector: RefCell::new(Box::new(ConnectorWrapper(
|
connector: Box::new(ConnectorWrapper(Connector::new().finish())),
|
||||||
Connector::new().finish(),
|
|
||||||
))),
|
|
||||||
headers: HeaderMap::new(),
|
headers: HeaderMap::new(),
|
||||||
timeout: Some(Duration::from_secs(5)),
|
timeout: Some(Duration::from_secs(5)),
|
||||||
}))
|
}))
|
||||||
|
|
|
@ -1,21 +1,26 @@
|
||||||
use std::future::Future;
|
use std::{
|
||||||
use std::net;
|
future::Future,
|
||||||
use std::pin::Pin;
|
net,
|
||||||
use std::rc::Rc;
|
pin::Pin,
|
||||||
use std::task::{Context, Poll};
|
rc::Rc,
|
||||||
use std::time::Duration;
|
task::{Context, Poll},
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
|
use actix_http::{
|
||||||
|
body::{Body, BodyStream},
|
||||||
|
http::{
|
||||||
|
header::{self, HeaderMap, HeaderName, IntoHeaderValue},
|
||||||
|
Error as HttpError,
|
||||||
|
},
|
||||||
|
Error, RequestHead, RequestHeadType,
|
||||||
|
};
|
||||||
use actix_rt::time::{sleep, Sleep};
|
use actix_rt::time::{sleep, Sleep};
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use derive_more::From;
|
use derive_more::From;
|
||||||
use futures_core::Stream;
|
use futures_core::Stream;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
use actix_http::body::{Body, BodyStream};
|
|
||||||
use actix_http::http::header::{self, IntoHeaderValue};
|
|
||||||
use actix_http::http::{Error as HttpError, HeaderMap, HeaderName};
|
|
||||||
use actix_http::{Error, RequestHead};
|
|
||||||
|
|
||||||
#[cfg(feature = "compress")]
|
#[cfg(feature = "compress")]
|
||||||
use actix_http::encoding::Decoder;
|
use actix_http::encoding::Decoder;
|
||||||
#[cfg(feature = "compress")]
|
#[cfg(feature = "compress")]
|
||||||
|
@ -183,15 +188,17 @@ impl RequestSender {
|
||||||
where
|
where
|
||||||
B: Into<Body>,
|
B: Into<Body>,
|
||||||
{
|
{
|
||||||
let mut connector = config.connector.borrow_mut();
|
|
||||||
|
|
||||||
let fut = match self {
|
let fut = match self {
|
||||||
RequestSender::Owned(head) => {
|
RequestSender::Owned(head) => config.connector.send_request(
|
||||||
connector.send_request(head, body.into(), addr)
|
RequestHeadType::Owned(head),
|
||||||
}
|
body.into(),
|
||||||
RequestSender::Rc(head, extra_headers) => {
|
addr,
|
||||||
connector.send_request_extra(head, extra_headers, body.into(), addr)
|
),
|
||||||
}
|
RequestSender::Rc(head, extra_headers) => config.connector.send_request(
|
||||||
|
RequestHeadType::Rc(head, extra_headers),
|
||||||
|
body.into(),
|
||||||
|
addr,
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
SendClientRequest::new(fut, response_decompress, timeout.or(config.timeout))
|
SendClientRequest::new(fut, response_decompress, timeout.or(config.timeout))
|
||||||
|
|
|
@ -325,11 +325,7 @@ impl WebsocketsRequest {
|
||||||
let max_size = self.max_size;
|
let max_size = self.max_size;
|
||||||
let server_mode = self.server_mode;
|
let server_mode = self.server_mode;
|
||||||
|
|
||||||
let fut = self
|
let fut = self.config.connector.open_tunnel(head, self.addr);
|
||||||
.config
|
|
||||||
.connector
|
|
||||||
.borrow_mut()
|
|
||||||
.open_tunnel(head, self.addr);
|
|
||||||
|
|
||||||
// set request timeout
|
// set request timeout
|
||||||
let (head, framed) = if let Some(to) = self.config.timeout {
|
let (head, framed) = if let Some(to) = self.config.timeout {
|
||||||
|
|
|
@ -120,9 +120,7 @@ async fn test_timeout() {
|
||||||
});
|
});
|
||||||
|
|
||||||
let connector = awc::Connector::new()
|
let connector = awc::Connector::new()
|
||||||
.connector(actix_tls::connect::new_connector(
|
.connector(actix_tls::connect::default_connector())
|
||||||
actix_tls::connect::start_default_resolver().await.unwrap(),
|
|
||||||
))
|
|
||||||
.timeout(Duration::from_secs(15))
|
.timeout(Duration::from_secs(15))
|
||||||
.finish();
|
.finish();
|
||||||
|
|
||||||
|
@ -722,9 +720,9 @@ async fn test_client_cookie_handling() {
|
||||||
async fn client_unread_response() {
|
async fn client_unread_response() {
|
||||||
let addr = test::unused_addr();
|
let addr = test::unused_addr();
|
||||||
|
|
||||||
std::thread::spawn(move || {
|
|
||||||
let lst = std::net::TcpListener::bind(addr).unwrap();
|
let lst = std::net::TcpListener::bind(addr).unwrap();
|
||||||
|
|
||||||
|
std::thread::spawn(move || {
|
||||||
for stream in lst.incoming() {
|
for stream in lst.incoming() {
|
||||||
let mut stream = stream.unwrap();
|
let mut stream = stream.unwrap();
|
||||||
let mut b = [0; 1000];
|
let mut b = [0; 1000];
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
#![cfg(feature = "openssl")]
|
#![cfg(feature = "openssl")]
|
||||||
|
|
||||||
|
extern crate tls_openssl as openssl;
|
||||||
|
|
||||||
use actix_http::HttpService;
|
use actix_http::HttpService;
|
||||||
use actix_http_test::test_server;
|
use actix_http_test::test_server;
|
||||||
use actix_service::{map_config, ServiceFactoryExt};
|
use actix_service::{map_config, ServiceFactoryExt};
|
||||||
use actix_web::http::Version;
|
use actix_web::http::Version;
|
||||||
use actix_web::{dev::AppConfig, web, App, HttpResponse};
|
use actix_web::{dev::AppConfig, web, App, HttpResponse};
|
||||||
use open_ssl::ssl::{SslAcceptor, SslConnector, SslFiletype, SslMethod, SslVerifyMode};
|
use openssl::ssl::{SslAcceptor, SslConnector, SslFiletype, SslMethod, SslVerifyMode};
|
||||||
|
|
||||||
fn ssl_acceptor() -> SslAcceptor {
|
fn ssl_acceptor() -> SslAcceptor {
|
||||||
// load ssl keys
|
// load ssl keys
|
||||||
|
@ -20,7 +23,7 @@ fn ssl_acceptor() -> SslAcceptor {
|
||||||
if protos.windows(3).any(|window| window == H2) {
|
if protos.windows(3).any(|window| window == H2) {
|
||||||
Ok(b"h2")
|
Ok(b"h2")
|
||||||
} else {
|
} else {
|
||||||
Err(open_ssl::ssl::AlpnError::NOACK)
|
Err(openssl::ssl::AlpnError::NOACK)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
builder.set_alpn_protos(b"\x02h2").unwrap();
|
builder.set_alpn_protos(b"\x02h2").unwrap();
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#![cfg(feature = "rustls")]
|
#![cfg(feature = "rustls")]
|
||||||
|
|
||||||
extern crate rust_tls as rustls;
|
extern crate tls_rustls as rustls;
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
io::BufReader,
|
io::BufReader,
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
#![cfg(feature = "openssl")]
|
#![cfg(feature = "openssl")]
|
||||||
|
|
||||||
|
extern crate tls_openssl as openssl;
|
||||||
|
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
@ -8,7 +11,7 @@ use actix_service::{map_config, pipeline_factory, ServiceFactoryExt};
|
||||||
use actix_web::http::Version;
|
use actix_web::http::Version;
|
||||||
use actix_web::{dev::AppConfig, web, App, HttpResponse};
|
use actix_web::{dev::AppConfig, web, App, HttpResponse};
|
||||||
use futures_util::future::ok;
|
use futures_util::future::ok;
|
||||||
use open_ssl::ssl::{SslAcceptor, SslConnector, SslFiletype, SslMethod, SslVerifyMode};
|
use openssl::ssl::{SslAcceptor, SslConnector, SslFiletype, SslMethod, SslVerifyMode};
|
||||||
|
|
||||||
fn ssl_acceptor() -> SslAcceptor {
|
fn ssl_acceptor() -> SslAcceptor {
|
||||||
// load ssl keys
|
// load ssl keys
|
||||||
|
@ -24,7 +27,7 @@ fn ssl_acceptor() -> SslAcceptor {
|
||||||
if protos.windows(3).any(|window| window == H2) {
|
if protos.windows(3).any(|window| window == H2) {
|
||||||
Ok(b"h2")
|
Ok(b"h2")
|
||||||
} else {
|
} else {
|
||||||
Err(open_ssl::ssl::AlpnError::NOACK)
|
Err(openssl::ssl::AlpnError::NOACK)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
builder.set_alpn_protos(b"\x02h2").unwrap();
|
builder.set_alpn_protos(b"\x02h2").unwrap();
|
||||||
|
|
|
@ -68,7 +68,7 @@ impl<T: Responder> Responder for OptionResponder<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn future_responder(c: &mut Criterion) {
|
fn future_responder(c: &mut Criterion) {
|
||||||
let rt = actix_rt::System::new("test");
|
let rt = actix_rt::System::new();
|
||||||
let req = TestRequest::default().to_http_request();
|
let req = TestRequest::default().to_http_request();
|
||||||
|
|
||||||
c.bench_function("future_responder", move |b| {
|
c.bench_function("future_responder", move |b| {
|
||||||
|
@ -91,7 +91,7 @@ fn future_responder(c: &mut Criterion) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn responder(c: &mut Criterion) {
|
fn responder(c: &mut Criterion) {
|
||||||
let rt = actix_rt::System::new("test");
|
let rt = actix_rt::System::new();
|
||||||
let req = TestRequest::default().to_http_request();
|
let req = TestRequest::default().to_http_request();
|
||||||
c.bench_function("responder", move |b| {
|
c.bench_function("responder", move |b| {
|
||||||
b.iter_custom(|_| {
|
b.iter_custom(|_| {
|
||||||
|
|
|
@ -29,7 +29,7 @@ const STR: &str = "Hello World Hello World Hello World Hello World Hello World \
|
||||||
fn bench_async_burst(c: &mut Criterion) {
|
fn bench_async_burst(c: &mut Criterion) {
|
||||||
// We are using System here, since Runtime requires preinitialized tokio
|
// We are using System here, since Runtime requires preinitialized tokio
|
||||||
// Maybe add to actix_rt docs
|
// Maybe add to actix_rt docs
|
||||||
let rt = actix_rt::System::new("test");
|
let rt = actix_rt::System::new();
|
||||||
|
|
||||||
let srv = rt.block_on(async {
|
let srv = rt.block_on(async {
|
||||||
test::start(|| {
|
test::start(|| {
|
||||||
|
|
|
@ -25,7 +25,7 @@ pub fn bench_async_service<S>(c: &mut Criterion, srv: S, name: &str)
|
||||||
where
|
where
|
||||||
S: Service<ServiceRequest, Response = ServiceResponse, Error = Error> + 'static,
|
S: Service<ServiceRequest, Response = ServiceResponse, Error = Error> + 'static,
|
||||||
{
|
{
|
||||||
let rt = actix_rt::System::new("test");
|
let rt = actix_rt::System::new();
|
||||||
let srv = Rc::new(RefCell::new(srv));
|
let srv = Rc::new(RefCell::new(srv));
|
||||||
|
|
||||||
let req = TestRequest::default().to_srv_request();
|
let req = TestRequest::default().to_srv_request();
|
||||||
|
@ -67,7 +67,7 @@ async fn index(req: ServiceRequest) -> Result<ServiceResponse, Error> {
|
||||||
// Sample results on MacBook Pro '14
|
// Sample results on MacBook Pro '14
|
||||||
// time: [2.0724 us 2.1345 us 2.2074 us]
|
// time: [2.0724 us 2.1345 us 2.2074 us]
|
||||||
fn async_web_service(c: &mut Criterion) {
|
fn async_web_service(c: &mut Criterion) {
|
||||||
let rt = actix_rt::System::new("test");
|
let rt = actix_rt::System::new();
|
||||||
let srv = Rc::new(RefCell::new(rt.block_on(init_service(
|
let srv = Rc::new(RefCell::new(rt.block_on(init_service(
|
||||||
App::new().service(web::service("/").finish(index)),
|
App::new().service(web::service("/").finish(index)),
|
||||||
))));
|
))));
|
||||||
|
|
32
src/app.rs
32
src/app.rs
|
@ -418,7 +418,7 @@ where
|
||||||
>
|
>
|
||||||
where
|
where
|
||||||
B1: MessageBody,
|
B1: MessageBody,
|
||||||
F: FnMut(ServiceRequest, &mut T::Service) -> R + Clone,
|
F: Fn(ServiceRequest, &T::Service) -> R + Clone,
|
||||||
R: Future<Output = Result<ServiceResponse<B1>, Error>>,
|
R: Future<Output = Result<ServiceResponse<B1>, Error>>,
|
||||||
{
|
{
|
||||||
App {
|
App {
|
||||||
|
@ -474,7 +474,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 srv = init_service(
|
||||||
App::new().service(web::resource("/test").to(HttpResponse::Ok)),
|
App::new().service(web::resource("/test").to(HttpResponse::Ok)),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
@ -486,7 +486,7 @@ mod tests {
|
||||||
let resp = srv.call(req).await.unwrap();
|
let resp = srv.call(req).await.unwrap();
|
||||||
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
|
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
|
||||||
|
|
||||||
let mut srv = init_service(
|
let srv = init_service(
|
||||||
App::new()
|
App::new()
|
||||||
.service(web::resource("/test").to(HttpResponse::Ok))
|
.service(web::resource("/test").to(HttpResponse::Ok))
|
||||||
.service(
|
.service(
|
||||||
|
@ -519,7 +519,7 @@ mod tests {
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_data_factory() {
|
async fn test_data_factory() {
|
||||||
let mut srv =
|
let srv =
|
||||||
init_service(App::new().data_factory(|| ok::<_, ()>(10usize)).service(
|
init_service(App::new().data_factory(|| ok::<_, ()>(10usize)).service(
|
||||||
web::resource("/").to(|_: web::Data<usize>| HttpResponse::Ok()),
|
web::resource("/").to(|_: web::Data<usize>| HttpResponse::Ok()),
|
||||||
))
|
))
|
||||||
|
@ -528,7 +528,7 @@ mod tests {
|
||||||
let resp = srv.call(req).await.unwrap();
|
let resp = srv.call(req).await.unwrap();
|
||||||
assert_eq!(resp.status(), StatusCode::OK);
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
|
|
||||||
let mut srv =
|
let srv =
|
||||||
init_service(App::new().data_factory(|| ok::<_, ()>(10u32)).service(
|
init_service(App::new().data_factory(|| ok::<_, ()>(10u32)).service(
|
||||||
web::resource("/").to(|_: web::Data<usize>| HttpResponse::Ok()),
|
web::resource("/").to(|_: web::Data<usize>| HttpResponse::Ok()),
|
||||||
))
|
))
|
||||||
|
@ -559,7 +559,7 @@ mod tests {
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_extension() {
|
async fn test_extension() {
|
||||||
let mut srv = init_service(App::new().app_data(10usize).service(
|
let srv = init_service(App::new().app_data(10usize).service(
|
||||||
web::resource("/").to(|req: HttpRequest| {
|
web::resource("/").to(|req: HttpRequest| {
|
||||||
assert_eq!(*req.app_data::<usize>().unwrap(), 10);
|
assert_eq!(*req.app_data::<usize>().unwrap(), 10);
|
||||||
HttpResponse::Ok()
|
HttpResponse::Ok()
|
||||||
|
@ -573,7 +573,7 @@ mod tests {
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_wrap() {
|
async fn test_wrap() {
|
||||||
let mut srv = init_service(
|
let srv = init_service(
|
||||||
App::new()
|
App::new()
|
||||||
.wrap(
|
.wrap(
|
||||||
DefaultHeaders::new()
|
DefaultHeaders::new()
|
||||||
|
@ -583,7 +583,7 @@ mod tests {
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
let req = TestRequest::with_uri("/test").to_request();
|
let req = TestRequest::with_uri("/test").to_request();
|
||||||
let resp = call_service(&mut srv, req).await;
|
let resp = call_service(&srv, req).await;
|
||||||
assert_eq!(resp.status(), StatusCode::OK);
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
resp.headers().get(header::CONTENT_TYPE).unwrap(),
|
resp.headers().get(header::CONTENT_TYPE).unwrap(),
|
||||||
|
@ -593,7 +593,7 @@ mod tests {
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_router_wrap() {
|
async fn test_router_wrap() {
|
||||||
let mut srv = init_service(
|
let srv = init_service(
|
||||||
App::new()
|
App::new()
|
||||||
.route("/test", web::get().to(HttpResponse::Ok))
|
.route("/test", web::get().to(HttpResponse::Ok))
|
||||||
.wrap(
|
.wrap(
|
||||||
|
@ -603,7 +603,7 @@ mod tests {
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
let req = TestRequest::with_uri("/test").to_request();
|
let req = TestRequest::with_uri("/test").to_request();
|
||||||
let resp = call_service(&mut srv, req).await;
|
let resp = call_service(&srv, req).await;
|
||||||
assert_eq!(resp.status(), StatusCode::OK);
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
resp.headers().get(header::CONTENT_TYPE).unwrap(),
|
resp.headers().get(header::CONTENT_TYPE).unwrap(),
|
||||||
|
@ -613,7 +613,7 @@ mod tests {
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_wrap_fn() {
|
async fn test_wrap_fn() {
|
||||||
let mut srv = init_service(
|
let srv = init_service(
|
||||||
App::new()
|
App::new()
|
||||||
.wrap_fn(|req, srv| {
|
.wrap_fn(|req, srv| {
|
||||||
let fut = srv.call(req);
|
let fut = srv.call(req);
|
||||||
|
@ -630,7 +630,7 @@ mod tests {
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
let req = TestRequest::with_uri("/test").to_request();
|
let req = TestRequest::with_uri("/test").to_request();
|
||||||
let resp = call_service(&mut srv, req).await;
|
let resp = call_service(&srv, req).await;
|
||||||
assert_eq!(resp.status(), StatusCode::OK);
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
resp.headers().get(header::CONTENT_TYPE).unwrap(),
|
resp.headers().get(header::CONTENT_TYPE).unwrap(),
|
||||||
|
@ -640,7 +640,7 @@ mod tests {
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_router_wrap_fn() {
|
async fn test_router_wrap_fn() {
|
||||||
let mut srv = init_service(
|
let 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| {
|
||||||
|
@ -657,7 +657,7 @@ mod tests {
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
let req = TestRequest::with_uri("/test").to_request();
|
let req = TestRequest::with_uri("/test").to_request();
|
||||||
let resp = call_service(&mut srv, req).await;
|
let resp = call_service(&srv, req).await;
|
||||||
assert_eq!(resp.status(), StatusCode::OK);
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
resp.headers().get(header::CONTENT_TYPE).unwrap(),
|
resp.headers().get(header::CONTENT_TYPE).unwrap(),
|
||||||
|
@ -667,7 +667,7 @@ mod tests {
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_external_resource() {
|
async fn test_external_resource() {
|
||||||
let mut srv = init_service(
|
let srv = init_service(
|
||||||
App::new()
|
App::new()
|
||||||
.external_resource("youtube", "https://youtube.com/watch/{video_id}")
|
.external_resource("youtube", "https://youtube.com/watch/{video_id}")
|
||||||
.route(
|
.route(
|
||||||
|
@ -681,7 +681,7 @@ mod tests {
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
let req = TestRequest::with_uri("/test").to_request();
|
let req = TestRequest::with_uri("/test").to_request();
|
||||||
let resp = call_service(&mut srv, req).await;
|
let resp = call_service(&srv, req).await;
|
||||||
assert_eq!(resp.status(), StatusCode::OK);
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
let body = read_body(resp).await;
|
let body = read_body(resp).await;
|
||||||
assert_eq!(body, Bytes::from_static(b"https://youtube.com/watch/12345"));
|
assert_eq!(body, Bytes::from_static(b"https://youtube.com/watch/12345"));
|
||||||
|
|
|
@ -220,7 +220,7 @@ where
|
||||||
|
|
||||||
actix_service::forward_ready!(service);
|
actix_service::forward_ready!(service);
|
||||||
|
|
||||||
fn call(&mut self, req: Request) -> Self::Future {
|
fn call(&self, req: Request) -> Self::Future {
|
||||||
let (head, payload) = req.into_parts();
|
let (head, payload) = req.into_parts();
|
||||||
|
|
||||||
let req = if let Some(mut req) = self.app_state.pool().pop() {
|
let req = if let Some(mut req) = self.app_state.pool().pop() {
|
||||||
|
@ -311,8 +311,8 @@ impl Service<ServiceRequest> for AppRouting {
|
||||||
|
|
||||||
actix_service::always_ready!();
|
actix_service::always_ready!();
|
||||||
|
|
||||||
fn call(&mut self, mut req: ServiceRequest) -> Self::Future {
|
fn call(&self, mut req: ServiceRequest) -> Self::Future {
|
||||||
let res = self.router.recognize_mut_checked(&mut req, |req, guards| {
|
let res = self.router.recognize_checked(&mut req, |req, guards| {
|
||||||
if let Some(ref guards) = guards {
|
if let Some(ref guards) = guards {
|
||||||
for f in guards {
|
for f in guards {
|
||||||
if !f.check(req.head()) {
|
if !f.check(req.head()) {
|
||||||
|
@ -378,7 +378,7 @@ mod tests {
|
||||||
let data = Arc::new(AtomicBool::new(false));
|
let data = Arc::new(AtomicBool::new(false));
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut app = init_service(
|
let 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)),
|
||||||
|
|
|
@ -263,7 +263,7 @@ mod tests {
|
||||||
cfg.app_data(15u8);
|
cfg.app_data(15u8);
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut srv = init_service(App::new().configure(cfg).service(
|
let srv = init_service(App::new().configure(cfg).service(
|
||||||
web::resource("/").to(|_: web::Data<usize>, req: HttpRequest| {
|
web::resource("/").to(|_: web::Data<usize>, req: HttpRequest| {
|
||||||
assert_eq!(*req.app_data::<u8>().unwrap(), 15u8);
|
assert_eq!(*req.app_data::<u8>().unwrap(), 15u8);
|
||||||
HttpResponse::Ok()
|
HttpResponse::Ok()
|
||||||
|
@ -286,7 +286,7 @@ mod tests {
|
||||||
// });
|
// });
|
||||||
// };
|
// };
|
||||||
|
|
||||||
// let mut srv =
|
// let srv =
|
||||||
// init_service(App::new().configure(cfg).service(
|
// init_service(App::new().configure(cfg).service(
|
||||||
// web::resource("/").to(|_: web::Data<usize>| HttpResponse::Ok()),
|
// web::resource("/").to(|_: web::Data<usize>| HttpResponse::Ok()),
|
||||||
// ));
|
// ));
|
||||||
|
@ -297,7 +297,7 @@ mod tests {
|
||||||
// let cfg2 = |cfg: &mut ServiceConfig| {
|
// let cfg2 = |cfg: &mut ServiceConfig| {
|
||||||
// cfg.data_factory(|| Ok::<_, ()>(10u32));
|
// cfg.data_factory(|| Ok::<_, ()>(10u32));
|
||||||
// };
|
// };
|
||||||
// let mut srv = init_service(
|
// let srv = init_service(
|
||||||
// App::new()
|
// App::new()
|
||||||
// .service(web::resource("/").to(|_: web::Data<usize>| HttpResponse::Ok()))
|
// .service(web::resource("/").to(|_: web::Data<usize>| HttpResponse::Ok()))
|
||||||
// .configure(cfg2),
|
// .configure(cfg2),
|
||||||
|
@ -309,7 +309,7 @@ mod tests {
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_external_resource() {
|
async fn test_external_resource() {
|
||||||
let mut srv = init_service(
|
let srv = init_service(
|
||||||
App::new()
|
App::new()
|
||||||
.configure(|cfg| {
|
.configure(|cfg| {
|
||||||
cfg.external_resource(
|
cfg.external_resource(
|
||||||
|
@ -328,7 +328,7 @@ mod tests {
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
let req = TestRequest::with_uri("/test").to_request();
|
let req = TestRequest::with_uri("/test").to_request();
|
||||||
let resp = call_service(&mut srv, req).await;
|
let resp = call_service(&srv, req).await;
|
||||||
assert_eq!(resp.status(), StatusCode::OK);
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
let body = read_body(resp).await;
|
let body = read_body(resp).await;
|
||||||
assert_eq!(body, Bytes::from_static(b"https://youtube.com/watch/12345"));
|
assert_eq!(body, Bytes::from_static(b"https://youtube.com/watch/12345"));
|
||||||
|
@ -336,7 +336,7 @@ mod tests {
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_service() {
|
async fn test_service() {
|
||||||
let mut srv = init_service(App::new().configure(|cfg| {
|
let 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)),
|
||||||
)
|
)
|
||||||
|
@ -347,13 +347,13 @@ mod tests {
|
||||||
let req = TestRequest::with_uri("/test")
|
let req = TestRequest::with_uri("/test")
|
||||||
.method(Method::GET)
|
.method(Method::GET)
|
||||||
.to_request();
|
.to_request();
|
||||||
let resp = call_service(&mut srv, req).await;
|
let resp = call_service(&srv, req).await;
|
||||||
assert_eq!(resp.status(), StatusCode::CREATED);
|
assert_eq!(resp.status(), StatusCode::CREATED);
|
||||||
|
|
||||||
let req = TestRequest::with_uri("/index.html")
|
let req = TestRequest::with_uri("/index.html")
|
||||||
.method(Method::GET)
|
.method(Method::GET)
|
||||||
.to_request();
|
.to_request();
|
||||||
let resp = call_service(&mut srv, req).await;
|
let resp = call_service(&srv, req).await;
|
||||||
assert_eq!(resp.status(), StatusCode::OK);
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
16
src/data.rs
16
src/data.rs
|
@ -144,7 +144,7 @@ mod tests {
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_data_extractor() {
|
async fn test_data_extractor() {
|
||||||
let mut srv = init_service(App::new().data("TEST".to_string()).service(
|
let srv = init_service(App::new().data("TEST".to_string()).service(
|
||||||
web::resource("/").to(|data: web::Data<String>| {
|
web::resource("/").to(|data: web::Data<String>| {
|
||||||
assert_eq!(data.to_lowercase(), "test");
|
assert_eq!(data.to_lowercase(), "test");
|
||||||
HttpResponse::Ok()
|
HttpResponse::Ok()
|
||||||
|
@ -156,7 +156,7 @@ mod tests {
|
||||||
let resp = srv.call(req).await.unwrap();
|
let resp = srv.call(req).await.unwrap();
|
||||||
assert_eq!(resp.status(), StatusCode::OK);
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
|
|
||||||
let mut srv =
|
let srv =
|
||||||
init_service(App::new().data(10u32).service(
|
init_service(App::new().data(10u32).service(
|
||||||
web::resource("/").to(|_: web::Data<usize>| HttpResponse::Ok()),
|
web::resource("/").to(|_: web::Data<usize>| HttpResponse::Ok()),
|
||||||
))
|
))
|
||||||
|
@ -165,7 +165,7 @@ mod tests {
|
||||||
let resp = srv.call(req).await.unwrap();
|
let resp = srv.call(req).await.unwrap();
|
||||||
assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
|
assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
|
||||||
|
|
||||||
let mut srv = init_service(
|
let srv = init_service(
|
||||||
App::new()
|
App::new()
|
||||||
.data(10u32)
|
.data(10u32)
|
||||||
.data(13u32)
|
.data(13u32)
|
||||||
|
@ -186,7 +186,7 @@ mod tests {
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_app_data_extractor() {
|
async fn test_app_data_extractor() {
|
||||||
let mut srv =
|
let srv =
|
||||||
init_service(App::new().app_data(Data::new(10usize)).service(
|
init_service(App::new().app_data(Data::new(10usize)).service(
|
||||||
web::resource("/").to(|_: web::Data<usize>| HttpResponse::Ok()),
|
web::resource("/").to(|_: web::Data<usize>| HttpResponse::Ok()),
|
||||||
))
|
))
|
||||||
|
@ -196,7 +196,7 @@ mod tests {
|
||||||
let resp = srv.call(req).await.unwrap();
|
let resp = srv.call(req).await.unwrap();
|
||||||
assert_eq!(resp.status(), StatusCode::OK);
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
|
|
||||||
let mut srv =
|
let srv =
|
||||||
init_service(App::new().app_data(Data::new(10u32)).service(
|
init_service(App::new().app_data(Data::new(10u32)).service(
|
||||||
web::resource("/").to(|_: web::Data<usize>| HttpResponse::Ok()),
|
web::resource("/").to(|_: web::Data<usize>| HttpResponse::Ok()),
|
||||||
))
|
))
|
||||||
|
@ -208,7 +208,7 @@ mod tests {
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_route_data_extractor() {
|
async fn test_route_data_extractor() {
|
||||||
let mut srv = init_service(
|
let srv = init_service(
|
||||||
App::new().service(
|
App::new().service(
|
||||||
web::resource("/")
|
web::resource("/")
|
||||||
.data(10usize)
|
.data(10usize)
|
||||||
|
@ -222,7 +222,7 @@ mod tests {
|
||||||
assert_eq!(resp.status(), StatusCode::OK);
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
|
|
||||||
// different type
|
// different type
|
||||||
let mut srv = init_service(
|
let srv = init_service(
|
||||||
App::new().service(
|
App::new().service(
|
||||||
web::resource("/")
|
web::resource("/")
|
||||||
.data(10u32)
|
.data(10u32)
|
||||||
|
@ -237,7 +237,7 @@ mod tests {
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_override_data() {
|
async fn test_override_data() {
|
||||||
let mut srv = init_service(App::new().data(1usize).service(
|
let srv = init_service(App::new().data(1usize).service(
|
||||||
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);
|
||||||
|
|
|
@ -113,11 +113,11 @@ where
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Future = HandlerServiceFuture<F, T, R>;
|
type Future = HandlerServiceFuture<F, T, R>;
|
||||||
|
|
||||||
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
fn poll_ready(&self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||||
Poll::Ready(Ok(()))
|
Poll::Ready(Ok(()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call(&mut self, req: ServiceRequest) -> Self::Future {
|
fn call(&self, req: ServiceRequest) -> Self::Future {
|
||||||
let (req, mut payload) = req.into_parts();
|
let (req, mut payload) = req.into_parts();
|
||||||
let fut = T::from_request(&req, &mut payload);
|
let fut = T::from_request(&req, &mut payload);
|
||||||
HandlerServiceFuture::Extract(fut, Some(req), self.hnd.clone())
|
HandlerServiceFuture::Extract(fut, Some(req), self.hnd.clone())
|
||||||
|
|
|
@ -71,6 +71,11 @@
|
||||||
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
||||||
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
||||||
|
|
||||||
|
#[cfg(feature = "openssl")]
|
||||||
|
extern crate tls_openssl as openssl;
|
||||||
|
#[cfg(feature = "rustls")]
|
||||||
|
extern crate tls_rustls as rustls;
|
||||||
|
|
||||||
mod app;
|
mod app;
|
||||||
mod app_service;
|
mod app_service;
|
||||||
mod config;
|
mod config;
|
||||||
|
|
|
@ -80,11 +80,11 @@ where
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Future = CompatMiddlewareFuture<S::Future>;
|
type Future = CompatMiddlewareFuture<S::Future>;
|
||||||
|
|
||||||
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||||
self.service.poll_ready(cx).map_err(From::from)
|
self.service.poll_ready(cx).map_err(From::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call(&mut self, req: Req) -> Self::Future {
|
fn call(&self, req: Req) -> Self::Future {
|
||||||
let fut = self.service.call(req);
|
let fut = self.service.call(req);
|
||||||
CompatMiddlewareFuture { fut }
|
CompatMiddlewareFuture { fut }
|
||||||
}
|
}
|
||||||
|
@ -138,7 +138,7 @@ mod tests {
|
||||||
let logger = Logger::default();
|
let logger = Logger::default();
|
||||||
let compress = Compress::default();
|
let compress = Compress::default();
|
||||||
|
|
||||||
let mut srv = init_service(
|
let srv = init_service(
|
||||||
App::new().service(
|
App::new().service(
|
||||||
web::scope("app")
|
web::scope("app")
|
||||||
.wrap(Compat::new(logger))
|
.wrap(Compat::new(logger))
|
||||||
|
@ -151,7 +151,7 @@ mod tests {
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let req = TestRequest::with_uri("/app/test").to_request();
|
let req = TestRequest::with_uri("/app/test").to_request();
|
||||||
let resp = call_service(&mut srv, req).await;
|
let resp = call_service(&srv, req).await;
|
||||||
assert_eq!(resp.status(), StatusCode::OK);
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,7 +160,7 @@ mod tests {
|
||||||
let logger = Logger::default();
|
let logger = Logger::default();
|
||||||
let compress = Compress::default();
|
let compress = Compress::default();
|
||||||
|
|
||||||
let mut srv = init_service(
|
let srv = init_service(
|
||||||
App::new().service(
|
App::new().service(
|
||||||
web::resource("app/test")
|
web::resource("app/test")
|
||||||
.wrap(Compat::new(logger))
|
.wrap(Compat::new(logger))
|
||||||
|
@ -171,7 +171,7 @@ mod tests {
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let req = TestRequest::with_uri("/app/test").to_request();
|
let req = TestRequest::with_uri("/app/test").to_request();
|
||||||
let resp = call_service(&mut srv, req).await;
|
let resp = call_service(&srv, req).await;
|
||||||
assert_eq!(resp.status(), StatusCode::OK);
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,11 +185,11 @@ mod tests {
|
||||||
|
|
||||||
let logger = Logger::default();
|
let logger = Logger::default();
|
||||||
|
|
||||||
let mut mw = Condition::new(true, Compat::new(logger))
|
let mw = Condition::new(true, Compat::new(logger))
|
||||||
.new_transform(srv.into_service())
|
.new_transform(srv.into_service())
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let resp = call_service(&mut mw, TestRequest::default().to_srv_request()).await;
|
let resp = call_service(&mw, TestRequest::default().to_srv_request()).await;
|
||||||
assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
|
assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,7 +90,7 @@ where
|
||||||
actix_service::forward_ready!(service);
|
actix_service::forward_ready!(service);
|
||||||
|
|
||||||
#[allow(clippy::borrow_interior_mutable_const)]
|
#[allow(clippy::borrow_interior_mutable_const)]
|
||||||
fn call(&mut self, req: ServiceRequest) -> Self::Future {
|
fn call(&self, req: ServiceRequest) -> Self::Future {
|
||||||
// negotiate content-encoding
|
// negotiate content-encoding
|
||||||
let encoding = if let Some(val) = req.headers().get(&ACCEPT_ENCODING) {
|
let encoding = if let Some(val) = req.headers().get(&ACCEPT_ENCODING) {
|
||||||
if let Ok(enc) = val.to_str() {
|
if let Ok(enc) = val.to_str() {
|
||||||
|
|
|
@ -76,14 +76,14 @@ where
|
||||||
type Error = E::Error;
|
type Error = E::Error;
|
||||||
type Future = Either<E::Future, D::Future>;
|
type Future = Either<E::Future, D::Future>;
|
||||||
|
|
||||||
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||||
match self {
|
match self {
|
||||||
ConditionMiddleware::Enable(service) => service.poll_ready(cx),
|
ConditionMiddleware::Enable(service) => service.poll_ready(cx),
|
||||||
ConditionMiddleware::Disable(service) => service.poll_ready(cx),
|
ConditionMiddleware::Disable(service) => service.poll_ready(cx),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call(&mut self, req: Req) -> Self::Future {
|
fn call(&self, req: Req) -> Self::Future {
|
||||||
match self {
|
match self {
|
||||||
ConditionMiddleware::Enable(service) => Either::Left(service.call(req)),
|
ConditionMiddleware::Enable(service) => Either::Left(service.call(req)),
|
||||||
ConditionMiddleware::Disable(service) => Either::Right(service.call(req)),
|
ConditionMiddleware::Disable(service) => Either::Right(service.call(req)),
|
||||||
|
@ -106,7 +106,6 @@ mod tests {
|
||||||
HttpResponse,
|
HttpResponse,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[allow(clippy::unnecessary_wraps)]
|
|
||||||
fn render_500<B>(mut res: ServiceResponse<B>) -> Result<ErrorHandlerResponse<B>> {
|
fn render_500<B>(mut res: ServiceResponse<B>) -> Result<ErrorHandlerResponse<B>> {
|
||||||
res.response_mut()
|
res.response_mut()
|
||||||
.headers_mut()
|
.headers_mut()
|
||||||
|
@ -123,12 +122,12 @@ mod tests {
|
||||||
let mw =
|
let mw =
|
||||||
ErrorHandlers::new().handler(StatusCode::INTERNAL_SERVER_ERROR, render_500);
|
ErrorHandlers::new().handler(StatusCode::INTERNAL_SERVER_ERROR, render_500);
|
||||||
|
|
||||||
let mut mw = Condition::new(true, mw)
|
let mw = Condition::new(true, mw)
|
||||||
.new_transform(srv.into_service())
|
.new_transform(srv.into_service())
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let resp =
|
let resp =
|
||||||
test::call_service(&mut mw, TestRequest::default().to_srv_request()).await;
|
test::call_service(&mw, TestRequest::default().to_srv_request()).await;
|
||||||
assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), "0001");
|
assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), "0001");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,13 +140,13 @@ mod tests {
|
||||||
let mw =
|
let mw =
|
||||||
ErrorHandlers::new().handler(StatusCode::INTERNAL_SERVER_ERROR, render_500);
|
ErrorHandlers::new().handler(StatusCode::INTERNAL_SERVER_ERROR, render_500);
|
||||||
|
|
||||||
let mut mw = Condition::new(false, mw)
|
let mw = Condition::new(false, mw)
|
||||||
.new_transform(srv.into_service())
|
.new_transform(srv.into_service())
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let resp =
|
let resp =
|
||||||
test::call_service(&mut mw, TestRequest::default().to_srv_request()).await;
|
test::call_service(&mw, TestRequest::default().to_srv_request()).await;
|
||||||
assert_eq!(resp.headers().get(CONTENT_TYPE), None);
|
assert_eq!(resp.headers().get(CONTENT_TYPE), None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -143,7 +143,7 @@ where
|
||||||
|
|
||||||
actix_service::forward_ready!(service);
|
actix_service::forward_ready!(service);
|
||||||
|
|
||||||
fn call(&mut self, req: ServiceRequest) -> Self::Future {
|
fn call(&self, req: ServiceRequest) -> Self::Future {
|
||||||
let inner = self.inner.clone();
|
let inner = self.inner.clone();
|
||||||
let fut = self.service.call(req);
|
let fut = self.service.call(req);
|
||||||
|
|
||||||
|
@ -200,7 +200,7 @@ mod tests {
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_default_headers() {
|
async fn test_default_headers() {
|
||||||
let mut mw = DefaultHeaders::new()
|
let mw = DefaultHeaders::new()
|
||||||
.header(CONTENT_TYPE, "0001")
|
.header(CONTENT_TYPE, "0001")
|
||||||
.new_transform(ok_service())
|
.new_transform(ok_service())
|
||||||
.await
|
.await
|
||||||
|
@ -218,7 +218,7 @@ mod tests {
|
||||||
.finish(),
|
.finish(),
|
||||||
))
|
))
|
||||||
};
|
};
|
||||||
let mut mw = DefaultHeaders::new()
|
let mw = DefaultHeaders::new()
|
||||||
.header(CONTENT_TYPE, "0001")
|
.header(CONTENT_TYPE, "0001")
|
||||||
.new_transform(srv.into_service())
|
.new_transform(srv.into_service())
|
||||||
.await
|
.await
|
||||||
|
@ -231,7 +231,7 @@ mod tests {
|
||||||
async fn test_content_type() {
|
async fn test_content_type() {
|
||||||
let srv =
|
let srv =
|
||||||
|req: ServiceRequest| ok(req.into_response(HttpResponse::Ok().finish()));
|
|req: ServiceRequest| ok(req.into_response(HttpResponse::Ok().finish()));
|
||||||
let mut mw = DefaultHeaders::new()
|
let mw = DefaultHeaders::new()
|
||||||
.add_content_type()
|
.add_content_type()
|
||||||
.new_transform(srv.into_service())
|
.new_transform(srv.into_service())
|
||||||
.await
|
.await
|
||||||
|
|
|
@ -123,7 +123,7 @@ where
|
||||||
|
|
||||||
actix_service::forward_ready!(service);
|
actix_service::forward_ready!(service);
|
||||||
|
|
||||||
fn call(&mut self, req: ServiceRequest) -> Self::Future {
|
fn call(&self, req: ServiceRequest) -> Self::Future {
|
||||||
let handlers = self.handlers.clone();
|
let handlers = self.handlers.clone();
|
||||||
let fut = self.service.call(req);
|
let fut = self.service.call(req);
|
||||||
ErrorHandlersFuture::ServiceFuture { fut, handlers }
|
ErrorHandlersFuture::ServiceFuture { fut, handlers }
|
||||||
|
@ -182,7 +182,6 @@ mod tests {
|
||||||
use crate::test::{self, TestRequest};
|
use crate::test::{self, TestRequest};
|
||||||
use crate::HttpResponse;
|
use crate::HttpResponse;
|
||||||
|
|
||||||
#[allow(clippy::unnecessary_wraps)]
|
|
||||||
fn render_500<B>(mut res: ServiceResponse<B>) -> Result<ErrorHandlerResponse<B>> {
|
fn render_500<B>(mut res: ServiceResponse<B>) -> Result<ErrorHandlerResponse<B>> {
|
||||||
res.response_mut()
|
res.response_mut()
|
||||||
.headers_mut()
|
.headers_mut()
|
||||||
|
@ -196,18 +195,17 @@ mod tests {
|
||||||
ok(req.into_response(HttpResponse::InternalServerError().finish()))
|
ok(req.into_response(HttpResponse::InternalServerError().finish()))
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut mw = ErrorHandlers::new()
|
let mw = ErrorHandlers::new()
|
||||||
.handler(StatusCode::INTERNAL_SERVER_ERROR, render_500)
|
.handler(StatusCode::INTERNAL_SERVER_ERROR, render_500)
|
||||||
.new_transform(srv.into_service())
|
.new_transform(srv.into_service())
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let resp =
|
let resp =
|
||||||
test::call_service(&mut mw, TestRequest::default().to_srv_request()).await;
|
test::call_service(&mw, TestRequest::default().to_srv_request()).await;
|
||||||
assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), "0001");
|
assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), "0001");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::unnecessary_wraps)]
|
|
||||||
fn render_500_async<B: 'static>(
|
fn render_500_async<B: 'static>(
|
||||||
mut res: ServiceResponse<B>,
|
mut res: ServiceResponse<B>,
|
||||||
) -> Result<ErrorHandlerResponse<B>> {
|
) -> Result<ErrorHandlerResponse<B>> {
|
||||||
|
@ -223,14 +221,14 @@ mod tests {
|
||||||
ok(req.into_response(HttpResponse::InternalServerError().finish()))
|
ok(req.into_response(HttpResponse::InternalServerError().finish()))
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut mw = ErrorHandlers::new()
|
let mw = ErrorHandlers::new()
|
||||||
.handler(StatusCode::INTERNAL_SERVER_ERROR, render_500_async)
|
.handler(StatusCode::INTERNAL_SERVER_ERROR, render_500_async)
|
||||||
.new_transform(srv.into_service())
|
.new_transform(srv.into_service())
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let resp =
|
let resp =
|
||||||
test::call_service(&mut mw, TestRequest::default().to_srv_request()).await;
|
test::call_service(&mw, TestRequest::default().to_srv_request()).await;
|
||||||
assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), "0001");
|
assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), "0001");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -219,7 +219,7 @@ where
|
||||||
|
|
||||||
actix_service::forward_ready!(service);
|
actix_service::forward_ready!(service);
|
||||||
|
|
||||||
fn call(&mut self, req: ServiceRequest) -> Self::Future {
|
fn call(&self, req: ServiceRequest) -> Self::Future {
|
||||||
if self.inner.exclude.contains(req.path())
|
if self.inner.exclude.contains(req.path())
|
||||||
|| self.inner.exclude_regex.is_match(req.path())
|
|| self.inner.exclude_regex.is_match(req.path())
|
||||||
{
|
{
|
||||||
|
@ -609,7 +609,7 @@ mod tests {
|
||||||
};
|
};
|
||||||
let logger = Logger::new("%% %{User-Agent}i %{X-Test}o %{HOME}e %D test");
|
let logger = Logger::new("%% %{User-Agent}i %{X-Test}o %{HOME}e %D test");
|
||||||
|
|
||||||
let mut srv = logger.new_transform(srv.into_service()).await.unwrap();
|
let srv = logger.new_transform(srv.into_service()).await.unwrap();
|
||||||
|
|
||||||
let req = TestRequest::default()
|
let req = TestRequest::default()
|
||||||
.insert_header((
|
.insert_header((
|
||||||
|
@ -632,7 +632,7 @@ mod tests {
|
||||||
let logger = Logger::new("%% %{User-Agent}i %{X-Test}o %{HOME}e %D test")
|
let logger = Logger::new("%% %{User-Agent}i %{X-Test}o %{HOME}e %D test")
|
||||||
.exclude_regex("\\w");
|
.exclude_regex("\\w");
|
||||||
|
|
||||||
let mut srv = logger.new_transform(srv.into_service()).await.unwrap();
|
let srv = logger.new_transform(srv.into_service()).await.unwrap();
|
||||||
|
|
||||||
let req = TestRequest::default()
|
let req = TestRequest::default()
|
||||||
.insert_header((
|
.insert_header((
|
||||||
|
@ -806,7 +806,7 @@ mod tests {
|
||||||
captured.to_owned()
|
captured.to_owned()
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut srv = logger.new_transform(test::ok_service()).await.unwrap();
|
let srv = logger.new_transform(test::ok_service()).await.unwrap();
|
||||||
|
|
||||||
let req = TestRequest::default().to_srv_request();
|
let req = TestRequest::default().to_srv_request();
|
||||||
srv.call(req).await.unwrap();
|
srv.call(req).await.unwrap();
|
||||||
|
|
|
@ -57,7 +57,7 @@ impl Default for TrailingSlash {
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use actix_web::{web, middleware, App};
|
/// use actix_web::{web, middleware, App};
|
||||||
///
|
///
|
||||||
/// # actix_web::rt::System::new("doctest").block_on(async {
|
/// # actix_web::rt::System::new().block_on(async {
|
||||||
/// let app = App::new()
|
/// let app = App::new()
|
||||||
/// .wrap(middleware::NormalizePath::default())
|
/// .wrap(middleware::NormalizePath::default())
|
||||||
/// .route("/test", web::get().to(|| async { "test" }))
|
/// .route("/test", web::get().to(|| async { "test" }))
|
||||||
|
@ -66,22 +66,22 @@ impl Default for TrailingSlash {
|
||||||
/// use actix_web::http::StatusCode;
|
/// use actix_web::http::StatusCode;
|
||||||
/// use actix_web::test::{call_service, init_service, TestRequest};
|
/// use actix_web::test::{call_service, init_service, TestRequest};
|
||||||
///
|
///
|
||||||
/// let mut app = init_service(app).await;
|
/// let app = init_service(app).await;
|
||||||
///
|
///
|
||||||
/// let req = TestRequest::with_uri("/test").to_request();
|
/// let req = TestRequest::with_uri("/test").to_request();
|
||||||
/// let res = call_service(&mut app, req).await;
|
/// let res = call_service(&app, req).await;
|
||||||
/// assert_eq!(res.status(), StatusCode::OK);
|
/// assert_eq!(res.status(), StatusCode::OK);
|
||||||
///
|
///
|
||||||
/// let req = TestRequest::with_uri("/test/").to_request();
|
/// let req = TestRequest::with_uri("/test/").to_request();
|
||||||
/// let res = call_service(&mut app, req).await;
|
/// let res = call_service(&app, req).await;
|
||||||
/// assert_eq!(res.status(), StatusCode::OK);
|
/// assert_eq!(res.status(), StatusCode::OK);
|
||||||
///
|
///
|
||||||
/// let req = TestRequest::with_uri("/unmatchable").to_request();
|
/// let req = TestRequest::with_uri("/unmatchable").to_request();
|
||||||
/// let res = call_service(&mut app, req).await;
|
/// let res = call_service(&app, req).await;
|
||||||
/// assert_eq!(res.status(), StatusCode::NOT_FOUND);
|
/// assert_eq!(res.status(), StatusCode::NOT_FOUND);
|
||||||
///
|
///
|
||||||
/// let req = TestRequest::with_uri("/unmatchable/").to_request();
|
/// let req = TestRequest::with_uri("/unmatchable/").to_request();
|
||||||
/// let res = call_service(&mut app, req).await;
|
/// let res = call_service(&app, req).await;
|
||||||
/// assert_eq!(res.status(), StatusCode::NOT_FOUND);
|
/// assert_eq!(res.status(), StatusCode::NOT_FOUND);
|
||||||
/// # })
|
/// # })
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -132,7 +132,7 @@ where
|
||||||
|
|
||||||
actix_service::forward_ready!(service);
|
actix_service::forward_ready!(service);
|
||||||
|
|
||||||
fn call(&mut self, mut req: ServiceRequest) -> Self::Future {
|
fn call(&self, mut req: ServiceRequest) -> Self::Future {
|
||||||
let head = req.head_mut();
|
let head = req.head_mut();
|
||||||
|
|
||||||
let original_path = head.uri.path();
|
let original_path = head.uri.path();
|
||||||
|
@ -195,7 +195,7 @@ mod tests {
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_wrap() {
|
async fn test_wrap() {
|
||||||
let mut app = init_service(
|
let app = init_service(
|
||||||
App::new()
|
App::new()
|
||||||
.wrap(NormalizePath::default())
|
.wrap(NormalizePath::default())
|
||||||
.service(web::resource("/").to(HttpResponse::Ok))
|
.service(web::resource("/").to(HttpResponse::Ok))
|
||||||
|
@ -204,37 +204,37 @@ mod tests {
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let req = TestRequest::with_uri("/").to_request();
|
let req = TestRequest::with_uri("/").to_request();
|
||||||
let res = call_service(&mut app, req).await;
|
let res = call_service(&app, req).await;
|
||||||
assert!(res.status().is_success());
|
assert!(res.status().is_success());
|
||||||
|
|
||||||
let req = TestRequest::with_uri("/?query=test").to_request();
|
let req = TestRequest::with_uri("/?query=test").to_request();
|
||||||
let res = call_service(&mut app, req).await;
|
let res = call_service(&app, req).await;
|
||||||
assert!(res.status().is_success());
|
assert!(res.status().is_success());
|
||||||
|
|
||||||
let req = TestRequest::with_uri("///").to_request();
|
let req = TestRequest::with_uri("///").to_request();
|
||||||
let res = call_service(&mut app, req).await;
|
let res = call_service(&app, req).await;
|
||||||
assert!(res.status().is_success());
|
assert!(res.status().is_success());
|
||||||
|
|
||||||
let req = TestRequest::with_uri("/v1//something////").to_request();
|
let req = TestRequest::with_uri("/v1//something////").to_request();
|
||||||
let res = call_service(&mut app, req).await;
|
let res = call_service(&app, req).await;
|
||||||
assert!(res.status().is_success());
|
assert!(res.status().is_success());
|
||||||
|
|
||||||
let req2 = TestRequest::with_uri("//v1/something").to_request();
|
let req2 = TestRequest::with_uri("//v1/something").to_request();
|
||||||
let res2 = call_service(&mut app, req2).await;
|
let res2 = call_service(&app, req2).await;
|
||||||
assert!(res2.status().is_success());
|
assert!(res2.status().is_success());
|
||||||
|
|
||||||
let req3 = TestRequest::with_uri("//v1//////something").to_request();
|
let req3 = TestRequest::with_uri("//v1//////something").to_request();
|
||||||
let res3 = call_service(&mut app, req3).await;
|
let res3 = call_service(&app, req3).await;
|
||||||
assert!(res3.status().is_success());
|
assert!(res3.status().is_success());
|
||||||
|
|
||||||
let req4 = TestRequest::with_uri("/v1//something").to_request();
|
let req4 = TestRequest::with_uri("/v1//something").to_request();
|
||||||
let res4 = call_service(&mut app, req4).await;
|
let res4 = call_service(&app, req4).await;
|
||||||
assert!(res4.status().is_success());
|
assert!(res4.status().is_success());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn trim_trailing_slashes() {
|
async fn trim_trailing_slashes() {
|
||||||
let mut app = init_service(
|
let app = init_service(
|
||||||
App::new()
|
App::new()
|
||||||
.wrap(NormalizePath(TrailingSlash::Trim))
|
.wrap(NormalizePath(TrailingSlash::Trim))
|
||||||
.service(web::resource("/").to(HttpResponse::Ok))
|
.service(web::resource("/").to(HttpResponse::Ok))
|
||||||
|
@ -244,37 +244,37 @@ mod tests {
|
||||||
|
|
||||||
// root paths should still work
|
// root paths should still work
|
||||||
let req = TestRequest::with_uri("/").to_request();
|
let req = TestRequest::with_uri("/").to_request();
|
||||||
let res = call_service(&mut app, req).await;
|
let res = call_service(&app, req).await;
|
||||||
assert!(res.status().is_success());
|
assert!(res.status().is_success());
|
||||||
|
|
||||||
let req = TestRequest::with_uri("/?query=test").to_request();
|
let req = TestRequest::with_uri("/?query=test").to_request();
|
||||||
let res = call_service(&mut app, req).await;
|
let res = call_service(&app, req).await;
|
||||||
assert!(res.status().is_success());
|
assert!(res.status().is_success());
|
||||||
|
|
||||||
let req = TestRequest::with_uri("///").to_request();
|
let req = TestRequest::with_uri("///").to_request();
|
||||||
let res = call_service(&mut app, req).await;
|
let res = call_service(&app, req).await;
|
||||||
assert!(res.status().is_success());
|
assert!(res.status().is_success());
|
||||||
|
|
||||||
let req = TestRequest::with_uri("/v1/something////").to_request();
|
let req = TestRequest::with_uri("/v1/something////").to_request();
|
||||||
let res = call_service(&mut app, req).await;
|
let res = call_service(&app, req).await;
|
||||||
assert!(res.status().is_success());
|
assert!(res.status().is_success());
|
||||||
|
|
||||||
let req2 = TestRequest::with_uri("/v1/something/").to_request();
|
let req2 = TestRequest::with_uri("/v1/something/").to_request();
|
||||||
let res2 = call_service(&mut app, req2).await;
|
let res2 = call_service(&app, req2).await;
|
||||||
assert!(res2.status().is_success());
|
assert!(res2.status().is_success());
|
||||||
|
|
||||||
let req3 = TestRequest::with_uri("//v1//something//").to_request();
|
let req3 = TestRequest::with_uri("//v1//something//").to_request();
|
||||||
let res3 = call_service(&mut app, req3).await;
|
let res3 = call_service(&app, req3).await;
|
||||||
assert!(res3.status().is_success());
|
assert!(res3.status().is_success());
|
||||||
|
|
||||||
let req4 = TestRequest::with_uri("//v1//something").to_request();
|
let req4 = TestRequest::with_uri("//v1//something").to_request();
|
||||||
let res4 = call_service(&mut app, req4).await;
|
let res4 = call_service(&app, req4).await;
|
||||||
assert!(res4.status().is_success());
|
assert!(res4.status().is_success());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn keep_trailing_slash_unchanged() {
|
async fn keep_trailing_slash_unchanged() {
|
||||||
let mut app = init_service(
|
let app = init_service(
|
||||||
App::new()
|
App::new()
|
||||||
.wrap(NormalizePath(TrailingSlash::MergeOnly))
|
.wrap(NormalizePath(TrailingSlash::MergeOnly))
|
||||||
.service(web::resource("/").to(HttpResponse::Ok))
|
.service(web::resource("/").to(HttpResponse::Ok))
|
||||||
|
@ -299,7 +299,7 @@ mod tests {
|
||||||
|
|
||||||
for (path, success) in tests {
|
for (path, success) in tests {
|
||||||
let req = TestRequest::with_uri(path).to_request();
|
let req = TestRequest::with_uri(path).to_request();
|
||||||
let res = call_service(&mut app, req).await;
|
let res = call_service(&app, req).await;
|
||||||
assert_eq!(res.status().is_success(), success);
|
assert_eq!(res.status().is_success(), success);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -311,7 +311,7 @@ mod tests {
|
||||||
ready(Ok(req.into_response(HttpResponse::Ok().finish())))
|
ready(Ok(req.into_response(HttpResponse::Ok().finish())))
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut normalize = NormalizePath::default()
|
let normalize = NormalizePath::default()
|
||||||
.new_transform(srv.into_service())
|
.new_transform(srv.into_service())
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -342,7 +342,7 @@ mod tests {
|
||||||
ready(Ok(req.into_response(HttpResponse::Ok().finish())))
|
ready(Ok(req.into_response(HttpResponse::Ok().finish())))
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut normalize = NormalizePath::default()
|
let normalize = NormalizePath::default()
|
||||||
.new_transform(srv.into_service())
|
.new_transform(srv.into_service())
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -359,7 +359,7 @@ mod tests {
|
||||||
ready(Ok(req.into_response(HttpResponse::Ok().finish())))
|
ready(Ok(req.into_response(HttpResponse::Ok().finish())))
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut normalize = NormalizePath::default()
|
let normalize = NormalizePath::default()
|
||||||
.new_transform(srv.into_service())
|
.new_transform(srv.into_service())
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
|
@ -556,7 +556,7 @@ mod tests {
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_drop_http_request_pool() {
|
async fn test_drop_http_request_pool() {
|
||||||
let mut srv = init_service(App::new().service(web::resource("/").to(
|
let srv = init_service(App::new().service(web::resource("/").to(
|
||||||
|req: HttpRequest| {
|
|req: HttpRequest| {
|
||||||
HttpResponse::Ok()
|
HttpResponse::Ok()
|
||||||
.insert_header(("pool_cap", req.app_state().pool().cap))
|
.insert_header(("pool_cap", req.app_state().pool().cap))
|
||||||
|
@ -566,7 +566,7 @@ mod tests {
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let req = TestRequest::default().to_request();
|
let req = TestRequest::default().to_request();
|
||||||
let resp = call_service(&mut srv, req).await;
|
let resp = call_service(&srv, req).await;
|
||||||
|
|
||||||
drop(srv);
|
drop(srv);
|
||||||
|
|
||||||
|
@ -575,7 +575,7 @@ mod tests {
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_data() {
|
async fn test_data() {
|
||||||
let mut srv = init_service(App::new().app_data(10usize).service(
|
let srv = init_service(App::new().app_data(10usize).service(
|
||||||
web::resource("/").to(|req: HttpRequest| {
|
web::resource("/").to(|req: HttpRequest| {
|
||||||
if req.app_data::<usize>().is_some() {
|
if req.app_data::<usize>().is_some() {
|
||||||
HttpResponse::Ok()
|
HttpResponse::Ok()
|
||||||
|
@ -587,10 +587,10 @@ mod tests {
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let req = TestRequest::default().to_request();
|
let req = TestRequest::default().to_request();
|
||||||
let resp = call_service(&mut srv, req).await;
|
let resp = call_service(&srv, req).await;
|
||||||
assert_eq!(resp.status(), StatusCode::OK);
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
|
|
||||||
let mut srv = init_service(App::new().app_data(10u32).service(
|
let srv = init_service(App::new().app_data(10u32).service(
|
||||||
web::resource("/").to(|req: HttpRequest| {
|
web::resource("/").to(|req: HttpRequest| {
|
||||||
if req.app_data::<usize>().is_some() {
|
if req.app_data::<usize>().is_some() {
|
||||||
HttpResponse::Ok()
|
HttpResponse::Ok()
|
||||||
|
@ -602,7 +602,7 @@ mod tests {
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let req = TestRequest::default().to_request();
|
let req = TestRequest::default().to_request();
|
||||||
let resp = call_service(&mut srv, req).await;
|
let resp = call_service(&srv, req).await;
|
||||||
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
|
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -614,7 +614,7 @@ mod tests {
|
||||||
HttpResponse::Ok().body(num.to_string())
|
HttpResponse::Ok().body(num.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut srv = init_service(
|
let srv = init_service(
|
||||||
App::new()
|
App::new()
|
||||||
.app_data(88usize)
|
.app_data(88usize)
|
||||||
.service(web::resource("/").route(web::get().to(echo_usize)))
|
.service(web::resource("/").route(web::get().to(echo_usize)))
|
||||||
|
@ -645,7 +645,7 @@ mod tests {
|
||||||
HttpResponse::Ok().body(num.to_string())
|
HttpResponse::Ok().body(num.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut srv = init_service(
|
let srv = init_service(
|
||||||
App::new()
|
App::new()
|
||||||
.app_data(88usize)
|
.app_data(88usize)
|
||||||
.service(web::resource("/").route(web::get().to(echo_usize)))
|
.service(web::resource("/").route(web::get().to(echo_usize)))
|
||||||
|
@ -685,7 +685,7 @@ mod tests {
|
||||||
let tracker = Rc::new(RefCell::new(Tracker { dropped: false }));
|
let tracker = Rc::new(RefCell::new(Tracker { dropped: false }));
|
||||||
{
|
{
|
||||||
let tracker2 = Rc::clone(&tracker);
|
let tracker2 = Rc::clone(&tracker);
|
||||||
let mut srv = init_service(App::new().data(10u32).service(
|
let srv = init_service(App::new().data(10u32).service(
|
||||||
web::resource("/").to(move |req: HttpRequest| {
|
web::resource("/").to(move |req: HttpRequest| {
|
||||||
req.extensions_mut().insert(Foo {
|
req.extensions_mut().insert(Foo {
|
||||||
tracker: Rc::clone(&tracker2),
|
tracker: Rc::clone(&tracker2),
|
||||||
|
@ -696,7 +696,7 @@ mod tests {
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let req = TestRequest::default().to_request();
|
let req = TestRequest::default().to_request();
|
||||||
let resp = call_service(&mut srv, req).await;
|
let resp = call_service(&srv, req).await;
|
||||||
assert_eq!(resp.status(), StatusCode::OK);
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -705,7 +705,7 @@ mod tests {
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn extract_path_pattern() {
|
async fn extract_path_pattern() {
|
||||||
let mut srv = init_service(
|
let srv = init_service(
|
||||||
App::new().service(
|
App::new().service(
|
||||||
web::scope("/user/{id}")
|
web::scope("/user/{id}")
|
||||||
.service(web::resource("/profile").route(web::get().to(
|
.service(web::resource("/profile").route(web::get().to(
|
||||||
|
@ -727,17 +727,17 @@ mod tests {
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let req = TestRequest::get().uri("/user/22/profile").to_request();
|
let req = TestRequest::get().uri("/user/22/profile").to_request();
|
||||||
let res = call_service(&mut srv, req).await;
|
let res = call_service(&srv, req).await;
|
||||||
assert_eq!(res.status(), StatusCode::OK);
|
assert_eq!(res.status(), StatusCode::OK);
|
||||||
|
|
||||||
let req = TestRequest::get().uri("/user/22/not-exist").to_request();
|
let req = TestRequest::get().uri("/user/22/not-exist").to_request();
|
||||||
let res = call_service(&mut srv, req).await;
|
let res = call_service(&srv, req).await;
|
||||||
assert_eq!(res.status(), StatusCode::OK);
|
assert_eq!(res.status(), StatusCode::OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn extract_path_pattern_complex() {
|
async fn extract_path_pattern_complex() {
|
||||||
let mut srv = init_service(
|
let srv = init_service(
|
||||||
App::new()
|
App::new()
|
||||||
.service(web::scope("/user").service(web::scope("/{id}").service(
|
.service(web::scope("/user").service(web::scope("/{id}").service(
|
||||||
web::resource("").to(move |req: HttpRequest| {
|
web::resource("").to(move |req: HttpRequest| {
|
||||||
|
@ -759,15 +759,15 @@ mod tests {
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let req = TestRequest::get().uri("/user/test").to_request();
|
let req = TestRequest::get().uri("/user/test").to_request();
|
||||||
let res = call_service(&mut srv, req).await;
|
let res = call_service(&srv, req).await;
|
||||||
assert_eq!(res.status(), StatusCode::OK);
|
assert_eq!(res.status(), StatusCode::OK);
|
||||||
|
|
||||||
let req = TestRequest::get().uri("/").to_request();
|
let req = TestRequest::get().uri("/").to_request();
|
||||||
let res = call_service(&mut srv, req).await;
|
let res = call_service(&srv, req).await;
|
||||||
assert_eq!(res.status(), StatusCode::OK);
|
assert_eq!(res.status(), StatusCode::OK);
|
||||||
|
|
||||||
let req = TestRequest::get().uri("/not-exist").to_request();
|
let req = TestRequest::get().uri("/not-exist").to_request();
|
||||||
let res = call_service(&mut srv, req).await;
|
let res = call_service(&srv, req).await;
|
||||||
assert_eq!(res.status(), StatusCode::OK);
|
assert_eq!(res.status(), StatusCode::OK);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -102,7 +102,7 @@ mod tests {
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn req_data_extractor() {
|
async fn req_data_extractor() {
|
||||||
let mut srv = init_service(
|
let srv = init_service(
|
||||||
App::new()
|
App::new()
|
||||||
.wrap_fn(|req, srv| {
|
.wrap_fn(|req, srv| {
|
||||||
if req.method() == Method::POST {
|
if req.method() == Method::POST {
|
||||||
|
@ -142,7 +142,7 @@ mod tests {
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn req_data_internal_mutability() {
|
async fn req_data_internal_mutability() {
|
||||||
let mut srv = init_service(
|
let srv = init_service(
|
||||||
App::new()
|
App::new()
|
||||||
.wrap_fn(|req, srv| {
|
.wrap_fn(|req, srv| {
|
||||||
let data_before = Rc::new(RefCell::new(42u32));
|
let data_before = Rc::new(RefCell::new(42u32));
|
||||||
|
|
|
@ -328,7 +328,7 @@ where
|
||||||
>,
|
>,
|
||||||
>
|
>
|
||||||
where
|
where
|
||||||
F: FnMut(ServiceRequest, &mut T::Service) -> R + Clone,
|
F: Fn(ServiceRequest, &T::Service) -> R + Clone,
|
||||||
R: Future<Output = Result<ServiceResponse, Error>>,
|
R: Future<Output = Result<ServiceResponse, Error>>,
|
||||||
{
|
{
|
||||||
Resource {
|
Resource {
|
||||||
|
@ -471,8 +471,8 @@ impl Service<ServiceRequest> for ResourceService {
|
||||||
|
|
||||||
actix_service::always_ready!();
|
actix_service::always_ready!();
|
||||||
|
|
||||||
fn call(&mut self, mut req: ServiceRequest) -> Self::Future {
|
fn call(&self, mut req: ServiceRequest) -> Self::Future {
|
||||||
for route in self.routes.iter_mut() {
|
for route in self.routes.iter() {
|
||||||
if route.check(&mut req) {
|
if route.check(&mut req) {
|
||||||
if let Some(ref app_data) = self.app_data {
|
if let Some(ref app_data) = self.app_data {
|
||||||
req.add_data_container(app_data.clone());
|
req.add_data_container(app_data.clone());
|
||||||
|
@ -530,7 +530,7 @@ mod tests {
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_middleware() {
|
async fn test_middleware() {
|
||||||
let mut srv =
|
let srv =
|
||||||
init_service(
|
init_service(
|
||||||
App::new().service(
|
App::new().service(
|
||||||
web::resource("/test")
|
web::resource("/test")
|
||||||
|
@ -544,7 +544,7 @@ mod tests {
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
let req = TestRequest::with_uri("/test").to_request();
|
let req = TestRequest::with_uri("/test").to_request();
|
||||||
let resp = call_service(&mut srv, req).await;
|
let resp = call_service(&srv, req).await;
|
||||||
assert_eq!(resp.status(), StatusCode::OK);
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
resp.headers().get(header::CONTENT_TYPE).unwrap(),
|
resp.headers().get(header::CONTENT_TYPE).unwrap(),
|
||||||
|
@ -554,7 +554,7 @@ mod tests {
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_middleware_fn() {
|
async fn test_middleware_fn() {
|
||||||
let mut srv = init_service(
|
let srv = init_service(
|
||||||
App::new().service(
|
App::new().service(
|
||||||
web::resource("/test")
|
web::resource("/test")
|
||||||
.wrap_fn(|req, srv| {
|
.wrap_fn(|req, srv| {
|
||||||
|
@ -574,7 +574,7 @@ mod tests {
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
let req = TestRequest::with_uri("/test").to_request();
|
let req = TestRequest::with_uri("/test").to_request();
|
||||||
let resp = call_service(&mut srv, req).await;
|
let resp = call_service(&srv, req).await;
|
||||||
assert_eq!(resp.status(), StatusCode::OK);
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
resp.headers().get(header::CONTENT_TYPE).unwrap(),
|
resp.headers().get(header::CONTENT_TYPE).unwrap(),
|
||||||
|
@ -584,20 +584,20 @@ mod tests {
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_to() {
|
async fn test_to() {
|
||||||
let mut srv =
|
let srv =
|
||||||
init_service(App::new().service(web::resource("/test").to(|| async {
|
init_service(App::new().service(web::resource("/test").to(|| async {
|
||||||
sleep(Duration::from_millis(100)).await;
|
sleep(Duration::from_millis(100)).await;
|
||||||
Ok::<_, Error>(HttpResponse::Ok())
|
Ok::<_, Error>(HttpResponse::Ok())
|
||||||
})))
|
})))
|
||||||
.await;
|
.await;
|
||||||
let req = TestRequest::with_uri("/test").to_request();
|
let req = TestRequest::with_uri("/test").to_request();
|
||||||
let resp = call_service(&mut srv, req).await;
|
let resp = call_service(&srv, req).await;
|
||||||
assert_eq!(resp.status(), StatusCode::OK);
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_pattern() {
|
async fn test_pattern() {
|
||||||
let mut srv = init_service(
|
let srv = init_service(
|
||||||
App::new().service(
|
App::new().service(
|
||||||
web::resource(["/test", "/test2"])
|
web::resource(["/test", "/test2"])
|
||||||
.to(|| async { Ok::<_, Error>(HttpResponse::Ok()) }),
|
.to(|| async { Ok::<_, Error>(HttpResponse::Ok()) }),
|
||||||
|
@ -605,16 +605,16 @@ mod tests {
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
let req = TestRequest::with_uri("/test").to_request();
|
let req = TestRequest::with_uri("/test").to_request();
|
||||||
let resp = call_service(&mut srv, req).await;
|
let resp = call_service(&srv, req).await;
|
||||||
assert_eq!(resp.status(), StatusCode::OK);
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
let req = TestRequest::with_uri("/test2").to_request();
|
let req = TestRequest::with_uri("/test2").to_request();
|
||||||
let resp = call_service(&mut srv, req).await;
|
let resp = call_service(&srv, req).await;
|
||||||
assert_eq!(resp.status(), StatusCode::OK);
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_default_resource() {
|
async fn test_default_resource() {
|
||||||
let mut srv = init_service(
|
let srv = init_service(
|
||||||
App::new()
|
App::new()
|
||||||
.service(web::resource("/test").route(web::get().to(HttpResponse::Ok)))
|
.service(web::resource("/test").route(web::get().to(HttpResponse::Ok)))
|
||||||
.default_service(|r: ServiceRequest| {
|
.default_service(|r: ServiceRequest| {
|
||||||
|
@ -623,16 +623,16 @@ mod tests {
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
let req = TestRequest::with_uri("/test").to_request();
|
let req = TestRequest::with_uri("/test").to_request();
|
||||||
let resp = call_service(&mut srv, req).await;
|
let resp = call_service(&srv, req).await;
|
||||||
assert_eq!(resp.status(), StatusCode::OK);
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
|
|
||||||
let req = TestRequest::with_uri("/test")
|
let req = TestRequest::with_uri("/test")
|
||||||
.method(Method::POST)
|
.method(Method::POST)
|
||||||
.to_request();
|
.to_request();
|
||||||
let resp = call_service(&mut srv, req).await;
|
let resp = call_service(&srv, req).await;
|
||||||
assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED);
|
assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED);
|
||||||
|
|
||||||
let mut srv = init_service(
|
let srv = init_service(
|
||||||
App::new().service(
|
App::new().service(
|
||||||
web::resource("/test")
|
web::resource("/test")
|
||||||
.route(web::get().to(HttpResponse::Ok))
|
.route(web::get().to(HttpResponse::Ok))
|
||||||
|
@ -644,19 +644,19 @@ mod tests {
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let req = TestRequest::with_uri("/test").to_request();
|
let req = TestRequest::with_uri("/test").to_request();
|
||||||
let resp = call_service(&mut srv, req).await;
|
let resp = call_service(&srv, req).await;
|
||||||
assert_eq!(resp.status(), StatusCode::OK);
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
|
|
||||||
let req = TestRequest::with_uri("/test")
|
let req = TestRequest::with_uri("/test")
|
||||||
.method(Method::POST)
|
.method(Method::POST)
|
||||||
.to_request();
|
.to_request();
|
||||||
let resp = call_service(&mut srv, req).await;
|
let resp = call_service(&srv, req).await;
|
||||||
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
|
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_resource_guards() {
|
async fn test_resource_guards() {
|
||||||
let mut srv = init_service(
|
let srv = init_service(
|
||||||
App::new()
|
App::new()
|
||||||
.service(
|
.service(
|
||||||
web::resource("/test/{p}")
|
web::resource("/test/{p}")
|
||||||
|
@ -679,25 +679,25 @@ mod tests {
|
||||||
let req = TestRequest::with_uri("/test/it")
|
let req = TestRequest::with_uri("/test/it")
|
||||||
.method(Method::GET)
|
.method(Method::GET)
|
||||||
.to_request();
|
.to_request();
|
||||||
let resp = call_service(&mut srv, req).await;
|
let resp = call_service(&srv, req).await;
|
||||||
assert_eq!(resp.status(), StatusCode::OK);
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
|
|
||||||
let req = TestRequest::with_uri("/test/it")
|
let req = TestRequest::with_uri("/test/it")
|
||||||
.method(Method::PUT)
|
.method(Method::PUT)
|
||||||
.to_request();
|
.to_request();
|
||||||
let resp = call_service(&mut srv, req).await;
|
let resp = call_service(&srv, req).await;
|
||||||
assert_eq!(resp.status(), StatusCode::CREATED);
|
assert_eq!(resp.status(), StatusCode::CREATED);
|
||||||
|
|
||||||
let req = TestRequest::with_uri("/test/it")
|
let req = TestRequest::with_uri("/test/it")
|
||||||
.method(Method::DELETE)
|
.method(Method::DELETE)
|
||||||
.to_request();
|
.to_request();
|
||||||
let resp = call_service(&mut srv, req).await;
|
let resp = call_service(&srv, req).await;
|
||||||
assert_eq!(resp.status(), StatusCode::NO_CONTENT);
|
assert_eq!(resp.status(), StatusCode::NO_CONTENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_data() {
|
async fn test_data() {
|
||||||
let mut srv = init_service(
|
let srv = init_service(
|
||||||
App::new()
|
App::new()
|
||||||
.data(1.0f64)
|
.data(1.0f64)
|
||||||
.data(1usize)
|
.data(1usize)
|
||||||
|
@ -723,13 +723,13 @@ mod tests {
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let req = TestRequest::get().uri("/test").to_request();
|
let req = TestRequest::get().uri("/test").to_request();
|
||||||
let resp = call_service(&mut srv, req).await;
|
let resp = call_service(&srv, req).await;
|
||||||
assert_eq!(resp.status(), StatusCode::OK);
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_data_default_service() {
|
async fn test_data_default_service() {
|
||||||
let mut srv = init_service(
|
let srv = init_service(
|
||||||
App::new().data(1usize).service(
|
App::new().data(1usize).service(
|
||||||
web::resource("/test")
|
web::resource("/test")
|
||||||
.data(10usize)
|
.data(10usize)
|
||||||
|
@ -742,7 +742,7 @@ mod tests {
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let req = TestRequest::get().uri("/test").to_request();
|
let req = TestRequest::get().uri("/test").to_request();
|
||||||
let resp = call_service(&mut srv, req).await;
|
let resp = call_service(&srv, req).await;
|
||||||
assert_eq!(resp.status(), StatusCode::OK);
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -260,7 +260,7 @@ pub(crate) mod tests {
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_option_responder() {
|
async fn test_option_responder() {
|
||||||
let mut srv = init_service(
|
let srv = init_service(
|
||||||
App::new()
|
App::new()
|
||||||
.service(
|
.service(
|
||||||
web::resource("/none").to(|| async { Option::<&'static str>::None }),
|
web::resource("/none").to(|| async { Option::<&'static str>::None }),
|
||||||
|
|
22
src/route.rs
22
src/route.rs
|
@ -121,11 +121,11 @@ impl Service<ServiceRequest> for RouteService {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
|
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
|
||||||
|
|
||||||
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||||
self.service.poll_ready(cx)
|
self.service.poll_ready(cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call(&mut self, req: ServiceRequest) -> Self::Future {
|
fn call(&self, req: ServiceRequest) -> Self::Future {
|
||||||
self.service.call(req)
|
self.service.call(req)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -299,11 +299,11 @@ where
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
|
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
|
||||||
|
|
||||||
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||||
self.service.poll_ready(cx)
|
self.service.poll_ready(cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call(&mut self, req: ServiceRequest) -> Self::Future {
|
fn call(&self, req: ServiceRequest) -> Self::Future {
|
||||||
Box::pin(self.service.call(req))
|
Box::pin(self.service.call(req))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -327,7 +327,7 @@ mod tests {
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_route() {
|
async fn test_route() {
|
||||||
let mut srv = init_service(
|
let srv = init_service(
|
||||||
App::new()
|
App::new()
|
||||||
.service(
|
.service(
|
||||||
web::resource("/test")
|
web::resource("/test")
|
||||||
|
@ -356,35 +356,35 @@ mod tests {
|
||||||
let req = TestRequest::with_uri("/test")
|
let req = TestRequest::with_uri("/test")
|
||||||
.method(Method::GET)
|
.method(Method::GET)
|
||||||
.to_request();
|
.to_request();
|
||||||
let resp = call_service(&mut srv, req).await;
|
let resp = call_service(&srv, req).await;
|
||||||
assert_eq!(resp.status(), StatusCode::OK);
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
|
|
||||||
let req = TestRequest::with_uri("/test")
|
let req = TestRequest::with_uri("/test")
|
||||||
.method(Method::POST)
|
.method(Method::POST)
|
||||||
.to_request();
|
.to_request();
|
||||||
let resp = call_service(&mut srv, req).await;
|
let resp = call_service(&srv, req).await;
|
||||||
assert_eq!(resp.status(), StatusCode::CREATED);
|
assert_eq!(resp.status(), StatusCode::CREATED);
|
||||||
|
|
||||||
let req = TestRequest::with_uri("/test")
|
let req = TestRequest::with_uri("/test")
|
||||||
.method(Method::PUT)
|
.method(Method::PUT)
|
||||||
.to_request();
|
.to_request();
|
||||||
let resp = call_service(&mut srv, req).await;
|
let resp = call_service(&srv, req).await;
|
||||||
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
|
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
|
||||||
|
|
||||||
let req = TestRequest::with_uri("/test")
|
let req = TestRequest::with_uri("/test")
|
||||||
.method(Method::DELETE)
|
.method(Method::DELETE)
|
||||||
.to_request();
|
.to_request();
|
||||||
let resp = call_service(&mut srv, req).await;
|
let resp = call_service(&srv, req).await;
|
||||||
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
|
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
|
||||||
|
|
||||||
let req = TestRequest::with_uri("/test")
|
let req = TestRequest::with_uri("/test")
|
||||||
.method(Method::HEAD)
|
.method(Method::HEAD)
|
||||||
.to_request();
|
.to_request();
|
||||||
let resp = call_service(&mut srv, req).await;
|
let resp = call_service(&srv, req).await;
|
||||||
assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED);
|
assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED);
|
||||||
|
|
||||||
let req = TestRequest::with_uri("/json").to_request();
|
let req = TestRequest::with_uri("/json").to_request();
|
||||||
let resp = call_service(&mut srv, req).await;
|
let resp = call_service(&srv, req).await;
|
||||||
assert_eq!(resp.status(), StatusCode::OK);
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
|
|
||||||
let body = read_body(resp).await;
|
let body = read_body(resp).await;
|
||||||
|
|
77
src/scope.rs
77
src/scope.rs
|
@ -380,7 +380,7 @@ where
|
||||||
>,
|
>,
|
||||||
>
|
>
|
||||||
where
|
where
|
||||||
F: FnMut(ServiceRequest, &mut T::Service) -> R + Clone,
|
F: Fn(ServiceRequest, &T::Service) -> R + Clone,
|
||||||
R: Future<Output = Result<ServiceResponse, Error>>,
|
R: Future<Output = Result<ServiceResponse, Error>>,
|
||||||
{
|
{
|
||||||
Scope {
|
Scope {
|
||||||
|
@ -526,8 +526,8 @@ impl Service<ServiceRequest> for ScopeService {
|
||||||
|
|
||||||
actix_service::always_ready!();
|
actix_service::always_ready!();
|
||||||
|
|
||||||
fn call(&mut self, mut req: ServiceRequest) -> Self::Future {
|
fn call(&self, mut req: ServiceRequest) -> Self::Future {
|
||||||
let res = self.router.recognize_mut_checked(&mut req, |req, guards| {
|
let res = self.router.recognize_checked(&mut req, |req, guards| {
|
||||||
if let Some(ref guards) = guards {
|
if let Some(ref guards) = guards {
|
||||||
for f in guards {
|
for f in guards {
|
||||||
if !f.check(req.head()) {
|
if !f.check(req.head()) {
|
||||||
|
@ -589,7 +589,7 @@ mod tests {
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_scope() {
|
async fn test_scope() {
|
||||||
let mut srv = init_service(App::new().service(
|
let srv = init_service(App::new().service(
|
||||||
web::scope("/app").service(web::resource("/path1").to(HttpResponse::Ok)),
|
web::scope("/app").service(web::resource("/path1").to(HttpResponse::Ok)),
|
||||||
))
|
))
|
||||||
.await;
|
.await;
|
||||||
|
@ -601,7 +601,7 @@ mod tests {
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_scope_root() {
|
async fn test_scope_root() {
|
||||||
let mut srv = init_service(
|
let srv = init_service(
|
||||||
App::new().service(
|
App::new().service(
|
||||||
web::scope("/app")
|
web::scope("/app")
|
||||||
.service(web::resource("").to(HttpResponse::Ok))
|
.service(web::resource("").to(HttpResponse::Ok))
|
||||||
|
@ -621,7 +621,7 @@ mod tests {
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_scope_root2() {
|
async fn test_scope_root2() {
|
||||||
let mut srv = init_service(App::new().service(
|
let srv = init_service(App::new().service(
|
||||||
web::scope("/app/").service(web::resource("").to(HttpResponse::Ok)),
|
web::scope("/app/").service(web::resource("").to(HttpResponse::Ok)),
|
||||||
))
|
))
|
||||||
.await;
|
.await;
|
||||||
|
@ -637,7 +637,7 @@ mod tests {
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_scope_root3() {
|
async fn test_scope_root3() {
|
||||||
let mut srv = init_service(App::new().service(
|
let srv = init_service(App::new().service(
|
||||||
web::scope("/app/").service(web::resource("/").to(HttpResponse::Ok)),
|
web::scope("/app/").service(web::resource("/").to(HttpResponse::Ok)),
|
||||||
))
|
))
|
||||||
.await;
|
.await;
|
||||||
|
@ -653,7 +653,7 @@ mod tests {
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_scope_route() {
|
async fn test_scope_route() {
|
||||||
let mut srv = init_service(
|
let srv = init_service(
|
||||||
App::new().service(
|
App::new().service(
|
||||||
web::scope("app")
|
web::scope("app")
|
||||||
.route("/path1", web::get().to(HttpResponse::Ok))
|
.route("/path1", web::get().to(HttpResponse::Ok))
|
||||||
|
@ -681,7 +681,7 @@ mod tests {
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_scope_route_without_leading_slash() {
|
async fn test_scope_route_without_leading_slash() {
|
||||||
let mut srv = init_service(
|
let srv = init_service(
|
||||||
App::new().service(
|
App::new().service(
|
||||||
web::scope("app").service(
|
web::scope("app").service(
|
||||||
web::resource("path1")
|
web::resource("path1")
|
||||||
|
@ -711,7 +711,7 @@ mod tests {
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_scope_guard() {
|
async fn test_scope_guard() {
|
||||||
let mut srv = init_service(
|
let srv = init_service(
|
||||||
App::new().service(
|
App::new().service(
|
||||||
web::scope("/app")
|
web::scope("/app")
|
||||||
.guard(guard::Get())
|
.guard(guard::Get())
|
||||||
|
@ -735,8 +735,7 @@ mod tests {
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_scope_variable_segment() {
|
async fn test_scope_variable_segment() {
|
||||||
let mut srv =
|
let srv = init_service(App::new().service(web::scope("/ab-{project}").service(
|
||||||
init_service(App::new().service(web::scope("/ab-{project}").service(
|
|
||||||
web::resource("/path1").to(|r: HttpRequest| {
|
web::resource("/path1").to(|r: HttpRequest| {
|
||||||
HttpResponse::Ok()
|
HttpResponse::Ok()
|
||||||
.body(format!("project: {}", &r.match_info()["project"]))
|
.body(format!("project: {}", &r.match_info()["project"]))
|
||||||
|
@ -763,7 +762,7 @@ mod tests {
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_nested_scope() {
|
async fn test_nested_scope() {
|
||||||
let mut srv = init_service(App::new().service(web::scope("/app").service(
|
let srv = init_service(App::new().service(web::scope("/app").service(
|
||||||
web::scope("/t1").service(web::resource("/path1").to(HttpResponse::Created)),
|
web::scope("/t1").service(web::resource("/path1").to(HttpResponse::Created)),
|
||||||
)))
|
)))
|
||||||
.await;
|
.await;
|
||||||
|
@ -775,7 +774,7 @@ mod tests {
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_nested_scope_no_slash() {
|
async fn test_nested_scope_no_slash() {
|
||||||
let mut srv = init_service(App::new().service(web::scope("/app").service(
|
let srv = init_service(App::new().service(web::scope("/app").service(
|
||||||
web::scope("t1").service(web::resource("/path1").to(HttpResponse::Created)),
|
web::scope("t1").service(web::resource("/path1").to(HttpResponse::Created)),
|
||||||
)))
|
)))
|
||||||
.await;
|
.await;
|
||||||
|
@ -787,7 +786,7 @@ mod tests {
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_nested_scope_root() {
|
async fn test_nested_scope_root() {
|
||||||
let mut srv = init_service(
|
let srv = init_service(
|
||||||
App::new().service(
|
App::new().service(
|
||||||
web::scope("/app").service(
|
web::scope("/app").service(
|
||||||
web::scope("/t1")
|
web::scope("/t1")
|
||||||
|
@ -809,7 +808,7 @@ mod tests {
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_nested_scope_filter() {
|
async fn test_nested_scope_filter() {
|
||||||
let mut srv = init_service(
|
let srv = init_service(
|
||||||
App::new().service(
|
App::new().service(
|
||||||
web::scope("/app").service(
|
web::scope("/app").service(
|
||||||
web::scope("/t1")
|
web::scope("/t1")
|
||||||
|
@ -835,7 +834,7 @@ mod tests {
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_nested_scope_with_variable_segment() {
|
async fn test_nested_scope_with_variable_segment() {
|
||||||
let mut srv = init_service(App::new().service(web::scope("/app").service(
|
let srv = init_service(App::new().service(web::scope("/app").service(
|
||||||
web::scope("/{project_id}").service(web::resource("/path1").to(
|
web::scope("/{project_id}").service(web::resource("/path1").to(
|
||||||
|r: HttpRequest| {
|
|r: HttpRequest| {
|
||||||
HttpResponse::Created()
|
HttpResponse::Created()
|
||||||
|
@ -860,7 +859,7 @@ mod tests {
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_nested2_scope_with_variable_segment() {
|
async fn test_nested2_scope_with_variable_segment() {
|
||||||
let mut srv = init_service(App::new().service(web::scope("/app").service(
|
let srv = init_service(App::new().service(web::scope("/app").service(
|
||||||
web::scope("/{project}").service(web::scope("/{id}").service(
|
web::scope("/{project}").service(web::scope("/{id}").service(
|
||||||
web::resource("/path1").to(|r: HttpRequest| {
|
web::resource("/path1").to(|r: HttpRequest| {
|
||||||
HttpResponse::Created().body(format!(
|
HttpResponse::Created().body(format!(
|
||||||
|
@ -892,7 +891,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 srv = init_service(
|
||||||
App::new().service(
|
App::new().service(
|
||||||
web::scope("/app")
|
web::scope("/app")
|
||||||
.service(web::resource("/path1").to(HttpResponse::Ok))
|
.service(web::resource("/path1").to(HttpResponse::Ok))
|
||||||
|
@ -914,7 +913,7 @@ mod tests {
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_default_resource_propagation() {
|
async fn test_default_resource_propagation() {
|
||||||
let mut srv = init_service(
|
let srv = init_service(
|
||||||
App::new()
|
App::new()
|
||||||
.service(
|
.service(
|
||||||
web::scope("/app1")
|
web::scope("/app1")
|
||||||
|
@ -942,7 +941,7 @@ mod tests {
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_middleware() {
|
async fn test_middleware() {
|
||||||
let mut srv = init_service(
|
let srv = init_service(
|
||||||
App::new().service(
|
App::new().service(
|
||||||
web::scope("app")
|
web::scope("app")
|
||||||
.wrap(
|
.wrap(
|
||||||
|
@ -959,7 +958,7 @@ mod tests {
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let req = TestRequest::with_uri("/app/test").to_request();
|
let req = TestRequest::with_uri("/app/test").to_request();
|
||||||
let resp = call_service(&mut srv, req).await;
|
let resp = call_service(&srv, req).await;
|
||||||
assert_eq!(resp.status(), StatusCode::OK);
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
resp.headers().get(header::CONTENT_TYPE).unwrap(),
|
resp.headers().get(header::CONTENT_TYPE).unwrap(),
|
||||||
|
@ -969,7 +968,7 @@ mod tests {
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_middleware_fn() {
|
async fn test_middleware_fn() {
|
||||||
let mut srv = init_service(
|
let srv = init_service(
|
||||||
App::new().service(
|
App::new().service(
|
||||||
web::scope("app")
|
web::scope("app")
|
||||||
.wrap_fn(|req, srv| {
|
.wrap_fn(|req, srv| {
|
||||||
|
@ -989,7 +988,7 @@ mod tests {
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let req = TestRequest::with_uri("/app/test").to_request();
|
let req = TestRequest::with_uri("/app/test").to_request();
|
||||||
let resp = call_service(&mut srv, req).await;
|
let resp = call_service(&srv, req).await;
|
||||||
assert_eq!(resp.status(), StatusCode::OK);
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
resp.headers().get(header::CONTENT_TYPE).unwrap(),
|
resp.headers().get(header::CONTENT_TYPE).unwrap(),
|
||||||
|
@ -999,7 +998,7 @@ mod tests {
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_override_data() {
|
async fn test_override_data() {
|
||||||
let mut srv = init_service(App::new().data(1usize).service(
|
let srv = init_service(App::new().data(1usize).service(
|
||||||
web::scope("app").data(10usize).route(
|
web::scope("app").data(10usize).route(
|
||||||
"/t",
|
"/t",
|
||||||
web::get().to(|data: web::Data<usize>| {
|
web::get().to(|data: web::Data<usize>| {
|
||||||
|
@ -1011,13 +1010,13 @@ mod tests {
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let req = TestRequest::with_uri("/app/t").to_request();
|
let req = TestRequest::with_uri("/app/t").to_request();
|
||||||
let resp = call_service(&mut srv, req).await;
|
let resp = call_service(&srv, req).await;
|
||||||
assert_eq!(resp.status(), StatusCode::OK);
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_override_data_default_service() {
|
async fn test_override_data_default_service() {
|
||||||
let mut srv = init_service(App::new().data(1usize).service(
|
let srv = init_service(App::new().data(1usize).service(
|
||||||
web::scope("app").data(10usize).default_service(web::to(
|
web::scope("app").data(10usize).default_service(web::to(
|
||||||
|data: web::Data<usize>| {
|
|data: web::Data<usize>| {
|
||||||
assert_eq!(**data, 10);
|
assert_eq!(**data, 10);
|
||||||
|
@ -1028,13 +1027,13 @@ mod tests {
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let req = TestRequest::with_uri("/app/t").to_request();
|
let req = TestRequest::with_uri("/app/t").to_request();
|
||||||
let resp = call_service(&mut srv, req).await;
|
let resp = call_service(&srv, req).await;
|
||||||
assert_eq!(resp.status(), StatusCode::OK);
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_override_app_data() {
|
async fn test_override_app_data() {
|
||||||
let mut srv = init_service(App::new().app_data(web::Data::new(1usize)).service(
|
let srv = init_service(App::new().app_data(web::Data::new(1usize)).service(
|
||||||
web::scope("app").app_data(web::Data::new(10usize)).route(
|
web::scope("app").app_data(web::Data::new(10usize)).route(
|
||||||
"/t",
|
"/t",
|
||||||
web::get().to(|data: web::Data<usize>| {
|
web::get().to(|data: web::Data<usize>| {
|
||||||
|
@ -1046,14 +1045,13 @@ mod tests {
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let req = TestRequest::with_uri("/app/t").to_request();
|
let req = TestRequest::with_uri("/app/t").to_request();
|
||||||
let resp = call_service(&mut srv, req).await;
|
let resp = call_service(&srv, req).await;
|
||||||
assert_eq!(resp.status(), StatusCode::OK);
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_scope_config() {
|
async fn test_scope_config() {
|
||||||
let mut srv =
|
let srv = init_service(App::new().service(web::scope("/app").configure(|s| {
|
||||||
init_service(App::new().service(web::scope("/app").configure(|s| {
|
|
||||||
s.route("/path1", web::get().to(HttpResponse::Ok));
|
s.route("/path1", web::get().to(HttpResponse::Ok));
|
||||||
})))
|
})))
|
||||||
.await;
|
.await;
|
||||||
|
@ -1065,8 +1063,7 @@ mod tests {
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_scope_config_2() {
|
async fn test_scope_config_2() {
|
||||||
let mut srv =
|
let srv = init_service(App::new().service(web::scope("/app").configure(|s| {
|
||||||
init_service(App::new().service(web::scope("/app").configure(|s| {
|
|
||||||
s.service(web::scope("/v1").configure(|s| {
|
s.service(web::scope("/v1").configure(|s| {
|
||||||
s.route("/", web::get().to(HttpResponse::Ok));
|
s.route("/", web::get().to(HttpResponse::Ok));
|
||||||
}));
|
}));
|
||||||
|
@ -1080,13 +1077,9 @@ mod tests {
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_url_for_external() {
|
async fn test_url_for_external() {
|
||||||
let mut srv =
|
let srv = init_service(App::new().service(web::scope("/app").configure(|s| {
|
||||||
init_service(App::new().service(web::scope("/app").configure(|s| {
|
|
||||||
s.service(web::scope("/v1").configure(|s| {
|
s.service(web::scope("/v1").configure(|s| {
|
||||||
s.external_resource(
|
s.external_resource("youtube", "https://youtube.com/watch/{video_id}");
|
||||||
"youtube",
|
|
||||||
"https://youtube.com/watch/{video_id}",
|
|
||||||
);
|
|
||||||
s.route(
|
s.route(
|
||||||
"/",
|
"/",
|
||||||
web::get().to(|req: HttpRequest| {
|
web::get().to(|req: HttpRequest| {
|
||||||
|
@ -1108,7 +1101,7 @@ mod tests {
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_url_for_nested() {
|
async fn test_url_for_nested() {
|
||||||
let mut srv = init_service(App::new().service(web::scope("/a").service(
|
let srv = init_service(App::new().service(web::scope("/a").service(
|
||||||
web::scope("/b").service(web::resource("/c/{stuff}").name("c").route(
|
web::scope("/b").service(web::resource("/c/{stuff}").name("c").route(
|
||||||
web::get().to(|req: HttpRequest| {
|
web::get().to(|req: HttpRequest| {
|
||||||
HttpResponse::Ok()
|
HttpResponse::Ok()
|
||||||
|
@ -1119,7 +1112,7 @@ mod tests {
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let req = TestRequest::with_uri("/a/b/c/test").to_request();
|
let req = TestRequest::with_uri("/a/b/c/test").to_request();
|
||||||
let resp = call_service(&mut srv, req).await;
|
let resp = call_service(&srv, req).await;
|
||||||
assert_eq!(resp.status(), StatusCode::OK);
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
let body = read_body(resp).await;
|
let body = read_body(resp).await;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|
|
@ -630,7 +630,7 @@ where
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn run(self) -> Server {
|
pub fn run(self) -> Server {
|
||||||
self.builder.start()
|
self.builder.run()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
148
src/service.rs
148
src/service.rs
|
@ -22,6 +22,13 @@ pub trait HttpServiceFactory {
|
||||||
fn register(self, config: &mut AppService);
|
fn register(self, config: &mut AppService);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: HttpServiceFactory> HttpServiceFactory for Vec<T> {
|
||||||
|
fn register(self, config: &mut AppService) {
|
||||||
|
self.into_iter()
|
||||||
|
.for_each(|factory| factory.register(config));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) trait AppServiceFactory {
|
pub(crate) trait AppServiceFactory {
|
||||||
fn register(&mut self, config: &mut AppService);
|
fn register(&mut self, config: &mut AppService);
|
||||||
}
|
}
|
||||||
|
@ -532,6 +539,65 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Macro helping register different types of services at the sametime.
|
||||||
|
///
|
||||||
|
/// The service type must be implementing [`HttpServiceFactory`](self::HttpServiceFactory) trait.
|
||||||
|
///
|
||||||
|
/// The max number of services can be grouped together is 12.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use actix_web::{services, web, App};
|
||||||
|
///
|
||||||
|
/// let services = services![
|
||||||
|
/// web::resource("/test2").to(|| async { "test2" }),
|
||||||
|
/// web::scope("/test3").route("/", web::get().to(|| async { "test3" }))
|
||||||
|
/// ];
|
||||||
|
///
|
||||||
|
/// let app = App::new().service(services);
|
||||||
|
///
|
||||||
|
/// // services macro just convert multiple services to a tuple.
|
||||||
|
/// // below would also work without importing the macro.
|
||||||
|
/// let app = App::new().service((
|
||||||
|
/// web::resource("/test2").to(|| async { "test2" }),
|
||||||
|
/// web::scope("/test3").route("/", web::get().to(|| async { "test3" }))
|
||||||
|
/// ));
|
||||||
|
/// ```
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! services {
|
||||||
|
($($x:expr),+ $(,)?) => {
|
||||||
|
($($x,)+)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// HttpServiceFactory trait impl for tuples
|
||||||
|
macro_rules! service_tuple ({ $(($n:tt, $T:ident)),+} => {
|
||||||
|
impl<$($T: HttpServiceFactory),+> HttpServiceFactory for ($($T,)+) {
|
||||||
|
fn register(self, config: &mut AppService) {
|
||||||
|
$(self.$n.register(config);)+
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
mod m {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
service_tuple!((0, A));
|
||||||
|
service_tuple!((0, A), (1, B));
|
||||||
|
service_tuple!((0, A), (1, B), (2, C));
|
||||||
|
service_tuple!((0, A), (1, B), (2, C), (3, D));
|
||||||
|
service_tuple!((0, A), (1, B), (2, C), (3, D), (4, E));
|
||||||
|
service_tuple!((0, A), (1, B), (2, C), (3, D), (4, E), (5, F));
|
||||||
|
service_tuple!((0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G));
|
||||||
|
service_tuple!((0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G), (7, H));
|
||||||
|
service_tuple!((0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G), (7, H), (8, I));
|
||||||
|
service_tuple!((0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G), (7, H), (8, I), (9, J));
|
||||||
|
service_tuple!((0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G), (7, H), (8, I), (9, J), (10, K));
|
||||||
|
service_tuple!((0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G), (7, H), (8, I), (9, J), (10, K), (11, L));
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -542,7 +608,7 @@ mod tests {
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_service() {
|
async fn test_service() {
|
||||||
let mut srv = init_service(
|
let srv = init_service(
|
||||||
App::new().service(web::service("/test").name("test").finish(
|
App::new().service(web::service("/test").name("test").finish(
|
||||||
|req: ServiceRequest| ok(req.into_response(HttpResponse::Ok().finish())),
|
|req: ServiceRequest| ok(req.into_response(HttpResponse::Ok().finish())),
|
||||||
)),
|
)),
|
||||||
|
@ -552,7 +618,7 @@ mod tests {
|
||||||
let resp = srv.call(req).await.unwrap();
|
let resp = srv.call(req).await.unwrap();
|
||||||
assert_eq!(resp.status(), http::StatusCode::OK);
|
assert_eq!(resp.status(), http::StatusCode::OK);
|
||||||
|
|
||||||
let mut srv = init_service(
|
let srv = init_service(
|
||||||
App::new().service(web::service("/test").guard(guard::Get()).finish(
|
App::new().service(web::service("/test").guard(guard::Get()).finish(
|
||||||
|req: ServiceRequest| ok(req.into_response(HttpResponse::Ok().finish())),
|
|req: ServiceRequest| ok(req.into_response(HttpResponse::Ok().finish())),
|
||||||
)),
|
)),
|
||||||
|
@ -567,7 +633,7 @@ mod tests {
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_service_data() {
|
async fn test_service_data() {
|
||||||
let mut srv = init_service(
|
let srv = init_service(
|
||||||
App::new()
|
App::new()
|
||||||
.data(42u32)
|
.data(42u32)
|
||||||
.service(web::service("/test").name("test").finish(
|
.service(web::service("/test").name("test").finish(
|
||||||
|
@ -606,4 +672,80 @@ mod tests {
|
||||||
assert!(s.contains("ServiceResponse"));
|
assert!(s.contains("ServiceResponse"));
|
||||||
assert!(s.contains("x-test"));
|
assert!(s.contains("x-test"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn test_services_macro() {
|
||||||
|
let scoped = services![
|
||||||
|
web::service("/scoped_test1").name("scoped_test1").finish(
|
||||||
|
|req: ServiceRequest| async {
|
||||||
|
Ok(req.into_response(HttpResponse::Ok().finish()))
|
||||||
|
}
|
||||||
|
),
|
||||||
|
web::resource("/scoped_test2").to(|| async { "test2" }),
|
||||||
|
];
|
||||||
|
|
||||||
|
let services = services![
|
||||||
|
web::service("/test1")
|
||||||
|
.name("test")
|
||||||
|
.finish(|req: ServiceRequest| async {
|
||||||
|
Ok(req.into_response(HttpResponse::Ok().finish()))
|
||||||
|
}),
|
||||||
|
web::resource("/test2").to(|| async { "test2" }),
|
||||||
|
web::scope("/test3").service(scoped)
|
||||||
|
];
|
||||||
|
|
||||||
|
let srv = init_service(App::new().service(services)).await;
|
||||||
|
|
||||||
|
let req = TestRequest::with_uri("/test1").to_request();
|
||||||
|
let resp = srv.call(req).await.unwrap();
|
||||||
|
assert_eq!(resp.status(), http::StatusCode::OK);
|
||||||
|
|
||||||
|
let req = TestRequest::with_uri("/test2").to_request();
|
||||||
|
let resp = srv.call(req).await.unwrap();
|
||||||
|
assert_eq!(resp.status(), http::StatusCode::OK);
|
||||||
|
|
||||||
|
let req = TestRequest::with_uri("/test3/scoped_test1").to_request();
|
||||||
|
let resp = srv.call(req).await.unwrap();
|
||||||
|
assert_eq!(resp.status(), http::StatusCode::OK);
|
||||||
|
|
||||||
|
let req = TestRequest::with_uri("/test3/scoped_test2").to_request();
|
||||||
|
let resp = srv.call(req).await.unwrap();
|
||||||
|
assert_eq!(resp.status(), http::StatusCode::OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn test_services_vec() {
|
||||||
|
let services = vec![
|
||||||
|
web::resource("/test1").to(|| async { "test1" }),
|
||||||
|
web::resource("/test2").to(|| async { "test2" }),
|
||||||
|
];
|
||||||
|
|
||||||
|
let scoped = vec![
|
||||||
|
web::resource("/scoped_test1").to(|| async { "test1" }),
|
||||||
|
web::resource("/scoped_test2").to(|| async { "test2" }),
|
||||||
|
];
|
||||||
|
|
||||||
|
let srv = init_service(
|
||||||
|
App::new()
|
||||||
|
.service(services)
|
||||||
|
.service(web::scope("/test3").service(scoped)),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let req = TestRequest::with_uri("/test1").to_request();
|
||||||
|
let resp = srv.call(req).await.unwrap();
|
||||||
|
assert_eq!(resp.status(), http::StatusCode::OK);
|
||||||
|
|
||||||
|
let req = TestRequest::with_uri("/test2").to_request();
|
||||||
|
let resp = srv.call(req).await.unwrap();
|
||||||
|
assert_eq!(resp.status(), http::StatusCode::OK);
|
||||||
|
|
||||||
|
let req = TestRequest::with_uri("/test3/scoped_test1").to_request();
|
||||||
|
let resp = srv.call(req).await.unwrap();
|
||||||
|
assert_eq!(resp.status(), http::StatusCode::OK);
|
||||||
|
|
||||||
|
let req = TestRequest::with_uri("/test3/scoped_test2").to_request();
|
||||||
|
let resp = srv.call(req).await.unwrap();
|
||||||
|
assert_eq!(resp.status(), http::StatusCode::OK);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
84
src/test.rs
84
src/test.rs
|
@ -60,7 +60,7 @@ pub fn default_service(
|
||||||
///
|
///
|
||||||
/// #[actix_rt::test]
|
/// #[actix_rt::test]
|
||||||
/// async fn test_init_service() {
|
/// async fn test_init_service() {
|
||||||
/// let mut app = test::init_service(
|
/// let app = test::init_service(
|
||||||
/// App::new()
|
/// App::new()
|
||||||
/// .service(web::resource("/test").to(|| async { HttpResponse::Ok() }))
|
/// .service(web::resource("/test").to(|| async { HttpResponse::Ok() }))
|
||||||
/// ).await;
|
/// ).await;
|
||||||
|
@ -116,7 +116,7 @@ where
|
||||||
///
|
///
|
||||||
/// #[actix_rt::test]
|
/// #[actix_rt::test]
|
||||||
/// async fn test_response() {
|
/// async fn test_response() {
|
||||||
/// let mut app = test::init_service(
|
/// let app = test::init_service(
|
||||||
/// App::new()
|
/// App::new()
|
||||||
/// .service(web::resource("/test").to(|| async {
|
/// .service(web::resource("/test").to(|| async {
|
||||||
/// HttpResponse::Ok()
|
/// HttpResponse::Ok()
|
||||||
|
@ -127,11 +127,11 @@ where
|
||||||
/// let req = test::TestRequest::with_uri("/test").to_request();
|
/// let req = test::TestRequest::with_uri("/test").to_request();
|
||||||
///
|
///
|
||||||
/// // Call application
|
/// // Call application
|
||||||
/// let resp = test::call_service(&mut app, req).await;
|
/// let resp = test::call_service(&app, req).await;
|
||||||
/// assert_eq!(resp.status(), StatusCode::OK);
|
/// assert_eq!(resp.status(), StatusCode::OK);
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub async fn call_service<S, R, B, E>(app: &mut S, req: R) -> S::Response
|
pub async fn call_service<S, R, B, E>(app: &S, req: R) -> S::Response
|
||||||
where
|
where
|
||||||
S: Service<R, Response = ServiceResponse<B>, Error = E>,
|
S: Service<R, Response = ServiceResponse<B>, Error = E>,
|
||||||
E: std::fmt::Debug,
|
E: std::fmt::Debug,
|
||||||
|
@ -147,7 +147,7 @@ where
|
||||||
///
|
///
|
||||||
/// #[actix_rt::test]
|
/// #[actix_rt::test]
|
||||||
/// async fn test_index() {
|
/// async fn test_index() {
|
||||||
/// let mut app = test::init_service(
|
/// let app = test::init_service(
|
||||||
/// App::new().service(
|
/// App::new().service(
|
||||||
/// web::resource("/index.html")
|
/// web::resource("/index.html")
|
||||||
/// .route(web::post().to(|| async {
|
/// .route(web::post().to(|| async {
|
||||||
|
@ -160,11 +160,11 @@ where
|
||||||
/// .header(header::CONTENT_TYPE, "application/json")
|
/// .header(header::CONTENT_TYPE, "application/json")
|
||||||
/// .to_request();
|
/// .to_request();
|
||||||
///
|
///
|
||||||
/// let result = test::read_response(&mut app, req).await;
|
/// let result = test::read_response(&app, req).await;
|
||||||
/// assert_eq!(result, Bytes::from_static(b"welcome!"));
|
/// assert_eq!(result, Bytes::from_static(b"welcome!"));
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub async fn read_response<S, B>(app: &mut S, req: Request) -> Bytes
|
pub async fn read_response<S, B>(app: &S, req: Request) -> Bytes
|
||||||
where
|
where
|
||||||
S: Service<Request, Response = ServiceResponse<B>, Error = Error>,
|
S: Service<Request, Response = ServiceResponse<B>, Error = Error>,
|
||||||
B: MessageBody + Unpin,
|
B: MessageBody + Unpin,
|
||||||
|
@ -190,7 +190,7 @@ where
|
||||||
///
|
///
|
||||||
/// #[actix_rt::test]
|
/// #[actix_rt::test]
|
||||||
/// async fn test_index() {
|
/// async fn test_index() {
|
||||||
/// let mut app = test::init_service(
|
/// let app = test::init_service(
|
||||||
/// App::new().service(
|
/// App::new().service(
|
||||||
/// web::resource("/index.html")
|
/// web::resource("/index.html")
|
||||||
/// .route(web::post().to(|| async {
|
/// .route(web::post().to(|| async {
|
||||||
|
@ -203,7 +203,7 @@ where
|
||||||
/// .header(header::CONTENT_TYPE, "application/json")
|
/// .header(header::CONTENT_TYPE, "application/json")
|
||||||
/// .to_request();
|
/// .to_request();
|
||||||
///
|
///
|
||||||
/// let resp = test::call_service(&mut app, req).await;
|
/// let resp = test::call_service(&app, req).await;
|
||||||
/// let result = test::read_body(resp).await;
|
/// let result = test::read_body(resp).await;
|
||||||
/// assert_eq!(result, Bytes::from_static(b"welcome!"));
|
/// assert_eq!(result, Bytes::from_static(b"welcome!"));
|
||||||
/// }
|
/// }
|
||||||
|
@ -234,7 +234,7 @@ where
|
||||||
///
|
///
|
||||||
/// #[actix_rt::test]
|
/// #[actix_rt::test]
|
||||||
/// async fn test_post_person() {
|
/// async fn test_post_person() {
|
||||||
/// let mut app = test::init_service(
|
/// let app = test::init_service(
|
||||||
/// App::new().service(
|
/// App::new().service(
|
||||||
/// web::resource("/people")
|
/// web::resource("/people")
|
||||||
/// .route(web::post().to(|person: web::Json<Person>| async {
|
/// .route(web::post().to(|person: web::Json<Person>| async {
|
||||||
|
@ -294,7 +294,7 @@ where
|
||||||
///
|
///
|
||||||
/// #[actix_rt::test]
|
/// #[actix_rt::test]
|
||||||
/// async fn test_add_person() {
|
/// async fn test_add_person() {
|
||||||
/// let mut app = test::init_service(
|
/// let app = test::init_service(
|
||||||
/// App::new().service(
|
/// App::new().service(
|
||||||
/// web::resource("/people")
|
/// web::resource("/people")
|
||||||
/// .route(web::post().to(|person: web::Json<Person>| async {
|
/// .route(web::post().to(|person: web::Json<Person>| async {
|
||||||
|
@ -314,7 +314,7 @@ where
|
||||||
/// let result: Person = test::read_response_json(&mut app, req).await;
|
/// let result: Person = test::read_response_json(&mut app, req).await;
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub async fn read_response_json<S, B, T>(app: &mut S, req: Request) -> T
|
pub async fn read_response_json<S, B, T>(app: &S, req: Request) -> T
|
||||||
where
|
where
|
||||||
S: Service<Request, Response = ServiceResponse<B>, Error = Error>,
|
S: Service<Request, Response = ServiceResponse<B>, Error = Error>,
|
||||||
B: MessageBody + Unpin,
|
B: MessageBody + Unpin,
|
||||||
|
@ -569,7 +569,7 @@ impl TestRequest {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Complete request creation, calls service and waits for response future completion.
|
/// Complete request creation, calls service and waits for response future completion.
|
||||||
pub async fn send_request<S, B, E>(self, app: &mut S) -> S::Response
|
pub async fn send_request<S, B, E>(self, app: &S) -> S::Response
|
||||||
where
|
where
|
||||||
S: Service<Request, Response = ServiceResponse<B>, Error = E>,
|
S: Service<Request, Response = ServiceResponse<B>, Error = E>,
|
||||||
E: std::fmt::Debug,
|
E: std::fmt::Debug,
|
||||||
|
@ -595,7 +595,7 @@ impl TestRequest {
|
||||||
///
|
///
|
||||||
/// #[actix_rt::test]
|
/// #[actix_rt::test]
|
||||||
/// async fn test_example() {
|
/// async fn test_example() {
|
||||||
/// let mut srv = test::start(
|
/// let srv = test::start(
|
||||||
/// || App::new().service(
|
/// || App::new().service(
|
||||||
/// web::resource("/").to(my_handler))
|
/// web::resource("/").to(my_handler))
|
||||||
/// );
|
/// );
|
||||||
|
@ -635,7 +635,7 @@ where
|
||||||
///
|
///
|
||||||
/// #[actix_rt::test]
|
/// #[actix_rt::test]
|
||||||
/// async fn test_example() {
|
/// async fn test_example() {
|
||||||
/// let mut srv = test::start_with(test::config().h1(), ||
|
/// let srv = test::start_with(test::config().h1(), ||
|
||||||
/// App::new().service(web::resource("/").to(my_handler))
|
/// App::new().service(web::resource("/").to(my_handler))
|
||||||
/// );
|
/// );
|
||||||
///
|
///
|
||||||
|
@ -667,7 +667,7 @@ where
|
||||||
|
|
||||||
// run server in separate thread
|
// run server in separate thread
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
let sys = System::new("actix-test-server");
|
let sys = System::new();
|
||||||
let tcp = net::TcpListener::bind("127.0.0.1:0").unwrap();
|
let tcp = net::TcpListener::bind("127.0.0.1:0").unwrap();
|
||||||
let local_addr = tcp.local_addr().unwrap();
|
let local_addr = tcp.local_addr().unwrap();
|
||||||
let factory = factory.clone();
|
let factory = factory.clone();
|
||||||
|
@ -760,7 +760,7 @@ where
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
sys.block_on(async {
|
sys.block_on(async {
|
||||||
let srv = srv.start();
|
let srv = srv.run();
|
||||||
tx.send((System::current(), srv, local_addr)).unwrap();
|
tx.send((System::current(), srv, local_addr)).unwrap();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -773,7 +773,7 @@ where
|
||||||
let connector = {
|
let connector = {
|
||||||
#[cfg(feature = "openssl")]
|
#[cfg(feature = "openssl")]
|
||||||
{
|
{
|
||||||
use open_ssl::ssl::{SslConnector, SslMethod, SslVerifyMode};
|
use openssl::ssl::{SslConnector, SslMethod, SslVerifyMode};
|
||||||
|
|
||||||
let mut builder = SslConnector::builder(SslMethod::tls()).unwrap();
|
let mut builder = SslConnector::builder(SslMethod::tls()).unwrap();
|
||||||
builder.set_verify(SslVerifyMode::NONE);
|
builder.set_verify(SslVerifyMode::NONE);
|
||||||
|
@ -825,9 +825,9 @@ enum HttpVer {
|
||||||
enum StreamType {
|
enum StreamType {
|
||||||
Tcp,
|
Tcp,
|
||||||
#[cfg(feature = "openssl")]
|
#[cfg(feature = "openssl")]
|
||||||
Openssl(open_ssl::ssl::SslAcceptor),
|
Openssl(openssl::ssl::SslAcceptor),
|
||||||
#[cfg(feature = "rustls")]
|
#[cfg(feature = "rustls")]
|
||||||
Rustls(rust_tls::ServerConfig),
|
Rustls(rustls::ServerConfig),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for TestServerConfig {
|
impl Default for TestServerConfig {
|
||||||
|
@ -865,14 +865,14 @@ impl TestServerConfig {
|
||||||
|
|
||||||
/// Start openssl server
|
/// Start openssl server
|
||||||
#[cfg(feature = "openssl")]
|
#[cfg(feature = "openssl")]
|
||||||
pub fn openssl(mut self, acceptor: open_ssl::ssl::SslAcceptor) -> Self {
|
pub fn openssl(mut self, acceptor: openssl::ssl::SslAcceptor) -> Self {
|
||||||
self.stream = StreamType::Openssl(acceptor);
|
self.stream = StreamType::Openssl(acceptor);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Start rustls server
|
/// Start rustls server
|
||||||
#[cfg(feature = "rustls")]
|
#[cfg(feature = "rustls")]
|
||||||
pub fn rustls(mut self, config: rust_tls::ServerConfig) -> Self {
|
pub fn rustls(mut self, config: rustls::ServerConfig) -> Self {
|
||||||
self.stream = StreamType::Rustls(config);
|
self.stream = StreamType::Rustls(config);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
@ -1043,7 +1043,7 @@ mod tests {
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_request_methods() {
|
async fn test_request_methods() {
|
||||||
let mut app = init_service(
|
let app = init_service(
|
||||||
App::new().service(
|
App::new().service(
|
||||||
web::resource("/index.html")
|
web::resource("/index.html")
|
||||||
.route(web::put().to(|| HttpResponse::Ok().body("put!")))
|
.route(web::put().to(|| HttpResponse::Ok().body("put!")))
|
||||||
|
@ -1058,7 +1058,7 @@ mod tests {
|
||||||
.insert_header((header::CONTENT_TYPE, "application/json"))
|
.insert_header((header::CONTENT_TYPE, "application/json"))
|
||||||
.to_request();
|
.to_request();
|
||||||
|
|
||||||
let result = read_response(&mut app, put_req).await;
|
let result = read_response(&app, put_req).await;
|
||||||
assert_eq!(result, Bytes::from_static(b"put!"));
|
assert_eq!(result, Bytes::from_static(b"put!"));
|
||||||
|
|
||||||
let patch_req = TestRequest::patch()
|
let patch_req = TestRequest::patch()
|
||||||
|
@ -1066,17 +1066,17 @@ mod tests {
|
||||||
.insert_header((header::CONTENT_TYPE, "application/json"))
|
.insert_header((header::CONTENT_TYPE, "application/json"))
|
||||||
.to_request();
|
.to_request();
|
||||||
|
|
||||||
let result = read_response(&mut app, patch_req).await;
|
let result = read_response(&app, patch_req).await;
|
||||||
assert_eq!(result, Bytes::from_static(b"patch!"));
|
assert_eq!(result, Bytes::from_static(b"patch!"));
|
||||||
|
|
||||||
let delete_req = TestRequest::delete().uri("/index.html").to_request();
|
let delete_req = TestRequest::delete().uri("/index.html").to_request();
|
||||||
let result = read_response(&mut app, delete_req).await;
|
let result = read_response(&app, delete_req).await;
|
||||||
assert_eq!(result, Bytes::from_static(b"delete!"));
|
assert_eq!(result, Bytes::from_static(b"delete!"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_response() {
|
async fn test_response() {
|
||||||
let mut app = init_service(
|
let app = init_service(
|
||||||
App::new().service(
|
App::new().service(
|
||||||
web::resource("/index.html")
|
web::resource("/index.html")
|
||||||
.route(web::post().to(|| HttpResponse::Ok().body("welcome!"))),
|
.route(web::post().to(|| HttpResponse::Ok().body("welcome!"))),
|
||||||
|
@ -1089,13 +1089,13 @@ mod tests {
|
||||||
.insert_header((header::CONTENT_TYPE, "application/json"))
|
.insert_header((header::CONTENT_TYPE, "application/json"))
|
||||||
.to_request();
|
.to_request();
|
||||||
|
|
||||||
let result = read_response(&mut app, req).await;
|
let result = read_response(&app, req).await;
|
||||||
assert_eq!(result, Bytes::from_static(b"welcome!"));
|
assert_eq!(result, Bytes::from_static(b"welcome!"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_send_request() {
|
async fn test_send_request() {
|
||||||
let mut app = init_service(
|
let app = init_service(
|
||||||
App::new().service(
|
App::new().service(
|
||||||
web::resource("/index.html")
|
web::resource("/index.html")
|
||||||
.route(web::get().to(|| HttpResponse::Ok().body("welcome!"))),
|
.route(web::get().to(|| HttpResponse::Ok().body("welcome!"))),
|
||||||
|
@ -1105,7 +1105,7 @@ mod tests {
|
||||||
|
|
||||||
let resp = TestRequest::get()
|
let resp = TestRequest::get()
|
||||||
.uri("/index.html")
|
.uri("/index.html")
|
||||||
.send_request(&mut app)
|
.send_request(&app)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let result = read_body(resp).await;
|
let result = read_body(resp).await;
|
||||||
|
@ -1120,7 +1120,7 @@ mod tests {
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_response_json() {
|
async fn test_response_json() {
|
||||||
let mut app = init_service(App::new().service(web::resource("/people").route(
|
let app = init_service(App::new().service(web::resource("/people").route(
|
||||||
web::post().to(|person: web::Json<Person>| HttpResponse::Ok().json(person)),
|
web::post().to(|person: web::Json<Person>| HttpResponse::Ok().json(person)),
|
||||||
)))
|
)))
|
||||||
.await;
|
.await;
|
||||||
|
@ -1133,13 +1133,13 @@ mod tests {
|
||||||
.set_payload(payload)
|
.set_payload(payload)
|
||||||
.to_request();
|
.to_request();
|
||||||
|
|
||||||
let result: Person = read_response_json(&mut app, req).await;
|
let result: Person = read_response_json(&app, req).await;
|
||||||
assert_eq!(&result.id, "12345");
|
assert_eq!(&result.id, "12345");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_body_json() {
|
async fn test_body_json() {
|
||||||
let mut app = init_service(App::new().service(web::resource("/people").route(
|
let app = init_service(App::new().service(web::resource("/people").route(
|
||||||
web::post().to(|person: web::Json<Person>| HttpResponse::Ok().json(person)),
|
web::post().to(|person: web::Json<Person>| HttpResponse::Ok().json(person)),
|
||||||
)))
|
)))
|
||||||
.await;
|
.await;
|
||||||
|
@ -1150,7 +1150,7 @@ mod tests {
|
||||||
.uri("/people")
|
.uri("/people")
|
||||||
.insert_header((header::CONTENT_TYPE, "application/json"))
|
.insert_header((header::CONTENT_TYPE, "application/json"))
|
||||||
.set_payload(payload)
|
.set_payload(payload)
|
||||||
.send_request(&mut app)
|
.send_request(&app)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let result: Person = read_body_json(resp).await;
|
let result: Person = read_body_json(resp).await;
|
||||||
|
@ -1159,7 +1159,7 @@ mod tests {
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_request_response_form() {
|
async fn test_request_response_form() {
|
||||||
let mut app = init_service(App::new().service(web::resource("/people").route(
|
let app = init_service(App::new().service(web::resource("/people").route(
|
||||||
web::post().to(|person: web::Form<Person>| HttpResponse::Ok().json(person)),
|
web::post().to(|person: web::Form<Person>| HttpResponse::Ok().json(person)),
|
||||||
)))
|
)))
|
||||||
.await;
|
.await;
|
||||||
|
@ -1176,14 +1176,14 @@ mod tests {
|
||||||
|
|
||||||
assert_eq!(req.content_type(), "application/x-www-form-urlencoded");
|
assert_eq!(req.content_type(), "application/x-www-form-urlencoded");
|
||||||
|
|
||||||
let result: Person = read_response_json(&mut app, req).await;
|
let result: Person = read_response_json(&app, req).await;
|
||||||
assert_eq!(&result.id, "12345");
|
assert_eq!(&result.id, "12345");
|
||||||
assert_eq!(&result.name, "User name");
|
assert_eq!(&result.name, "User name");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_request_response_json() {
|
async fn test_request_response_json() {
|
||||||
let mut app = init_service(App::new().service(web::resource("/people").route(
|
let app = init_service(App::new().service(web::resource("/people").route(
|
||||||
web::post().to(|person: web::Json<Person>| HttpResponse::Ok().json(person)),
|
web::post().to(|person: web::Json<Person>| HttpResponse::Ok().json(person)),
|
||||||
)))
|
)))
|
||||||
.await;
|
.await;
|
||||||
|
@ -1200,7 +1200,7 @@ mod tests {
|
||||||
|
|
||||||
assert_eq!(req.content_type(), "application/json");
|
assert_eq!(req.content_type(), "application/json");
|
||||||
|
|
||||||
let result: Person = read_response_json(&mut app, req).await;
|
let result: Person = read_response_json(&app, req).await;
|
||||||
assert_eq!(&result.id, "12345");
|
assert_eq!(&result.id, "12345");
|
||||||
assert_eq!(&result.name, "User name");
|
assert_eq!(&result.name, "User name");
|
||||||
}
|
}
|
||||||
|
@ -1213,12 +1213,12 @@ mod tests {
|
||||||
match res {
|
match res {
|
||||||
Ok(value) => Ok(HttpResponse::Ok()
|
Ok(value) => Ok(HttpResponse::Ok()
|
||||||
.content_type("text/plain")
|
.content_type("text/plain")
|
||||||
.body(format!("Async with block value: {}", value))),
|
.body(format!("Async with block value: {:?}", value))),
|
||||||
Err(_) => panic!("Unexpected"),
|
Err(_) => panic!("Unexpected"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut app = init_service(
|
let app = init_service(
|
||||||
App::new().service(web::resource("/index.html").to(async_with_block)),
|
App::new().service(web::resource("/index.html").to(async_with_block)),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
@ -1235,7 +1235,7 @@ mod tests {
|
||||||
HttpResponse::Ok()
|
HttpResponse::Ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut app = init_service(
|
let app = init_service(
|
||||||
App::new()
|
App::new()
|
||||||
.data(10usize)
|
.data(10usize)
|
||||||
.service(web::resource("/index.html").to(handler)),
|
.service(web::resource("/index.html").to(handler)),
|
||||||
|
@ -1291,7 +1291,7 @@ mod tests {
|
||||||
.data(addr.clone())
|
.data(addr.clone())
|
||||||
.service(web::resource("/").to(actor_handler));
|
.service(web::resource("/").to(actor_handler));
|
||||||
|
|
||||||
let mut app = init_service(srv).await;
|
let app = init_service(srv).await;
|
||||||
|
|
||||||
let req = TestRequest::post().uri("/").to_request();
|
let req = TestRequest::post().uri("/").to_request();
|
||||||
let res = app.call(req).await.unwrap();
|
let res = app.call(req).await.unwrap();
|
||||||
|
|
|
@ -289,10 +289,12 @@ impl HttpMessageBody {
|
||||||
if let Some(l) = req.headers().get(&header::CONTENT_LENGTH) {
|
if let Some(l) = req.headers().get(&header::CONTENT_LENGTH) {
|
||||||
match l.to_str() {
|
match l.to_str() {
|
||||||
Ok(s) => match s.parse::<usize>() {
|
Ok(s) => match s.parse::<usize>() {
|
||||||
Ok(l) if l > DEFAULT_CONFIG_LIMIT => {
|
Ok(l) => {
|
||||||
err = Some(PayloadError::Overflow)
|
if l > DEFAULT_CONFIG_LIMIT {
|
||||||
|
err = Some(PayloadError::Overflow);
|
||||||
|
}
|
||||||
|
length = Some(l)
|
||||||
}
|
}
|
||||||
Ok(l) => length = Some(l),
|
|
||||||
Err(_) => err = Some(PayloadError::UnknownLength),
|
Err(_) => err = Some(PayloadError::UnknownLength),
|
||||||
},
|
},
|
||||||
Err(_) => err = Some(PayloadError::UnknownLength),
|
Err(_) => err = Some(PayloadError::UnknownLength),
|
||||||
|
@ -316,9 +318,11 @@ impl HttpMessageBody {
|
||||||
/// Change max size of payload. By default max size is 256kB
|
/// Change max size of payload. By default max size is 256kB
|
||||||
pub fn limit(mut self, limit: usize) -> Self {
|
pub fn limit(mut self, limit: usize) -> Self {
|
||||||
if let Some(l) = self.length {
|
if let Some(l) = self.length {
|
||||||
if l > limit {
|
self.err = if l > limit {
|
||||||
self.err = Some(PayloadError::Overflow);
|
Some(PayloadError::Overflow)
|
||||||
}
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
}
|
}
|
||||||
self.limit = limit;
|
self.limit = limit;
|
||||||
self
|
self
|
||||||
|
@ -388,7 +392,7 @@ mod tests {
|
||||||
"payload is probably json string"
|
"payload is probably json string"
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut srv = init_service(
|
let srv = init_service(
|
||||||
App::new()
|
App::new()
|
||||||
.service(
|
.service(
|
||||||
web::resource("/bytes-app-data")
|
web::resource("/bytes-app-data")
|
||||||
|
@ -418,43 +422,43 @@ mod tests {
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let req = TestRequest::with_uri("/bytes-app-data").to_request();
|
let req = TestRequest::with_uri("/bytes-app-data").to_request();
|
||||||
let resp = call_service(&mut srv, req).await;
|
let resp = call_service(&srv, req).await;
|
||||||
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
|
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
|
||||||
|
|
||||||
let req = TestRequest::with_uri("/bytes-data").to_request();
|
let req = TestRequest::with_uri("/bytes-data").to_request();
|
||||||
let resp = call_service(&mut srv, req).await;
|
let resp = call_service(&srv, req).await;
|
||||||
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
|
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
|
||||||
|
|
||||||
let req = TestRequest::with_uri("/string-app-data").to_request();
|
let req = TestRequest::with_uri("/string-app-data").to_request();
|
||||||
let resp = call_service(&mut srv, req).await;
|
let resp = call_service(&srv, req).await;
|
||||||
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
|
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
|
||||||
|
|
||||||
let req = TestRequest::with_uri("/string-data").to_request();
|
let req = TestRequest::with_uri("/string-data").to_request();
|
||||||
let resp = call_service(&mut srv, req).await;
|
let resp = call_service(&srv, req).await;
|
||||||
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
|
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
|
||||||
|
|
||||||
let req = TestRequest::with_uri("/bytes-app-data")
|
let req = TestRequest::with_uri("/bytes-app-data")
|
||||||
.insert_header(header::ContentType(mime::APPLICATION_JSON))
|
.insert_header(header::ContentType(mime::APPLICATION_JSON))
|
||||||
.to_request();
|
.to_request();
|
||||||
let resp = call_service(&mut srv, req).await;
|
let resp = call_service(&srv, req).await;
|
||||||
assert_eq!(resp.status(), StatusCode::OK);
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
|
|
||||||
let req = TestRequest::with_uri("/bytes-data")
|
let req = TestRequest::with_uri("/bytes-data")
|
||||||
.insert_header(header::ContentType(mime::APPLICATION_JSON))
|
.insert_header(header::ContentType(mime::APPLICATION_JSON))
|
||||||
.to_request();
|
.to_request();
|
||||||
let resp = call_service(&mut srv, req).await;
|
let resp = call_service(&srv, req).await;
|
||||||
assert_eq!(resp.status(), StatusCode::OK);
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
|
|
||||||
let req = TestRequest::with_uri("/string-app-data")
|
let req = TestRequest::with_uri("/string-app-data")
|
||||||
.insert_header(header::ContentType(mime::APPLICATION_JSON))
|
.insert_header(header::ContentType(mime::APPLICATION_JSON))
|
||||||
.to_request();
|
.to_request();
|
||||||
let resp = call_service(&mut srv, req).await;
|
let resp = call_service(&srv, req).await;
|
||||||
assert_eq!(resp.status(), StatusCode::OK);
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
|
|
||||||
let req = TestRequest::with_uri("/string-data")
|
let req = TestRequest::with_uri("/string-data")
|
||||||
.insert_header(header::ContentType(mime::APPLICATION_JSON))
|
.insert_header(header::ContentType(mime::APPLICATION_JSON))
|
||||||
.to_request();
|
.to_request();
|
||||||
let resp = call_service(&mut srv, req).await;
|
let resp = call_service(&srv, req).await;
|
||||||
assert_eq!(resp.status(), StatusCode::OK);
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
13
src/web.rs
13
src/web.rs
|
@ -274,14 +274,11 @@ pub fn service<T: IntoPattern>(path: T) -> WebService {
|
||||||
|
|
||||||
/// Execute blocking function on a thread pool, returns future that resolves
|
/// Execute blocking function on a thread pool, returns future that resolves
|
||||||
/// to result of the function execution.
|
/// to result of the function execution.
|
||||||
pub async fn block<F, I, E>(f: F) -> Result<I, BlockingError<E>>
|
pub fn block<F, R>(f: F) -> impl Future<Output = Result<R, BlockingError>>
|
||||||
where
|
where
|
||||||
F: FnOnce() -> Result<I, E> + Send + 'static,
|
F: FnOnce() -> R + Send + 'static,
|
||||||
I: Send + 'static,
|
R: Send + 'static,
|
||||||
E: Send + std::fmt::Debug + 'static,
|
|
||||||
{
|
{
|
||||||
match actix_rt::task::spawn_blocking(f).await {
|
let fut = actix_rt::task::spawn_blocking(f);
|
||||||
Ok(res) => res.map_err(BlockingError::Error),
|
async { fut.await.map_err(|_| BlockingError) }
|
||||||
Err(_) => Err(BlockingError::Canceled),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,12 @@ use std::sync::mpsc;
|
||||||
use std::{thread, time::Duration};
|
use std::{thread, time::Duration};
|
||||||
|
|
||||||
#[cfg(feature = "openssl")]
|
#[cfg(feature = "openssl")]
|
||||||
use open_ssl::ssl::SslAcceptorBuilder;
|
extern crate tls_openssl as openssl;
|
||||||
|
#[cfg(feature = "rustls")]
|
||||||
|
extern crate tls_rustls as rustls;
|
||||||
|
|
||||||
|
#[cfg(feature = "openssl")]
|
||||||
|
use openssl::ssl::SslAcceptorBuilder;
|
||||||
|
|
||||||
use actix_web::{test, web, App, HttpResponse, HttpServer};
|
use actix_web::{test, web, App, HttpResponse, HttpServer};
|
||||||
|
|
||||||
|
@ -13,7 +18,7 @@ async fn test_start() {
|
||||||
let (tx, rx) = mpsc::channel();
|
let (tx, rx) = mpsc::channel();
|
||||||
|
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
let sys = actix_rt::System::new("test");
|
let sys = actix_rt::System::new();
|
||||||
|
|
||||||
sys.block_on(async {
|
sys.block_on(async {
|
||||||
let srv = HttpServer::new(|| {
|
let srv = HttpServer::new(|| {
|
||||||
|
@ -67,10 +72,9 @@ async fn test_start() {
|
||||||
let _ = sys.stop();
|
let _ = sys.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::unnecessary_wraps)]
|
|
||||||
#[cfg(feature = "openssl")]
|
#[cfg(feature = "openssl")]
|
||||||
fn ssl_acceptor() -> std::io::Result<SslAcceptorBuilder> {
|
fn ssl_acceptor() -> std::io::Result<SslAcceptorBuilder> {
|
||||||
use open_ssl::ssl::{SslAcceptor, SslFiletype, SslMethod};
|
use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod};
|
||||||
// load ssl keys
|
// load ssl keys
|
||||||
let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap();
|
let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap();
|
||||||
builder
|
builder
|
||||||
|
@ -91,7 +95,7 @@ async fn test_start_ssl() {
|
||||||
let (tx, rx) = mpsc::channel();
|
let (tx, rx) = mpsc::channel();
|
||||||
|
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
let sys = actix_rt::System::new("test");
|
let sys = actix_rt::System::new();
|
||||||
let builder = ssl_acceptor().unwrap();
|
let builder = ssl_acceptor().unwrap();
|
||||||
|
|
||||||
let srv = HttpServer::new(|| {
|
let srv = HttpServer::new(|| {
|
||||||
|
@ -116,7 +120,7 @@ async fn test_start_ssl() {
|
||||||
});
|
});
|
||||||
let (srv, sys) = rx.recv().unwrap();
|
let (srv, sys) = rx.recv().unwrap();
|
||||||
|
|
||||||
use open_ssl::ssl::{SslConnector, SslMethod, SslVerifyMode};
|
use openssl::ssl::{SslConnector, SslMethod, SslVerifyMode};
|
||||||
let mut builder = SslConnector::builder(SslMethod::tls()).unwrap();
|
let mut builder = SslConnector::builder(SslMethod::tls()).unwrap();
|
||||||
builder.set_verify(SslVerifyMode::NONE);
|
builder.set_verify(SslVerifyMode::NONE);
|
||||||
let _ = builder
|
let _ = builder
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
|
#[cfg(feature = "openssl")]
|
||||||
|
extern crate tls_openssl as openssl;
|
||||||
|
#[cfg(feature = "rustls")]
|
||||||
|
extern crate tls_rustls as rustls;
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
future::Future,
|
future::Future,
|
||||||
io::{Read, Write},
|
io::{Read, Write},
|
||||||
|
@ -713,7 +718,7 @@ async fn test_brotli_encoding_large() {
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_brotli_encoding_large_openssl() {
|
async fn test_brotli_encoding_large_openssl() {
|
||||||
// load ssl keys
|
// load ssl keys
|
||||||
use open_ssl::ssl::{SslAcceptor, SslFiletype, SslMethod};
|
use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod};
|
||||||
let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap();
|
let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap();
|
||||||
builder
|
builder
|
||||||
.set_private_key_file("tests/key.pem", SslFiletype::PEM)
|
.set_private_key_file("tests/key.pem", SslFiletype::PEM)
|
||||||
|
@ -753,8 +758,8 @@ async fn test_brotli_encoding_large_openssl() {
|
||||||
#[cfg(all(feature = "rustls", feature = "openssl"))]
|
#[cfg(all(feature = "rustls", feature = "openssl"))]
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_reading_deflate_encoding_large_random_rustls() {
|
async fn test_reading_deflate_encoding_large_random_rustls() {
|
||||||
use rust_tls::internal::pemfile::{certs, pkcs8_private_keys};
|
use rustls::internal::pemfile::{certs, pkcs8_private_keys};
|
||||||
use rust_tls::{NoClientAuth, ServerConfig};
|
use rustls::{NoClientAuth, ServerConfig};
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::BufReader;
|
use std::io::BufReader;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue