1
0
mirror of https://github.com/fafhrd91/actix-web synced 2025-07-06 10:50:17 +02:00

Compare commits

...

12 Commits

54 changed files with 828 additions and 549 deletions

View File

@ -3,6 +3,18 @@
## Unreleased - 2021-xx-xx
## 4.0.0-beta.11 - 2021-11-15
### Added
* Re-export `dev::ServerHandle` from `actix-server`. [#2442]
### Changed
* `ContentType::html` now produces `text/html; charset=utf-8` instead of `text/html`. [#2423]
* Update `actix-server` to `2.0.0-beta.9`. [#2442]
[#2423]: https://github.com/actix/actix-web/pull/2423
[#2442]: https://github.com/actix/actix-web/pull/2442
## 4.0.0-beta.10 - 2021-10-20
### Added
* Option to allow `Json` extractor to work without a `Content-Type` header present. [#2362]
@ -16,12 +28,13 @@
* Minimum supported Rust version (MSRV) is now 1.52.
### Removed
* `ServiceResponse::checked_expr` was a legacy and just removed. [#2401]
* Useless `ServiceResponse::checked_expr` method. [#2401]
[#2233]: https://github.com/actix/actix-web/pull/2233
[#2362]: https://github.com/actix/actix-web/pull/2362
[#2384]: https://github.com/actix/actix-web/pull/2384
[#2401]: https://github.com/actix/actix-web/pull/2401
[#2403]: https://github.com/actix/actix-web/pull/2403
[#2409]: https://github.com/actix/actix-web/pull/2409
[#2414]: https://github.com/actix/actix-web/pull/2414

View File

@ -1,6 +1,6 @@
[package]
name = "actix-web"
version = "4.0.0-beta.10"
version = "4.0.0-beta.11"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Actix Web is a powerful, pragmatic, and extremely fast web framework for Rust"
keywords = ["actix", "http", "web", "framework", "async"]
@ -69,12 +69,12 @@ __compress = []
actix-codec = "0.4.0"
actix-macros = "0.2.3"
actix-rt = "2.2"
actix-server = "2.0.0-beta.3"
actix-server = "2.0.0-beta.9"
actix-service = "2.0.0"
actix-utils = "3.0.0"
actix-tls = { version = "3.0.0-beta.7", default-features = false, optional = true }
actix-http = "3.0.0-beta.11"
actix-http = "3.0.0-beta.12"
actix-router = "0.5.0-beta.2"
actix-web-codegen = "0.5.0-beta.5"
@ -104,8 +104,8 @@ time = { version = "0.3", default-features = false, features = ["formatting"] }
url = "2.1"
[dev-dependencies]
actix-test = { version = "0.1.0-beta.3", features = ["openssl", "rustls"] }
awc = { version = "3.0.0-beta.9", features = ["openssl"] }
actix-test = { version = "0.1.0-beta.6", features = ["openssl", "rustls"] }
awc = { version = "3.0.0-beta.10", features = ["openssl"] }
brotli2 = "0.3.2"
criterion = { version = "0.3", features = ["html_reports"] }

View File

@ -6,10 +6,10 @@
<p>
[![crates.io](https://img.shields.io/crates/v/actix-web?label=latest)](https://crates.io/crates/actix-web)
[![Documentation](https://docs.rs/actix-web/badge.svg?version=4.0.0-beta.10)](https://docs.rs/actix-web/4.0.0-beta.10)
[![Documentation](https://docs.rs/actix-web/badge.svg?version=4.0.0-beta.11)](https://docs.rs/actix-web/4.0.0-beta.11)
[![Version](https://img.shields.io/badge/rustc-1.52+-ab6000.svg)](https://blog.rust-lang.org/2021/05/06/Rust-1.52.0.html)
![MIT or Apache 2.0 licensed](https://img.shields.io/crates/l/actix-web.svg)
[![Dependency Status](https://deps.rs/crate/actix-web/4.0.0-beta.10/status.svg)](https://deps.rs/crate/actix-web/4.0.0-beta.10)
[![Dependency Status](https://deps.rs/crate/actix-web/4.0.0-beta.11/status.svg)](https://deps.rs/crate/actix-web/4.0.0-beta.11)
<br />
[![build status](https://github.com/actix/actix-web/workflows/CI%20%28Linux%29/badge.svg?branch=master&event=push)](https://github.com/actix/actix-web/actions)
[![codecov](https://codecov.io/gh/actix/actix-web/branch/master/graph/badge.svg)](https://codecov.io/gh/actix/actix-web)

View File

@ -1,6 +1,9 @@
# Changes
## Unreleased - 2021-xx-xx
## 0.6.0-beta.8 - 2021-10-20
* Minimum supported Rust version (MSRV) is now 1.52.

View File

@ -1,6 +1,6 @@
[package]
name = "actix-files"
version = "0.6.0-beta.7"
version = "0.6.0-beta.8"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Static file serving for Actix Web"
keywords = ["actix", "http", "async", "futures"]
@ -15,8 +15,8 @@ name = "actix_files"
path = "src/lib.rs"
[dependencies]
actix-web = { version = "4.0.0-beta.10", default-features = false }
actix-http = "3.0.0-beta.11"
actix-web = { version = "4.0.0-beta.11", default-features = false }
actix-http = "3.0.0-beta.12"
actix-service = "2.0.0"
actix-utils = "3.0.0"
@ -33,5 +33,5 @@ percent-encoding = "2.1"
[dev-dependencies]
actix-rt = "2.2"
actix-web = "4.0.0-beta.10"
actix-test = "0.1.0-beta.5"
actix-web = "4.0.0-beta.11"
actix-test = "0.1.0-beta.6"

View File

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

View File

@ -1,8 +1,15 @@
# Changes
## Unreleased - 2021-xx-xx
## 3.0.0-beta.6 - 2021-11-15
* `TestServer::stop` is now async and will wait for the server and system to shutdown. [#2442]
* Update `actix-server` to `2.0.0-beta.9`. [#2442]
* Minimum supported Rust version (MSRV) is now 1.52.
[#2442]: https://github.com/actix/actix-web/pull/2442
## 3.0.0-beta.5 - 2021-09-09
* Minimum supported Rust version (MSRV) is now 1.51.

View File

@ -1,6 +1,6 @@
[package]
name = "actix-http-test"
version = "3.0.0-beta.5"
version = "3.0.0-beta.6"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Various helpers for Actix applications to use during testing"
keywords = ["http", "web", "framework", "async", "futures"]
@ -34,13 +34,13 @@ actix-codec = "0.4.0"
actix-tls = "3.0.0-beta.7"
actix-utils = "3.0.0"
actix-rt = "2.2"
actix-server = "2.0.0-beta.3"
awc = { version = "3.0.0-beta.9", default-features = false }
actix-server = "2.0.0-beta.9"
awc = { version = "3.0.0-beta.10", default-features = false }
base64 = "0.13"
bytes = "1"
futures-core = { version = "0.3.7", default-features = false }
http = "0.2.2"
http = "0.2.5"
log = "0.4"
socket2 = "0.4"
serde = "1.0"
@ -48,7 +48,8 @@ serde_json = "1.0"
slab = "0.4"
serde_urlencoded = "0.7"
tls-openssl = { version = "0.10.9", package = "openssl", optional = true }
tokio = { version = "1.2", features = ["sync"] }
[dev-dependencies]
actix-web = { version = "4.0.0-beta.10", default-features = false, features = ["cookies"] }
actix-http = "3.0.0-beta.11"
actix-web = { version = "4.0.0-beta.11", default-features = false, features = ["cookies"] }
actix-http = "3.0.0-beta.12"

View File

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

View File

@ -7,7 +7,7 @@
#[cfg(feature = "openssl")]
extern crate tls_openssl as openssl;
use std::{net, sync::mpsc, thread, time::Duration};
use std::{net, thread, time::Duration};
use actix_codec::{AsyncRead, AsyncWrite, Framed};
use actix_rt::{net::TcpStream, System};
@ -19,6 +19,7 @@ use bytes::Bytes;
use futures_core::stream::Stream;
use http::Method;
use socket2::{Domain, Protocol, Socket, Type};
use tokio::sync::mpsc;
/// Start test server
///
@ -55,12 +56,13 @@ pub async fn test_server<F: ServiceFactory<TcpStream>>(factory: F) -> TestServer
test_server_with_addr(tcp, factory).await
}
/// Start [`test server`](test_server()) on a concrete Address
/// Start [`test server`](test_server()) on an existing address binding.
pub async fn test_server_with_addr<F: ServiceFactory<TcpStream>>(
tcp: net::TcpListener,
factory: F,
) -> TestServer {
let (tx, rx) = mpsc::channel();
let (started_tx, started_rx) = std::sync::mpsc::channel();
let (thread_stop_tx, thread_stop_rx) = mpsc::channel(1);
// run server in separate thread
thread::spawn(move || {
@ -68,59 +70,73 @@ pub async fn test_server_with_addr<F: ServiceFactory<TcpStream>>(
let local_addr = tcp.local_addr().unwrap();
let srv = Server::build()
.listen("test", tcp, factory)?
.workers(1)
.disable_signals();
.disable_signals()
.listen("test", tcp, factory)
.expect("test server could not be created");
sys.block_on(async {
srv.run();
tx.send((System::current(), local_addr)).unwrap();
});
let srv = srv.run();
started_tx
.send((System::current(), srv.handle(), local_addr))
.unwrap();
sys.run()
// drive server loop
sys.block_on(srv).unwrap();
// start system event loop
sys.run().unwrap();
// notify TestServer that server and system have shut down
// all thread managed resources should be dropped at this point
let _ = thread_stop_tx.send(());
});
let (system, addr) = rx.recv().unwrap();
let (system, server, addr) = started_rx.recv().unwrap();
let client = {
#[cfg(feature = "openssl")]
let connector = {
#[cfg(feature = "openssl")]
{
use openssl::ssl::{SslConnector, SslMethod, SslVerifyMode};
use openssl::ssl::{SslConnector, SslMethod, SslVerifyMode};
let mut builder = SslConnector::builder(SslMethod::tls()).unwrap();
builder.set_verify(SslVerifyMode::NONE);
let _ = builder
.set_alpn_protos(b"\x02h2\x08http/1.1")
.map_err(|e| log::error!("Can not set alpn protocol: {:?}", e));
Connector::new()
.conn_lifetime(Duration::from_secs(0))
.timeout(Duration::from_millis(30000))
.ssl(builder.build())
}
#[cfg(not(feature = "openssl"))]
{
Connector::new()
.conn_lifetime(Duration::from_secs(0))
.timeout(Duration::from_millis(30000))
}
let mut builder = SslConnector::builder(SslMethod::tls()).unwrap();
builder.set_verify(SslVerifyMode::NONE);
let _ = builder
.set_alpn_protos(b"\x02h2\x08http/1.1")
.map_err(|e| log::error!("Can not set alpn protocol: {:?}", e));
Connector::new()
.conn_lifetime(Duration::from_secs(0))
.timeout(Duration::from_millis(30000))
.ssl(builder.build())
};
#[cfg(not(feature = "openssl"))]
let connector = {
Connector::new()
.conn_lifetime(Duration::from_secs(0))
.timeout(Duration::from_millis(30000))
};
Client::builder().connector(connector).finish()
};
TestServer {
addr,
server,
client,
system,
addr,
thread_stop_rx,
}
}
/// Test server controller
pub struct TestServer {
server: actix_server::ServerHandle,
client: awc::Client,
system: actix_rt::System,
addr: net::SocketAddr,
client: Client,
system: System,
thread_stop_rx: mpsc::Receiver<()>,
}
impl TestServer {
@ -257,15 +273,32 @@ impl TestServer {
self.client.headers()
}
/// Stop HTTP server
fn stop(&mut self) {
/// Gracefully stop HTTP server.
///
/// Waits for spawned `Server` and `System` to shutdown gracefully.
pub async fn stop(&mut self) {
// signal server to stop
self.server.stop(true).await;
// also signal system to stop
// though this is handled by `ServerBuilder::exit_system` too
self.system.stop();
// wait for thread to be stopped but don't care about result
let _ = self.thread_stop_rx.recv().await;
}
}
impl Drop for TestServer {
fn drop(&mut self) {
self.stop()
// calls in this Drop impl should be enough to shut down the server, system, and thread
// without needing to await anything
// signal server to stop
let _ = self.server.stop(true);
// signal system to stop
self.system.stop();
}
}

View File

@ -3,6 +3,18 @@
## Unreleased - 2021-xx-xx
## 3.0.0-beta.12 - 2021-11-15
### Changed
* Update `actix-server` to `2.0.0-beta.9`. [#2442]
### Removed
* `client` module. [#2425]
* `trust-dns` feature. [#2425]
[#2425]: https://github.com/actix/actix-web/pull/2425
[#2442]: https://github.com/actix/actix-web/pull/2442
## 3.0.0-beta.11 - 2021-10-20
### Changed
* Updated rustls to v0.20. [#2414]

View File

@ -1,6 +1,6 @@
[package]
name = "actix-http"
version = "3.0.0-beta.11"
version = "3.0.0-beta.12"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "HTTP primitives for the Actix ecosystem"
keywords = ["actix", "http", "framework", "async", "futures"]
@ -27,19 +27,16 @@ path = "src/lib.rs"
default = []
# openssl
openssl = ["actix-tls/openssl"]
openssl = ["actix-tls/accept", "actix-tls/openssl"]
# rustls support
rustls = ["actix-tls/rustls"]
rustls = ["actix-tls/accept", "actix-tls/rustls"]
# enable compression support
compress-brotli = ["brotli2", "__compress"]
compress-gzip = ["flate2", "__compress"]
compress-zstd = ["zstd", "__compress"]
# trust-dns as client dns resolver
trust-dns = ["trust-dns-resolver"]
# Internal (PRIVATE!) features used to aid testing and cheking feature status.
# Don't rely on these whatsoever. They may disappear at anytime.
__compress = []
@ -49,7 +46,6 @@ actix-service = "2.0.0"
actix-codec = "0.4.0"
actix-utils = "3.0.0"
actix-rt = "2.2"
actix-tls = { version = "3.0.0-beta.7", features = ["accept", "connect"] }
ahash = "0.7"
base64 = "0.13"
@ -61,13 +57,12 @@ encoding_rs = "0.8"
futures-core = { version = "0.3.7", default-features = false, features = ["alloc"] }
futures-util = { version = "0.3.7", default-features = false, features = ["alloc", "sink"] }
h2 = "0.3.1"
http = "0.2.2"
http = "0.2.5"
httparse = "1.5.1"
httpdate = "1.0.1"
itoa = "0.4"
language-tags = "0.3"
local-channel = "0.1"
once_cell = "1.5"
log = "0.4"
mime = "0.3"
percent-encoding = "2.1"
@ -76,18 +71,18 @@ pin-project-lite = "0.2"
rand = "0.8"
sha-1 = "0.9"
smallvec = "1.6.1"
tokio = { version = "1.2", features = ["sync"] }
# tls
actix-tls = { version = "3.0.0-beta.7", default-features = false, optional = true }
# compression
brotli2 = { version="0.3.2", optional = true }
flate2 = { version = "1.0.13", optional = true }
zstd = { version = "0.7", optional = true }
trust-dns-resolver = { version = "0.20.0", optional = true }
[dev-dependencies]
actix-server = "2.0.0-beta.3"
actix-http-test = { version = "3.0.0-beta.5", features = ["openssl"] }
actix-server = "2.0.0-beta.9"
actix-http-test = { version = "3.0.0-beta.6", features = ["openssl"] }
actix-tls = { version = "3.0.0-beta.7", features = ["openssl"] }
async-stream = "0.3"
criterion = { version = "0.3", features = ["html_reports"] }
@ -99,6 +94,7 @@ serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tls-openssl = { package = "openssl", version = "0.10.9" }
tls-rustls = { package = "rustls", version = "0.20.0" }
tokio = { version = "1.2", features = ["net", "rt"] }
[[example]]
name = "ws"

View File

@ -3,11 +3,11 @@
> HTTP primitives for the Actix ecosystem.
[![crates.io](https://img.shields.io/crates/v/actix-http?label=latest)](https://crates.io/crates/actix-http)
[![Documentation](https://docs.rs/actix-http/badge.svg?version=3.0.0-beta.11)](https://docs.rs/actix-http/3.0.0-beta.11)
[![Documentation](https://docs.rs/actix-http/badge.svg?version=3.0.0-beta.12)](https://docs.rs/actix-http/3.0.0-beta.12)
[![Version](https://img.shields.io/badge/rustc-1.52+-ab6000.svg)](https://blog.rust-lang.org/2021/05/06/Rust-1.52.0.html)
![MIT or Apache 2.0 licensed](https://img.shields.io/crates/l/actix-http.svg)
<br />
[![dependency status](https://deps.rs/crate/actix-http/3.0.0-beta.11/status.svg)](https://deps.rs/crate/actix-http/3.0.0-beta.11)
[![dependency status](https://deps.rs/crate/actix-http/3.0.0-beta.12/status.svg)](https://deps.rs/crate/actix-http/3.0.0-beta.12)
[![Download](https://img.shields.io/crates/d/actix-http.svg)](https://crates.io/crates/actix-http)
[![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x)

View File

@ -10,11 +10,15 @@ use std::{
};
use actix_codec::{AsyncRead, AsyncWrite};
use actix_rt::time::Sleep;
use actix_service::Service;
use actix_utils::future::poll_fn;
use bytes::{Bytes, BytesMut};
use futures_core::ready;
use h2::server::{Connection, SendResponse};
use h2::{
server::{Connection, SendResponse},
Ping, PingPong,
};
use http::header::{HeaderValue, CONNECTION, CONTENT_LENGTH, DATE, TRANSFER_ENCODING};
use log::{error, trace};
use pin_project_lite::pin_project;
@ -36,29 +40,46 @@ pin_project! {
on_connect_data: OnConnectData,
config: ServiceConfig,
peer_addr: Option<net::SocketAddr>,
_phantom: PhantomData<B>,
ping_pong: Option<H2PingPong>,
_phantom: PhantomData<B>
}
}
impl<T, S, B, X, U> Dispatcher<T, S, B, X, U> {
impl<T, S, B, X, U> Dispatcher<T, S, B, X, U>
where
T: AsyncRead + AsyncWrite + Unpin,
{
pub(crate) fn new(
flow: Rc<HttpFlow<S, X, U>>,
connection: Connection<T, Bytes>,
mut connection: Connection<T, Bytes>,
on_connect_data: OnConnectData,
config: ServiceConfig,
peer_addr: Option<net::SocketAddr>,
) -> Self {
let ping_pong = config.keep_alive_timer().map(|timer| H2PingPong {
timer: Box::pin(timer),
on_flight: false,
ping_pong: connection.ping_pong().unwrap(),
});
Self {
flow,
config,
peer_addr,
connection,
on_connect_data,
ping_pong,
_phantom: PhantomData,
}
}
}
struct H2PingPong {
timer: Pin<Box<Sleep>>,
on_flight: bool,
ping_pong: PingPong,
}
impl<T, S, B, X, U> Future for Dispatcher<T, S, B, X, U>
where
T: AsyncRead + AsyncWrite + Unpin,
@ -77,54 +98,92 @@ where
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.get_mut();
while let Some((req, tx)) =
ready!(Pin::new(&mut this.connection).poll_accept(cx)?)
{
let (parts, body) = req.into_parts();
let pl = crate::h2::Payload::new(body);
let pl = Payload::<crate::payload::PayloadStream>::H2(pl);
let mut req = Request::with_payload(pl);
loop {
match Pin::new(&mut this.connection).poll_accept(cx)? {
Poll::Ready(Some((req, tx))) => {
let (parts, body) = req.into_parts();
let pl = crate::h2::Payload::new(body);
let pl = Payload::<crate::payload::PayloadStream>::H2(pl);
let mut req = Request::with_payload(pl);
let head = req.head_mut();
head.uri = parts.uri;
head.method = parts.method;
head.version = parts.version;
head.headers = parts.headers.into();
head.peer_addr = this.peer_addr;
let head = req.head_mut();
head.uri = parts.uri;
head.method = parts.method;
head.version = parts.version;
head.headers = parts.headers.into();
head.peer_addr = this.peer_addr;
// merge on_connect_ext data into request extensions
this.on_connect_data.merge_into(&mut req);
// merge on_connect_ext data into request extensions
this.on_connect_data.merge_into(&mut req);
let fut = this.flow.service.call(req);
let config = this.config.clone();
let fut = this.flow.service.call(req);
let config = this.config.clone();
// multiplex request handling with spawn task
actix_rt::spawn(async move {
// resolve service call and send response.
let res = match fut.await {
Ok(res) => handle_response(res.into(), tx, config).await,
Err(err) => {
let res: Response<AnyBody> = err.into();
handle_response(res, tx, config).await
}
};
// multiplex request handling with spawn task
actix_rt::spawn(async move {
// resolve service call and send response.
let res = match fut.await {
Ok(res) => handle_response(res.into(), tx, config).await,
Err(err) => {
let res: Response<AnyBody> = err.into();
handle_response(res, tx, config).await
}
};
// log error.
if let Err(err) = res {
match err {
DispatchError::SendResponse(err) => {
trace!("Error sending HTTP/2 response: {:?}", err)
// log error.
if let Err(err) = res {
match err {
DispatchError::SendResponse(err) => {
trace!("Error sending HTTP/2 response: {:?}", err)
}
DispatchError::SendData(err) => warn!("{:?}", err),
DispatchError::ResponseBody(err) => {
error!("Response payload stream error: {:?}", err)
}
}
}
DispatchError::SendData(err) => warn!("{:?}", err),
DispatchError::ResponseBody(err) => {
error!("Response payload stream error: {:?}", err)
}
}
});
}
});
}
Poll::Ready(None) => return Poll::Ready(Ok(())),
Poll::Pending => match this.ping_pong.as_mut() {
Some(ping_pong) => loop {
if ping_pong.on_flight {
// When have on flight ping pong. poll pong and and keep alive timer.
// on success pong received update keep alive timer to determine the next timing of
// ping pong.
match ping_pong.ping_pong.poll_pong(cx)? {
Poll::Ready(_) => {
ping_pong.on_flight = false;
Poll::Ready(Ok(()))
let dead_line =
this.config.keep_alive_expire().unwrap();
ping_pong.timer.as_mut().reset(dead_line);
}
Poll::Pending => {
return ping_pong
.timer
.as_mut()
.poll(cx)
.map(|_| Ok(()))
}
}
} else {
// When there is no on flight ping pong. keep alive timer is used to wait for next
// timing of ping pong. Therefore at this point it serves as an interval instead.
ready!(ping_pong.timer.as_mut().poll(cx));
ping_pong.ping_pong.send_ping(Ping::opaque())?;
let dead_line = this.config.keep_alive_expire().unwrap();
ping_pong.timer.as_mut().reset(dead_line);
ping_pong.on_flight = true;
}
},
None => return Poll::Pending,
},
}
}
}
}

View File

@ -29,7 +29,6 @@ extern crate log;
pub mod body;
mod builder;
pub mod client;
mod config;
#[cfg(feature = "__compress")]

View File

@ -0,0 +1,77 @@
use std::io;
use actix_http::{error::Error, HttpService, Response};
use actix_server::Server;
#[actix_rt::test]
async fn h2_ping_pong() -> io::Result<()> {
let (tx, rx) = std::sync::mpsc::sync_channel(1);
let lst = std::net::TcpListener::bind("127.0.0.1:0")?;
let addr = lst.local_addr().unwrap();
let join = std::thread::spawn(move || {
actix_rt::System::new().block_on(async move {
let srv = Server::build()
.disable_signals()
.workers(1)
.listen("h2_ping_pong", lst, || {
HttpService::build()
.keep_alive(3)
.h2(|_| async { Ok::<_, Error>(Response::ok()) })
.tcp()
})?
.run();
tx.send(srv.handle()).unwrap();
srv.await
})
});
let handle = rx.recv().unwrap();
let (sync_tx, rx) = std::sync::mpsc::sync_channel(1);
// use a separate thread for h2 client so it can be blocked.
std::thread::spawn(move || {
tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap()
.block_on(async move {
let stream = tokio::net::TcpStream::connect(addr).await.unwrap();
let (mut tx, conn) = h2::client::handshake(stream).await.unwrap();
tokio::spawn(async move { conn.await.unwrap() });
let (res, _) = tx.send_request(::http::Request::new(()), true).unwrap();
let res = res.await.unwrap();
assert_eq!(res.status().as_u16(), 200);
sync_tx.send(()).unwrap();
// intentionally block the client thread so it can not answer ping pong.
std::thread::sleep(std::time::Duration::from_secs(1000));
})
});
rx.recv().unwrap();
let now = std::time::Instant::now();
// stop server gracefully. this step would take up to 30 seconds.
handle.stop(true).await;
// join server thread. only when connection are all gone this step would finish.
join.join().unwrap()?;
// check the time used for join server thread so it's known that the server shutdown
// is from keep alive and not server graceful shutdown timeout.
assert!(now.elapsed() < std::time::Duration::from_secs(30));
Ok(())
}

View File

@ -8,7 +8,7 @@ use actix_http::{
body::{AnyBody, Body, SizedStream},
error::PayloadError,
http::{
header::{self, HeaderName, HeaderValue},
header::{self, HeaderValue},
Method, StatusCode, Version,
},
Error, HttpMessage, HttpService, Request, Response,
@ -143,38 +143,25 @@ async fn test_h2_content_length() {
})
.await;
let header = HeaderName::from_static("content-length");
let value = HeaderValue::from_static("0");
static VALUE: HeaderValue = HeaderValue::from_static("0");
{
for &i in &[0] {
let req = srv
.request(Method::HEAD, srv.surl(&format!("/{}", i)))
.send();
let _response = req.await.expect_err("should timeout on recv 1xx frame");
// assert_eq!(response.headers().get(&header), None);
let req = srv.request(Method::HEAD, srv.surl("/0")).send();
req.await.expect_err("should timeout on recv 1xx frame");
let req = srv
.request(Method::GET, srv.surl(&format!("/{}", i)))
.send();
let _response = req.await.expect_err("should timeout on recv 1xx frame");
// assert_eq!(response.headers().get(&header), None);
}
let req = srv.request(Method::GET, srv.surl("/0")).send();
req.await.expect_err("should timeout on recv 1xx frame");
for &i in &[1] {
let req = srv
.request(Method::GET, srv.surl(&format!("/{}", i)))
.send();
let response = req.await.unwrap();
assert_eq!(response.headers().get(&header), None);
}
let req = srv.request(Method::GET, srv.surl("/1")).send();
let response = req.await.unwrap();
assert!(response.headers().get("content-length").is_none());
for &i in &[2, 3] {
let req = srv
.request(Method::GET, srv.surl(&format!("/{}", i)))
.send();
let response = req.await.unwrap();
assert_eq!(response.headers().get(&header), Some(&value));
assert_eq!(response.headers().get("content-length"), Some(&VALUE));
}
}
}

View File

@ -26,10 +26,7 @@ use bytes::{Bytes, BytesMut};
use derive_more::{Display, Error};
use futures_core::Stream;
use futures_util::stream::{once, StreamExt as _};
use rustls::{
Certificate, OwnedTrustAnchor, PrivateKey, RootCertStore,
ServerConfig as RustlsServerConfig, ServerName,
};
use rustls::{Certificate, PrivateKey, ServerConfig as RustlsServerConfig, ServerName};
use rustls_pemfile::{certs, pkcs8_private_keys};
async fn load_body<S>(mut stream: S) -> Result<BytesMut, PayloadError>

View File

@ -24,7 +24,7 @@ use regex::Regex;
#[actix_rt::test]
async fn test_h1() {
let srv = test_server(|| {
let mut srv = test_server(|| {
HttpService::build()
.keep_alive(KeepAlive::Disabled)
.client_timeout(1000)
@ -39,11 +39,13 @@ async fn test_h1() {
let response = srv.get("/").send().await.unwrap();
assert!(response.status().is_success());
srv.stop().await;
}
#[actix_rt::test]
async fn test_h1_2() {
let srv = test_server(|| {
let mut srv = test_server(|| {
HttpService::build()
.keep_alive(KeepAlive::Disabled)
.client_timeout(1000)
@ -59,6 +61,8 @@ async fn test_h1_2() {
let response = srv.get("/").send().await.unwrap();
assert!(response.status().is_success());
srv.stop().await;
}
#[derive(Debug, Display, Error)]
@ -73,7 +77,7 @@ impl From<ExpectFailed> for Response<AnyBody> {
#[actix_rt::test]
async fn test_expect_continue() {
let srv = test_server(|| {
let mut srv = test_server(|| {
HttpService::build()
.expect(fn_service(|req: Request| {
if req.head().uri.query() == Some("yes=") {
@ -98,11 +102,13 @@ async fn test_expect_continue() {
let mut data = String::new();
let _ = stream.read_to_string(&mut data);
assert!(data.starts_with("HTTP/1.1 100 Continue\r\n\r\nHTTP/1.1 200 OK\r\n"));
srv.stop().await;
}
#[actix_rt::test]
async fn test_expect_continue_h1() {
let srv = test_server(|| {
let mut srv = test_server(|| {
HttpService::build()
.expect(fn_service(|req: Request| {
sleep(Duration::from_millis(20)).then(move |_| {
@ -129,6 +135,8 @@ async fn test_expect_continue_h1() {
let mut data = String::new();
let _ = stream.read_to_string(&mut data);
assert!(data.starts_with("HTTP/1.1 100 Continue\r\n\r\nHTTP/1.1 200 OK\r\n"));
srv.stop().await;
}
#[actix_rt::test]
@ -136,7 +144,7 @@ async fn test_chunked_payload() {
let chunk_sizes = vec![32768, 32, 32768];
let total_size: usize = chunk_sizes.iter().sum();
let srv = test_server(|| {
let mut srv = test_server(|| {
HttpService::build()
.h1(fn_service(|mut request: Request| {
request
@ -188,11 +196,13 @@ async fn test_chunked_payload() {
};
assert_eq!(returned_size, total_size);
srv.stop().await;
}
#[actix_rt::test]
async fn test_slow_request() {
let srv = test_server(|| {
let mut srv = test_server(|| {
HttpService::build()
.client_timeout(100)
.finish(|_| ok::<_, Infallible>(Response::ok()))
@ -205,11 +215,13 @@ async fn test_slow_request() {
let mut data = String::new();
let _ = stream.read_to_string(&mut data);
assert!(data.starts_with("HTTP/1.1 408 Request Timeout"));
srv.stop().await;
}
#[actix_rt::test]
async fn test_http1_malformed_request() {
let srv = test_server(|| {
let mut srv = test_server(|| {
HttpService::build()
.h1(|_| ok::<_, Infallible>(Response::ok()))
.tcp()
@ -221,11 +233,13 @@ async fn test_http1_malformed_request() {
let mut data = String::new();
let _ = stream.read_to_string(&mut data);
assert!(data.starts_with("HTTP/1.1 400 Bad Request"));
srv.stop().await;
}
#[actix_rt::test]
async fn test_http1_keepalive() {
let srv = test_server(|| {
let mut srv = test_server(|| {
HttpService::build()
.h1(|_| ok::<_, Infallible>(Response::ok()))
.tcp()
@ -242,11 +256,13 @@ async fn test_http1_keepalive() {
let mut data = vec![0; 1024];
let _ = stream.read(&mut data);
assert_eq!(&data[..17], b"HTTP/1.1 200 OK\r\n");
srv.stop().await;
}
#[actix_rt::test]
async fn test_http1_keepalive_timeout() {
let srv = test_server(|| {
let mut srv = test_server(|| {
HttpService::build()
.keep_alive(1)
.h1(|_| ok::<_, Infallible>(Response::ok()))
@ -264,11 +280,13 @@ async fn test_http1_keepalive_timeout() {
let mut data = vec![0; 1024];
let res = stream.read(&mut data).unwrap();
assert_eq!(res, 0);
srv.stop().await;
}
#[actix_rt::test]
async fn test_http1_keepalive_close() {
let srv = test_server(|| {
let mut srv = test_server(|| {
HttpService::build()
.h1(|_| ok::<_, Infallible>(Response::ok()))
.tcp()
@ -285,11 +303,13 @@ async fn test_http1_keepalive_close() {
let mut data = vec![0; 1024];
let res = stream.read(&mut data).unwrap();
assert_eq!(res, 0);
srv.stop().await;
}
#[actix_rt::test]
async fn test_http10_keepalive_default_close() {
let srv = test_server(|| {
let mut srv = test_server(|| {
HttpService::build()
.h1(|_| ok::<_, Infallible>(Response::ok()))
.tcp()
@ -305,11 +325,13 @@ async fn test_http10_keepalive_default_close() {
let mut data = vec![0; 1024];
let res = stream.read(&mut data).unwrap();
assert_eq!(res, 0);
srv.stop().await;
}
#[actix_rt::test]
async fn test_http10_keepalive() {
let srv = test_server(|| {
let mut srv = test_server(|| {
HttpService::build()
.h1(|_| ok::<_, Infallible>(Response::ok()))
.tcp()
@ -332,11 +354,13 @@ async fn test_http10_keepalive() {
let mut data = vec![0; 1024];
let res = stream.read(&mut data).unwrap();
assert_eq!(res, 0);
srv.stop().await;
}
#[actix_rt::test]
async fn test_http1_keepalive_disabled() {
let srv = test_server(|| {
let mut srv = test_server(|| {
HttpService::build()
.keep_alive(KeepAlive::Disabled)
.h1(|_| ok::<_, Infallible>(Response::ok()))
@ -353,6 +377,8 @@ async fn test_http1_keepalive_disabled() {
let mut data = vec![0; 1024];
let res = stream.read(&mut data).unwrap();
assert_eq!(res, 0);
srv.stop().await;
}
#[actix_rt::test]
@ -362,7 +388,7 @@ async fn test_content_length() {
StatusCode,
};
let srv = test_server(|| {
let mut srv = test_server(|| {
HttpService::build()
.h1(|req: Request| {
let indx: usize = req.uri().path()[1..].parse().unwrap();
@ -400,6 +426,8 @@ async fn test_content_length() {
assert_eq!(response.headers().get(&header), Some(&value));
}
}
srv.stop().await;
}
#[actix_rt::test]
@ -439,6 +467,8 @@ async fn test_h1_headers() {
// read response
let bytes = srv.load_body(response).await.unwrap();
assert_eq!(bytes, Bytes::from(data2));
srv.stop().await;
}
const STR: &str = "Hello World Hello World Hello World Hello World Hello World \
@ -478,6 +508,8 @@ async fn test_h1_body() {
// read response
let bytes = srv.load_body(response).await.unwrap();
assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
srv.stop().await;
}
#[actix_rt::test]
@ -503,6 +535,8 @@ async fn test_h1_head_empty() {
// read response
let bytes = srv.load_body(response).await.unwrap();
assert!(bytes.is_empty());
srv.stop().await;
}
#[actix_rt::test]
@ -528,11 +562,13 @@ async fn test_h1_head_binary() {
// read response
let bytes = srv.load_body(response).await.unwrap();
assert!(bytes.is_empty());
srv.stop().await;
}
#[actix_rt::test]
async fn test_h1_head_binary2() {
let srv = test_server(|| {
let mut srv = test_server(|| {
HttpService::build()
.h1(|_| ok::<_, Infallible>(Response::ok().set_body(STR)))
.tcp()
@ -549,6 +585,8 @@ async fn test_h1_head_binary2() {
.unwrap();
assert_eq!(format!("{}", STR.len()), len.to_str().unwrap());
}
srv.stop().await;
}
#[actix_rt::test]
@ -571,6 +609,8 @@ async fn test_h1_body_length() {
// read response
let bytes = srv.load_body(response).await.unwrap();
assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
srv.stop().await;
}
#[actix_rt::test]
@ -606,6 +646,8 @@ async fn test_h1_body_chunked_explicit() {
// decode
assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
srv.stop().await;
}
#[actix_rt::test]
@ -635,6 +677,8 @@ async fn test_h1_body_chunked_implicit() {
// read response
let bytes = srv.load_body(response).await.unwrap();
assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
srv.stop().await;
}
#[actix_rt::test]
@ -662,6 +706,8 @@ async fn test_h1_response_http_error_handling() {
bytes,
Bytes::from_static(b"error processing HTTP: failed to parse header value")
);
srv.stop().await;
}
#[derive(Debug, Display, Error)]
@ -689,11 +735,13 @@ async fn test_h1_service_error() {
// read response
let bytes = srv.load_body(response).await.unwrap();
assert_eq!(bytes, Bytes::from_static(b"error"));
srv.stop().await;
}
#[actix_rt::test]
async fn test_h1_on_connect() {
let srv = test_server(|| {
let mut srv = test_server(|| {
HttpService::build()
.on_connect_ext(|_, data| {
data.insert(20isize);
@ -708,4 +756,6 @@ async fn test_h1_on_connect() {
let response = srv.get("/").send().await.unwrap();
assert!(response.status().is_success());
srv.stop().await;
}

View File

@ -1,6 +1,9 @@
# Changes
## Unreleased - 2021-xx-xx
## 0.4.0-beta.7 - 2021-10-20
* Minimum supported Rust version (MSRV) is now 1.52.

View File

@ -1,6 +1,6 @@
[package]
name = "actix-multipart"
version = "0.4.0-beta.6"
version = "0.4.0-beta.7"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Multipart form support for Actix Web"
keywords = ["http", "web", "framework", "async", "futures"]
@ -14,7 +14,7 @@ name = "actix_multipart"
path = "src/lib.rs"
[dependencies]
actix-web = { version = "4.0.0-beta.10", default-features = false }
actix-web = { version = "4.0.0-beta.11", default-features = false }
actix-utils = "3.0.0"
bytes = "1"
@ -29,6 +29,6 @@ twoway = "0.2"
[dev-dependencies]
actix-rt = "2.2"
actix-http = "3.0.0-beta.11"
actix-http = "3.0.0-beta.12"
tokio = { version = "1", features = ["sync"] }
tokio-stream = "0.1"

View File

@ -3,11 +3,11 @@
> Multipart form support for Actix Web.
[![crates.io](https://img.shields.io/crates/v/actix-multipart?label=latest)](https://crates.io/crates/actix-multipart)
[![Documentation](https://docs.rs/actix-multipart/badge.svg?version=0.4.0-beta.6)](https://docs.rs/actix-multipart/0.4.0-beta.6)
[![Documentation](https://docs.rs/actix-multipart/badge.svg?version=0.4.0-beta.7)](https://docs.rs/actix-multipart/0.4.0-beta.7)
[![Version](https://img.shields.io/badge/rustc-1.52+-ab6000.svg)](https://blog.rust-lang.org/2021/05/06/Rust-1.52.0.html)
![MIT or Apache 2.0 licensed](https://img.shields.io/crates/l/actix-multipart.svg)
<br />
[![dependency status](https://deps.rs/crate/actix-multipart/0.4.0-beta.6/status.svg)](https://deps.rs/crate/actix-multipart/0.4.0-beta.6)
[![dependency status](https://deps.rs/crate/actix-multipart/0.4.0-beta.7/status.svg)](https://deps.rs/crate/actix-multipart/0.4.0-beta.7)
[![Download](https://img.shields.io/crates/d/actix-multipart.svg)](https://crates.io/crates/actix-multipart)
[![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x)

View File

@ -30,7 +30,7 @@ serde = "1"
[dev-dependencies]
criterion = { version = "0.3", features = ["html_reports"] }
firestorm = { version = "0.4", features = ["enable_system_time"] }
http = "0.2.3"
http = "0.2.5"
serde = { version = "1", features = ["derive"] }
[[bench]]

View File

@ -3,6 +3,10 @@
## Unreleased - 2021-xx-xx
## 0.1.0-beta.6 - 2021-11-15
* No significant changes from `0.1.0-beta.5`.
## 0.1.0-beta.5 - 2021-10-20
* Updated rustls to v0.20. [#2414]
* Minimum supported Rust version (MSRV) is now 1.52.

View File

@ -1,6 +1,6 @@
[package]
name = "actix-test"
version = "0.1.0-beta.5"
version = "0.1.0-beta.6"
authors = [
"Nikolay Kim <fafhrd91@gmail.com>",
"Rob Ede <robjtede@icloud.com>",
@ -22,20 +22,20 @@ edition = "2018"
default = []
# rustls
rustls = ["tls-rustls", "actix-http/rustls"]
rustls = ["tls-rustls", "actix-http/rustls", "awc/rustls"]
# openssl
openssl = ["tls-openssl", "actix-http/openssl"]
openssl = ["tls-openssl", "actix-http/openssl", "awc/openssl"]
[dependencies]
actix-codec = "0.4.0"
actix-http = "3.0.0-beta.11"
actix-http-test = "3.0.0-beta.5"
actix-http = "3.0.0-beta.12"
actix-http-test = "3.0.0-beta.6"
actix-service = "2.0.0"
actix-utils = "3.0.0"
actix-web = { version = "4.0.0-beta.10", default-features = false, features = ["cookies"] }
actix-web = { version = "4.0.0-beta.11", default-features = false, features = ["cookies"] }
actix-rt = "2.1"
awc = { version = "3.0.0-beta.9", default-features = false, features = ["cookies"] }
awc = { version = "3.0.0-beta.10", default-features = false, features = ["cookies"] }
futures-core = { version = "0.3.7", default-features = false, features = ["std"] }
futures-util = { version = "0.3.7", default-features = false, features = [] }
@ -45,3 +45,4 @@ serde_json = "1"
serde_urlencoded = "0.7"
tls-openssl = { package = "openssl", version = "0.10.9", optional = true }
tls-rustls = { package = "rustls", version = "0.20.0", optional = true }
tokio = { version = "1.2", features = ["sync"] }

View File

@ -31,7 +31,7 @@ extern crate tls_openssl as openssl;
#[cfg(feature = "rustls")]
extern crate tls_rustls as rustls;
use std::{error::Error as StdError, fmt, net, sync::mpsc, thread, time};
use std::{error::Error as StdError, fmt, net, thread, time::Duration};
use actix_codec::{AsyncRead, AsyncWrite, Framed};
pub use actix_http::test::TestBuffer;
@ -41,8 +41,9 @@ use actix_http::{
};
use actix_service::{map_config, IntoServiceFactory, ServiceFactory, ServiceFactoryExt as _};
use actix_web::{
dev::{AppConfig, MessageBody, Server, Service},
rt, web, Error,
dev::{AppConfig, MessageBody, Server, ServerHandle, Service},
rt::{self, System},
web, Error,
};
use awc::{error::PayloadError, Client, ClientRequest, ClientResponse, Connector};
use futures_core::Stream;
@ -52,6 +53,7 @@ pub use actix_web::test::{
call_service, default_service, init_service, load_stream, ok_service, read_body,
read_body_json, read_response, read_response_json, TestRequest,
};
use tokio::sync::mpsc;
/// Start default [`TestServer`].
///
@ -128,7 +130,11 @@ where
B: MessageBody + 'static,
B::Error: Into<Box<dyn StdError>>,
{
let (tx, rx) = mpsc::channel();
// for sending handles and server info back from the spawned thread
let (started_tx, started_rx) = std::sync::mpsc::channel();
// for signaling the shutdown of spawned server and system
let (thread_stop_tx, thread_stop_rx) = mpsc::channel(1);
let tls = match cfg.stream {
StreamType::Tcp => false,
@ -138,7 +144,7 @@ where
StreamType::Rustls(_) => true,
};
// run server in separate thread
// run server in separate orphaned thread
thread::spawn(move || {
let sys = rt::System::new();
let tcp = net::TcpListener::bind("127.0.0.1:0").unwrap();
@ -146,7 +152,7 @@ where
let factory = factory.clone();
let srv_cfg = cfg.clone();
let timeout = cfg.client_timeout;
let builder = Server::build().workers(1).disable_signals();
let builder = Server::build().workers(1).disable_signals().system_exit();
let srv = match srv_cfg.stream {
StreamType::Tcp => match srv_cfg.tp {
@ -275,17 +281,25 @@ where
}),
},
}
.unwrap();
.expect("test server could not be created");
sys.block_on(async {
let srv = srv.run();
tx.send((rt::System::current(), srv, local_addr)).unwrap();
});
let srv = srv.run();
started_tx
.send((System::current(), srv.handle(), local_addr))
.unwrap();
sys.run()
// drive server loop
sys.block_on(srv).unwrap();
// start system event loop
sys.run().unwrap();
// notify TestServer that server and system have shut down
// all thread managed resources should be dropped at this point
let _ = thread_stop_tx.send(());
});
let (system, server, addr) = rx.recv().unwrap();
let (system, server, addr) = started_rx.recv().unwrap();
let client = {
let connector = {
@ -299,15 +313,15 @@ where
.set_alpn_protos(b"\x02h2\x08http/1.1")
.map_err(|e| log::error!("Can not set alpn protocol: {:?}", e));
Connector::new()
.conn_lifetime(time::Duration::from_secs(0))
.timeout(time::Duration::from_millis(30000))
.conn_lifetime(Duration::from_secs(0))
.timeout(Duration::from_millis(30000))
.ssl(builder.build())
}
#[cfg(not(feature = "openssl"))]
{
Connector::new()
.conn_lifetime(time::Duration::from_secs(0))
.timeout(time::Duration::from_millis(30000))
.conn_lifetime(Duration::from_secs(0))
.timeout(Duration::from_millis(30000))
}
};
@ -315,11 +329,12 @@ where
};
TestServer {
addr,
server,
thread_stop_rx,
client,
system,
addr,
tls,
server,
}
}
@ -405,11 +420,12 @@ impl TestServerConfig {
///
/// See [`start`] for usage example.
pub struct TestServer {
addr: net::SocketAddr,
server: ServerHandle,
thread_stop_rx: mpsc::Receiver<()>,
client: awc::Client,
system: rt::System,
addr: net::SocketAddr,
tls: bool,
server: Server,
}
impl TestServer {
@ -505,15 +521,30 @@ impl TestServer {
}
/// Gracefully stop HTTP server.
pub async fn stop(self) {
///
/// Waits for spawned `Server` and `System` to shutdown gracefully.
pub async fn stop(mut self) {
// signal server to stop
self.server.stop(true).await;
// also signal system to stop
// though this is handled by `ServerBuilder::exit_system` too
self.system.stop();
rt::time::sleep(time::Duration::from_millis(100)).await;
// wait for thread to be stopped but don't care about result
let _ = self.thread_stop_rx.recv().await;
}
}
impl Drop for TestServer {
fn drop(&mut self) {
self.system.stop()
// calls in this Drop impl should be enough to shut down the server, system, and thread
// without needing to await anything
// signal server to stop
let _ = self.server.stop(true);
// signal system to stop
self.system.stop();
}
}

View File

@ -16,8 +16,8 @@ path = "src/lib.rs"
[dependencies]
actix = { version = "0.12.0", default-features = false }
actix-codec = "0.4.0"
actix-http = "3.0.0-beta.11"
actix-web = { version = "4.0.0-beta.10", default-features = false }
actix-http = "3.0.0-beta.12"
actix-web = { version = "4.0.0-beta.11", default-features = false }
bytes = "1"
bytestring = "1"
@ -27,8 +27,8 @@ tokio = { version = "1", features = ["sync"] }
[dev-dependencies]
actix-rt = "2.2"
actix-test = "0.1.0-beta.5"
actix-test = "0.1.0-beta.6"
awc = { version = "3.0.0-beta.9", default-features = false }
awc = { version = "3.0.0-beta.10", default-features = false }
env_logger = "0.8"
futures-util = { version = "0.3.7", default-features = false }

View File

@ -23,9 +23,9 @@ actix-router = "0.5.0-beta.2"
[dev-dependencies]
actix-rt = "2.2"
actix-macros = "0.2.3"
actix-test = "0.1.0-beta.5"
actix-test = "0.1.0-beta.6"
actix-utils = "3.0.0"
actix-web = "4.0.0-beta.10"
actix-web = "4.0.0-beta.11"
futures-core = { version = "0.3.7", default-features = false, features = ["alloc"] }
trybuild = "1"

View File

@ -3,6 +3,9 @@
## Unreleased - 2021-xx-xx
## 3.0.0-beta.10 - 2021-11-15
## 3.0.0-beta.9 - 2021-10-20
* Updated rustls to v0.20. [#2414]

View File

@ -1,6 +1,6 @@
[package]
name = "awc"
version = "3.0.0-beta.9"
version = "3.0.0-beta.10"
authors = [
"Nikolay Kim <fafhrd91@gmail.com>",
"fakeshadow <24548779@qq.com>",
@ -30,10 +30,10 @@ features = ["openssl", "rustls", "compress-brotli", "compress-gzip", "compress-z
default = ["compress-brotli", "compress-gzip", "compress-zstd", "cookies"]
# openssl
openssl = ["tls-openssl", "actix-http/openssl"]
openssl = ["tls-openssl", "actix-tls/openssl"]
# rustls
rustls = ["tls-rustls", "actix-http/rustls"]
rustls = ["tls-rustls", "actix-tls/rustls"]
# Brotli algorithm content-encoding support
compress-brotli = ["actix-http/compress-brotli", "__compress"]
@ -46,7 +46,7 @@ compress-zstd = ["actix-http/compress-zstd", "__compress"]
cookies = ["cookie"]
# trust-dns as dns resolver
trust-dns = ["actix-http/trust-dns"]
trust-dns = ["trust-dns-resolver"]
# Internal (PRIVATE!) features used to aid testing and cheking feature status.
# Don't rely on these whatsoever. They may disappear at anytime.
@ -55,15 +55,20 @@ __compress = []
[dependencies]
actix-codec = "0.4.0"
actix-service = "2.0.0"
actix-http = "3.0.0-beta.11"
actix-http = "3.0.0-beta.12"
actix-rt = { version = "2.1", default-features = false }
actix-tls = { version = "3.0.0-beta.7", features = ["connect"] }
actix-utils = "3.0.0"
ahash = "0.7"
base64 = "0.13"
bytes = "1"
cfg-if = "1"
cookie = { version = "0.15", features = ["percent-encode"], optional = true }
derive_more = "0.99.5"
futures-core = { version = "0.3.7", default-features = false }
futures-util = { version = "0.3.7", default-features = false }
h2 = "0.3"
http = "0.2.5"
itoa = "0.4"
log =" 0.4"
mime = "0.3"
@ -73,17 +78,23 @@ rand = "0.8"
serde = "1.0"
serde_json = "1.0"
serde_urlencoded = "0.7"
tokio = { version = "1", features = ["sync"] }
cookie = { version = "0.15", features = ["percent-encode"], optional = true }
tls-openssl = { package = "openssl", version = "0.10.9", optional = true }
tls-rustls = { package = "rustls", version = "0.20.0", optional = true, features = ["dangerous_configuration"] }
trust-dns-resolver = { version = "0.20.0", optional = true }
[dev-dependencies]
actix-web = { version = "4.0.0-beta.10", features = ["openssl"] }
actix-http = { version = "3.0.0-beta.11", features = ["openssl"] }
actix-http-test = { version = "3.0.0-beta.5", features = ["openssl"] }
actix-web = { version = "4.0.0-beta.11", features = ["openssl"] }
actix-http = { version = "3.0.0-beta.12", features = ["openssl"] }
actix-http-test = { version = "3.0.0-beta.6", features = ["openssl"] }
actix-utils = "3.0.0"
actix-server = "2.0.0-beta.3"
actix-server = "2.0.0-beta.9"
actix-tls = { version = "3.0.0-beta.7", features = ["openssl", "rustls"] }
actix-test = { version = "0.1.0-beta.5", features = ["openssl", "rustls"] }
actix-test = { version = "0.1.0-beta.6", features = ["openssl", "rustls"] }
brotli2 = "0.3.2"
env_logger = "0.8"

View File

@ -3,9 +3,9 @@
> Async HTTP and WebSocket client library.
[![crates.io](https://img.shields.io/crates/v/awc?label=latest)](https://crates.io/crates/awc)
[![Documentation](https://docs.rs/awc/badge.svg?version=3.0.0-beta.9)](https://docs.rs/awc/3.0.0-beta.9)
[![Documentation](https://docs.rs/awc/badge.svg?version=3.0.0-beta.10)](https://docs.rs/awc/3.0.0-beta.10)
![MIT or Apache 2.0 licensed](https://img.shields.io/crates/l/awc)
[![Dependency Status](https://deps.rs/crate/awc/3.0.0-beta.9/status.svg)](https://deps.rs/crate/awc/3.0.0-beta.9)
[![Dependency Status](https://deps.rs/crate/awc/3.0.0-beta.10/status.svg)](https://deps.rs/crate/awc/3.0.0-beta.10)
[![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x)
## Documentation & Resources

View File

@ -4,13 +4,11 @@ use std::net::IpAddr;
use std::rc::Rc;
use std::time::Duration;
use actix_http::{
client::{Connector, ConnectorService, TcpConnect, TcpConnectError, TcpConnection},
http::{self, header, Error as HttpError, HeaderMap, HeaderName, Uri},
};
use actix_http::http::{self, header, Error as HttpError, HeaderMap, HeaderName, Uri};
use actix_rt::net::{ActixStream, TcpStream};
use actix_service::{boxed, Service};
use crate::client::{Connector, ConnectorService, TcpConnect, TcpConnectError, TcpConnection};
use crate::connect::DefaultConnector;
use crate::error::SendRequestError;
use crate::middleware::{NestTransform, Redirect, Transform};

View File

@ -1,5 +1,4 @@
use std::net::IpAddr;
use std::time::Duration;
use std::{net::IpAddr, time::Duration};
const DEFAULT_H2_CONN_WINDOW: u32 = 1024 * 1024 * 2; // 2MB
const DEFAULT_H2_STREAM_WINDOW: u32 = 1024 * 1024; // 1MB

View File

@ -12,10 +12,9 @@ use bytes::Bytes;
use futures_core::future::LocalBoxFuture;
use h2::client::SendRequest;
use crate::h1::ClientCodec;
use crate::message::{RequestHeadType, ResponseHead};
use crate::payload::Payload;
use crate::{body::MessageBody, Error};
use actix_http::{
body::MessageBody, h1::ClientCodec, Error, Payload, RequestHeadType, ResponseHead,
};
use super::error::SendRequestError;
use super::pool::Acquired;
@ -174,6 +173,7 @@ impl H2ConnectionInner {
/// Cancel spawned connection task on drop.
impl Drop for H2ConnectionInner {
fn drop(&mut self) {
// TODO: this can end up sending extraneous requests; see if there is a better way to handle
if self
.sender
.send_request(http::Request::new(()), true)
@ -184,8 +184,8 @@ impl Drop for H2ConnectionInner {
}
}
/// Unified connection type cover HTTP/1 Plain/TLS and HTTP/2 protocols.
#[allow(dead_code)]
/// Unified connection type cover Http1 Plain/Tls and Http2 protocols
pub enum Connection<A, B = Box<dyn ConnectionIo>>
where
A: ConnectionIo,
@ -219,11 +219,7 @@ impl<Io: ConnectionIo> ConnectionType<Io> {
}
}
pub(super) fn from_h1(
io: Io,
created: time::Instant,
acquired: Acquired<Io>,
) -> Self {
pub(super) fn from_h1(io: Io, created: time::Instant, acquired: Acquired<Io>) -> Self {
Self::H1(H1Connection {
io: Some(io),
created,
@ -271,9 +267,7 @@ where
Connection::Tls(ConnectionType::H2(conn)) => {
h2proto::send_request(conn, head.into(), body).await
}
_ => unreachable!(
"Plain Tcp connection can be used only in Http1 protocol"
),
_ => unreachable!("Plain Tcp connection can be used only in Http1 protocol"),
}
})
}
@ -301,9 +295,7 @@ where
Err(SendRequestError::TunnelNotSupported)
}
Connection::Tcp(ConnectionType::H2(_)) => {
unreachable!(
"Plain Tcp connection can be used only in Http1 protocol"
)
unreachable!("Plain Tcp connection can be used only in Http1 protocol")
}
}
})
@ -321,12 +313,8 @@ where
buf: &mut ReadBuf<'_>,
) -> Poll<io::Result<()>> {
match self.get_mut() {
Connection::Tcp(ConnectionType::H1(conn)) => {
Pin::new(conn).poll_read(cx, buf)
}
Connection::Tls(ConnectionType::H1(conn)) => {
Pin::new(conn).poll_read(cx, buf)
}
Connection::Tcp(ConnectionType::H1(conn)) => Pin::new(conn).poll_read(cx, buf),
Connection::Tls(ConnectionType::H1(conn)) => Pin::new(conn).poll_read(cx, buf),
_ => unreachable!("H2Connection can not impl AsyncRead trait"),
}
}
@ -345,12 +333,8 @@ where
buf: &[u8],
) -> Poll<io::Result<usize>> {
match self.get_mut() {
Connection::Tcp(ConnectionType::H1(conn)) => {
Pin::new(conn).poll_write(cx, buf)
}
Connection::Tls(ConnectionType::H1(conn)) => {
Pin::new(conn).poll_write(cx, buf)
}
Connection::Tcp(ConnectionType::H1(conn)) => Pin::new(conn).poll_write(cx, buf),
Connection::Tls(ConnectionType::H1(conn)) => Pin::new(conn).poll_write(cx, buf),
_ => unreachable!(H2_UNREACHABLE_WRITE),
}
}
@ -363,17 +347,10 @@ where
}
}
fn poll_shutdown(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<io::Result<()>> {
fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
match self.get_mut() {
Connection::Tcp(ConnectionType::H1(conn)) => {
Pin::new(conn).poll_shutdown(cx)
}
Connection::Tls(ConnectionType::H1(conn)) => {
Pin::new(conn).poll_shutdown(cx)
}
Connection::Tcp(ConnectionType::H1(conn)) => Pin::new(conn).poll_shutdown(cx),
Connection::Tls(ConnectionType::H1(conn)) => Pin::new(conn).poll_shutdown(cx),
_ => unreachable!(H2_UNREACHABLE_WRITE),
}
}

View File

@ -8,6 +8,7 @@ use std::{
time::Duration,
};
use actix_http::Protocol;
use actix_rt::{
net::{ActixStream, TcpStream},
time::{sleep, Sleep},
@ -19,14 +20,13 @@ use actix_tls::connect::{
};
use futures_core::{future::LocalBoxFuture, ready};
use http::Uri;
use pin_project::pin_project;
use pin_project_lite::pin_project;
use super::config::ConnectorConfig;
use super::connection::{Connection, ConnectionIo};
use super::error::ConnectError;
use super::pool::ConnectionPool;
use super::Connect;
use super::Protocol;
enum SslConnector {
#[allow(dead_code)]
@ -99,9 +99,7 @@ impl Connector<()> {
/// Build TLS connector with openssl, based on supplied ALPN protocols
#[cfg(all(feature = "openssl", not(feature = "rustls")))]
fn build_ssl(protocols: Vec<Vec<u8>>) -> SslConnector {
use actix_tls::connect::tls::openssl::{
SslConnector as OpensslConnector, SslMethod,
};
use actix_tls::connect::tls::openssl::{SslConnector as OpensslConnector, SslMethod};
use bytes::{BufMut, BytesMut};
let mut alpn = BytesMut::with_capacity(20);
@ -112,7 +110,7 @@ impl Connector<()> {
let mut ssl = OpensslConnector::builder(SslMethod::tls()).unwrap();
if let Err(err) = ssl.set_alpn_protos(&alpn) {
error!("Can not set ALPN protocol: {:?}", err);
log::error!("Can not set ALPN protocol: {:?}", err);
}
SslConnector::Openssl(ssl.build())
@ -148,11 +146,8 @@ where
// This remap is to hide ActixStream's trait methods. They are not meant to be called
// from user code.
Io: ActixStream + fmt::Debug + 'static,
S: Service<
TcpConnect<Uri>,
Response = TcpConnection<Uri, Io>,
Error = TcpConnectError,
> + Clone
S: Service<TcpConnect<Uri>, Response = TcpConnection<Uri, Io>, Error = TcpConnectError>
+ Clone
+ 'static,
{
/// Tcp connection timeout, i.e. max time to connect to remote host including dns name
@ -171,10 +166,7 @@ where
#[cfg(feature = "openssl")]
/// Use custom `SslConnector` instance.
pub fn ssl(
mut self,
connector: actix_tls::connect::ssl::openssl::SslConnector,
) -> Self {
pub fn ssl(mut self, connector: actix_tls::connect::ssl::openssl::SslConnector) -> Self {
self.ssl = SslConnector::Openssl(connector);
self
}
@ -328,10 +320,11 @@ where
impl<Io: ConnectionIo> IntoConnectionIo for TcpConnection<Uri, TlsStream<Io>> {
fn into_connection_io(self) -> (Box<dyn ConnectionIo>, Protocol) {
let sock = self.into_parts().0;
let h2 =
sock.get_ref().1.alpn_protocol().map_or(false, |protos| {
protos.windows(2).any(|w| w == H2)
});
let h2 = sock
.get_ref()
.1
.alpn_protocol()
.map_or(false, |protos| protos.windows(2).any(|w| w == H2));
if h2 {
(Box::new(sock), Protocol::Http2)
} else {
@ -357,8 +350,8 @@ where
let tcp_pool = ConnectionPool::new(tcp_service, tcp_config);
let tls_config = self.config;
let tls_pool = tls_service
.map(move |tls_service| ConnectionPool::new(tls_service, tls_config));
let tls_pool =
tls_service.map(move |tls_service| ConnectionPool::new(tls_service, tls_config));
ConnectorServicePriv { tcp_pool, tls_pool }
}
@ -389,10 +382,12 @@ where
}
}
#[pin_project]
pub struct TcpConnectorFuture<Fut> {
#[pin]
fut: Fut,
pin_project! {
#[project = TcpConnectorFutureProj]
pub struct TcpConnectorFuture<Fut> {
#[pin]
fut: Fut,
}
}
impl<Fut, Io> Future for TcpConnectorFuture<Fut>
@ -451,23 +446,25 @@ where
}
}
#[pin_project(project = TlsConnectorProj)]
#[allow(clippy::large_enum_variant)]
enum TlsConnectorFuture<S, Fut1, Fut2> {
TcpConnect {
#[pin]
fut: Fut1,
tls_service: Option<S>,
timeout: Duration,
},
TlsConnect {
#[pin]
fut: Fut2,
#[pin]
timeout: Sleep,
},
}
pin_project! {
#[project = TlsConnectorProj]
#[allow(clippy::large_enum_variant)]
enum TlsConnectorFuture<S, Fut1, Fut2> {
TcpConnect {
#[pin]
fut: Fut1,
tls_service: Option<S>,
timeout: Duration,
},
TlsConnect {
#[pin]
fut: Fut2,
#[pin]
timeout: Sleep,
},
}
}
/// helper trait for generic over different TlsStream types between tls crates.
trait IntoConnectionIo {
fn into_connection_io(self) -> (Box<dyn ConnectionIo>, Protocol);
@ -475,12 +472,7 @@ trait IntoConnectionIo {
impl<S, Io, Fut1, Fut2, Res> Future for TlsConnectorFuture<S, Fut1, Fut2>
where
S: Service<
TcpConnection<Uri, Io>,
Response = Res,
Error = std::io::Error,
Future = Fut2,
>,
S: Service<TcpConnection<Uri, Io>, Response = Res, Error = std::io::Error, Future = Fut2>,
S::Response: IntoConnectionIo,
Fut1: Future<Output = Result<TcpConnection<Uri, Io>, ConnectError>>,
Fut2: Future<Output = Result<S::Response, S::Error>>,
@ -522,11 +514,7 @@ pub struct TcpConnectorInnerService<S: Clone> {
}
impl<S: Clone> TcpConnectorInnerService<S> {
fn new(
service: S,
timeout: Duration,
local_address: Option<std::net::IpAddr>,
) -> Self {
fn new(service: S, timeout: Duration, local_address: Option<std::net::IpAddr>) -> Self {
Self {
service,
timeout,
@ -537,11 +525,8 @@ impl<S: Clone> TcpConnectorInnerService<S> {
impl<S, Io> Service<Connect> for TcpConnectorInnerService<S>
where
S: Service<
TcpConnect<Uri>,
Response = TcpConnection<Uri, Io>,
Error = TcpConnectError,
> + Clone
S: Service<TcpConnect<Uri>, Response = TcpConnection<Uri, Io>, Error = TcpConnectError>
+ Clone
+ 'static,
{
type Response = S::Response;
@ -564,12 +549,14 @@ where
}
}
#[pin_project]
pub struct TcpConnectorInnerFuture<Fut> {
#[pin]
fut: Fut,
#[pin]
timeout: Sleep,
pin_project! {
#[project = TcpConnectorInnerFutureProj]
pub struct TcpConnectorInnerFuture<Fut> {
#[pin]
fut: Fut,
#[pin]
timeout: Sleep,
}
}
impl<Fut, Io> Future for TcpConnectorInnerFuture<Fut>
@ -618,12 +605,8 @@ where
impl<S1, S2, Io1, Io2> Service<Connect> for ConnectorServicePriv<S1, S2, Io1, Io2>
where
S1: Service<Connect, Response = (Io1, Protocol), Error = ConnectError>
+ Clone
+ 'static,
S2: Service<Connect, Response = (Io2, Protocol), Error = ConnectError>
+ Clone
+ 'static,
S1: Service<Connect, Response = (Io1, Protocol), Error = ConnectError> + Clone + 'static,
S2: Service<Connect, Response = (Io2, Protocol), Error = ConnectError> + Clone + 'static,
Io1: ConnectionIo,
Io2: ConnectionIo,
{
@ -643,38 +626,46 @@ where
match req.uri.scheme_str() {
Some("https") | Some("wss") => match self.tls_pool {
None => ConnectorServiceFuture::SslIsNotSupported,
Some(ref pool) => ConnectorServiceFuture::Tls(pool.call(req)),
Some(ref pool) => ConnectorServiceFuture::Tls {
fut: pool.call(req),
},
},
_ => ConnectorServiceFuture::Tcp {
fut: self.tcp_pool.call(req),
},
_ => ConnectorServiceFuture::Tcp(self.tcp_pool.call(req)),
}
}
}
#[pin_project(project = ConnectorServiceProj)]
pub enum ConnectorServiceFuture<S1, S2, Io1, Io2>
where
S1: Service<Connect, Response = (Io1, Protocol), Error = ConnectError>
+ Clone
+ 'static,
S2: Service<Connect, Response = (Io2, Protocol), Error = ConnectError>
+ Clone
+ 'static,
Io1: ConnectionIo,
Io2: ConnectionIo,
{
Tcp(#[pin] <ConnectionPool<S1, Io1> as Service<Connect>>::Future),
Tls(#[pin] <ConnectionPool<S2, Io2> as Service<Connect>>::Future),
SslIsNotSupported,
pin_project! {
#[project = ConnectorServiceFutureProj]
pub enum ConnectorServiceFuture<S1, S2, Io1, Io2>
where
S1: Service<Connect, Response = (Io1, Protocol), Error = ConnectError>,
S1: Clone,
S1: 'static,
S2: Service<Connect, Response = (Io2, Protocol), Error = ConnectError>,
S2: Clone,
S2: 'static,
Io1: ConnectionIo,
Io2: ConnectionIo,
{
Tcp {
#[pin]
fut: <ConnectionPool<S1, Io1> as Service<Connect>>::Future
},
Tls {
#[pin]
fut: <ConnectionPool<S2, Io2> as Service<Connect>>::Future
},
SslIsNotSupported
}
}
impl<S1, S2, Io1, Io2> Future for ConnectorServiceFuture<S1, S2, Io1, Io2>
where
S1: Service<Connect, Response = (Io1, Protocol), Error = ConnectError>
+ Clone
+ 'static,
S2: Service<Connect, Response = (Io2, Protocol), Error = ConnectError>
+ Clone
+ 'static,
S1: Service<Connect, Response = (Io1, Protocol), Error = ConnectError> + Clone + 'static,
S2: Service<Connect, Response = (Io2, Protocol), Error = ConnectError> + Clone + 'static,
Io1: ConnectionIo,
Io2: ConnectionIo,
{
@ -682,9 +673,9 @@ where
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
match self.project() {
ConnectorServiceProj::Tcp(fut) => fut.poll(cx).map_ok(Connection::Tcp),
ConnectorServiceProj::Tls(fut) => fut.poll(cx).map_ok(Connection::Tls),
ConnectorServiceProj::SslIsNotSupported => {
ConnectorServiceFutureProj::Tcp { fut } => fut.poll(cx).map_ok(Connection::Tcp),
ConnectorServiceFutureProj::Tls { fut } => fut.poll(cx).map_ok(Connection::Tls),
ConnectorServiceFutureProj::SslIsNotSupported => {
Poll::Ready(Err(ConnectError::SslIsNotSupported))
}
}

View File

@ -2,12 +2,13 @@ use std::{error::Error as StdError, fmt, io};
use derive_more::{Display, From};
use actix_http::{
error::{Error, ParseError},
http::Error as HttpError,
};
#[cfg(feature = "openssl")]
use actix_tls::accept::openssl::SslError;
use crate::error::{Error, ParseError};
use crate::http::Error as HttpError;
/// A set of errors that can occur while connecting to an HTTP host
#[derive(Debug, Display, From)]
#[non_exhaustive]

View File

@ -5,24 +5,25 @@ use std::{
};
use actix_codec::Framed;
use actix_http::{
body::{BodySize, MessageBody},
error::PayloadError,
h1,
http::{
header::{HeaderMap, IntoHeaderValue, EXPECT, HOST},
StatusCode,
},
Error, Payload, RequestHeadType, ResponseHead,
};
use actix_utils::future::poll_fn;
use bytes::buf::BufMut;
use bytes::{Bytes, BytesMut};
use futures_core::{ready, Stream};
use futures_util::SinkExt as _;
use crate::h1;
use crate::http::{
header::{HeaderMap, IntoHeaderValue, EXPECT, HOST},
StatusCode,
};
use crate::message::{RequestHeadType, ResponseHead};
use crate::payload::Payload;
use crate::{error::PayloadError, Error};
use pin_project_lite::pin_project;
use super::connection::{ConnectionIo, H1Connection};
use super::error::{ConnectError, SendRequestError};
use crate::body::{BodySize, MessageBody};
pub(crate) async fn send_request<Io, B>(
io: H1Connection<Io>,
@ -194,10 +195,11 @@ where
Ok(())
}
#[pin_project::pin_project]
pub(crate) struct PlStream<Io: ConnectionIo> {
#[pin]
framed: Framed<H1Connection<Io>, h1::ClientPayloadCodec>,
pin_project! {
pub(crate) struct PlStream<Io: ConnectionIo> {
#[pin]
framed: Framed<H1Connection<Io>, h1::ClientPayloadCodec>,
}
}
impl<Io: ConnectionIo> PlStream<Io> {
@ -211,10 +213,7 @@ impl<Io: ConnectionIo> PlStream<Io> {
impl<Io: ConnectionIo> Stream for PlStream<Io> {
type Item = Result<Bytes, PayloadError>;
fn poll_next(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Option<Self::Item>> {
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let mut this = self.project();
match ready!(this.framed.as_mut().next_item(cx)?) {

View File

@ -8,13 +8,12 @@ use h2::{
};
use http::header::{HeaderValue, CONNECTION, CONTENT_LENGTH, TRANSFER_ENCODING};
use http::{request::Request, Method, Version};
use log::trace;
use crate::{
use actix_http::{
body::{BodySize, MessageBody},
header::HeaderMap,
message::{RequestHeadType, ResponseHead},
payload::Payload,
Error,
Error, Payload, RequestHeadType, ResponseHead,
};
use super::{
@ -131,10 +130,7 @@ where
Ok((head, payload))
}
async fn send_body<B>(
body: B,
mut send: SendStream<Bytes>,
) -> Result<(), SendRequestError>
async fn send_body<B>(body: B, mut send: SendStream<Bytes>) -> Result<(), SendRequestError>
where
B: MessageBody,
B::Error: Into<Error>,
@ -184,8 +180,7 @@ where
pub(crate) fn handshake<Io: ConnectionIo>(
io: Io,
config: &ConnectorConfig,
) -> impl Future<Output = Result<(SendRequest<Bytes>, Connection<Io, Bytes>), h2::Error>>
{
) -> impl Future<Output = Result<(SendRequest<Bytes>, Connection<Io, Bytes>), h2::Error>> {
let mut builder = Builder::new();
builder
.initial_window_size(config.stream_window_size)

View File

@ -17,7 +17,6 @@ pub use actix_tls::connect::{
pub use self::connection::{Connection, ConnectionIo};
pub use self::connector::{Connector, ConnectorService};
pub use self::error::{ConnectError, FreezeRequestError, InvalidUrl, SendRequestError};
pub use crate::Protocol;
#[derive(Clone)]
pub struct Connect {

View File

@ -14,22 +14,21 @@ use std::{
};
use actix_codec::{AsyncRead, AsyncWrite, ReadBuf};
use actix_http::Protocol;
use actix_rt::time::{sleep, Sleep};
use actix_service::Service;
use ahash::AHashMap;
use futures_core::future::LocalBoxFuture;
use futures_util::FutureExt;
use http::uri::Authority;
use pin_project::pin_project;
use pin_project_lite::pin_project;
use tokio::sync::{OwnedSemaphorePermit, Semaphore};
use super::config::ConnectorConfig;
use super::connection::{
ConnectionInnerType, ConnectionIo, ConnectionType, H2ConnectionInner,
};
use super::connection::{ConnectionInnerType, ConnectionIo, ConnectionType, H2ConnectionInner};
use super::error::ConnectError;
use super::h2proto::handshake;
use super::Connect;
use super::Protocol;
#[derive(Hash, Eq, PartialEq, Clone, Debug)]
pub struct Key {
@ -152,9 +151,7 @@ where
impl<S, Io> Service<Connect> for ConnectionPool<S, Io>
where
S: Service<Connect, Response = (Io, Protocol), Error = ConnectError>
+ Clone
+ 'static,
S: Service<Connect, Response = (Io, Protocol), Error = ConnectError> + Clone + 'static,
Io: ConnectionIo,
{
type Response = ConnectionType<Io>;
@ -195,8 +192,8 @@ where
let config = &inner.config;
let idle_dur = now - c.used;
let age = now - c.created;
let conn_ineligible = idle_dur > config.conn_keep_alive
|| age > config.conn_lifetime;
let conn_ineligible =
idle_dur > config.conn_keep_alive || age > config.conn_lifetime;
if conn_ineligible {
// drop connections that are too old
@ -205,7 +202,7 @@ where
// check if the connection is still usable
if let ConnectionInnerType::H1(ref mut io) = c.conn {
let check = ConnectionCheckFuture { io };
match check.await {
match check.now_or_never().expect("ConnectionCheckFuture must never yield with Poll::Pending.") {
ConnectionState::Tainted => {
inner.close(c.conn);
continue;
@ -231,9 +228,7 @@ where
// match the connection and spawn new one if did not get anything.
match conn {
Some(conn) => {
Ok(ConnectionType::from_pool(conn.conn, conn.created, acquired))
}
Some(conn) => Ok(ConnectionType::from_pool(conn.conn, conn.created, acquired)),
None => {
let (io, proto) = connector.call(req).await?;
@ -284,9 +279,7 @@ where
let mut read_buf = ReadBuf::new(&mut buf);
let state = match Pin::new(&mut this.io).poll_read(cx, &mut read_buf) {
Poll::Ready(Ok(())) if !read_buf.filled().is_empty() => {
ConnectionState::Tainted
}
Poll::Ready(Ok(())) if !read_buf.filled().is_empty() => ConnectionState::Tainted,
Poll::Pending => ConnectionState::Live,
_ => ConnectionState::Skip,
@ -302,11 +295,13 @@ struct PooledConnection<Io> {
created: Instant,
}
#[pin_project]
struct CloseConnection<Io> {
io: Io,
#[pin]
timeout: Sleep,
pin_project! {
#[project = CloseConnectionProj]
struct CloseConnection<Io> {
io: Io,
#[pin]
timeout: Sleep,
}
}
impl<Io> CloseConnection<Io>
@ -413,17 +408,11 @@ mod test {
unimplemented!()
}
fn poll_flush(
self: Pin<&mut Self>,
_: &mut Context<'_>,
) -> Poll<io::Result<()>> {
fn poll_flush(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<io::Result<()>> {
unimplemented!()
}
fn poll_shutdown(
self: Pin<&mut Self>,
_: &mut Context<'_>,
) -> Poll<io::Result<()>> {
fn poll_shutdown(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<io::Result<()>> {
Poll::Ready(Ok(()))
}
}

View File

@ -8,16 +8,14 @@ use std::{
use actix_codec::Framed;
use actix_http::{
body::Body,
client::{
Connect as ClientConnect, ConnectError, Connection, ConnectionIo, SendRequestError,
},
h1::ClientCodec,
Payload, RequestHead, RequestHeadType, ResponseHead,
body::Body, h1::ClientCodec, Payload, RequestHead, RequestHeadType, ResponseHead,
};
use actix_service::Service;
use futures_core::{future::LocalBoxFuture, ready};
use crate::client::{
Connect as ClientConnect, ConnectError, Connection, ConnectionIo, SendRequestError,
};
use crate::response::ClientResponse;
pub type BoxConnectorService = Rc<

View File

@ -1,15 +1,15 @@
//! HTTP client errors
pub use actix_http::client::{ConnectError, FreezeRequestError, InvalidUrl, SendRequestError};
pub use actix_http::error::PayloadError;
pub use actix_http::http::Error as HttpError;
pub use actix_http::ws::HandshakeError as WsHandshakeError;
pub use actix_http::ws::ProtocolError as WsProtocolError;
pub use actix_http::{
error::PayloadError,
http::{header::HeaderValue, Error as HttpError, StatusCode},
ws::{HandshakeError as WsHandshakeError, ProtocolError as WsProtocolError},
};
use derive_more::{Display, From};
use serde_json::error::Error as JsonError;
use actix_http::http::{header::HeaderValue, StatusCode};
use derive_more::{Display, From};
pub use crate::client::{ConnectError, FreezeRequestError, InvalidUrl, SendRequestError};
/// Websocket client error
#[derive(Debug, Display, From)]

View File

@ -104,22 +104,8 @@
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
use std::{convert::TryFrom, rc::Rc, time::Duration};
#[cfg(feature = "cookies")]
pub use cookie;
pub use actix_http::{client::Connector, http};
use actix_http::{
client::{TcpConnect, TcpConnectError, TcpConnection},
http::{Error as HttpError, HeaderMap, Method, Uri},
RequestHead,
};
use actix_rt::net::TcpStream;
use actix_service::Service;
mod builder;
mod client;
mod connect;
pub mod error;
mod frozen;
@ -130,13 +116,29 @@ mod sender;
pub mod test;
pub mod ws;
pub use actix_http::http;
#[cfg(feature = "cookies")]
pub use cookie;
pub use self::builder::ClientBuilder;
pub use self::client::Connector;
pub use self::connect::{BoxConnectorService, BoxedSocket, ConnectRequest, ConnectResponse};
pub use self::frozen::{FrozenClientRequest, FrozenSendBuilder};
pub use self::request::ClientRequest;
pub use self::response::{ClientResponse, JsonBody, MessageBody};
pub use self::sender::SendClientRequest;
use std::{convert::TryFrom, rc::Rc, time::Duration};
use actix_http::{
http::{Error as HttpError, HeaderMap, Method, Uri},
RequestHead,
};
use actix_rt::net::TcpStream;
use actix_service::Service;
use self::client::{TcpConnect, TcpConnectError, TcpConnection};
/// An asynchronous HTTP and WebSocket client.
///
/// You should take care to create, at most, one `Client` per thread. Otherwise, expect higher CPU

View File

@ -9,7 +9,6 @@ use std::{
use actix_http::{
body::Body,
client::{InvalidUrl, SendRequestError},
http::{header, Method, StatusCode, Uri},
RequestHead, RequestHeadType,
};
@ -19,6 +18,7 @@ use futures_core::ready;
use super::Transform;
use crate::client::{InvalidUrl, SendRequestError};
use crate::connect::{ConnectRequest, ConnectResponse};
use crate::ClientResponse;

View File

@ -795,17 +795,15 @@ async fn client_unread_response() {
let lst = std::net::TcpListener::bind(addr).unwrap();
std::thread::spawn(move || {
for stream in lst.incoming() {
let mut stream = stream.unwrap();
let mut b = [0; 1000];
let _ = stream.read(&mut b).unwrap();
let _ = stream.write_all(
b"HTTP/1.1 200 OK\r\n\
let (mut stream, _) = lst.accept().unwrap();
let mut b = [0; 1000];
let _ = stream.read(&mut b).unwrap();
let _ = stream.write_all(
b"HTTP/1.1 200 OK\r\n\
connection: close\r\n\
\r\n\
welcome!",
);
}
);
});
// client request

View File

@ -19,8 +19,7 @@ use actix_utils::future::ok;
use actix_web::{dev::AppConfig, http::Version, web, App, HttpResponse};
use rustls::{
client::{ServerCertVerified, ServerCertVerifier},
Certificate, ClientConfig, OwnedTrustAnchor, PrivateKey, RootCertStore, ServerConfig,
ServerName,
Certificate, ClientConfig, PrivateKey, ServerConfig, ServerName,
};
use rustls_pemfile::{certs, pkcs8_private_keys};

View File

@ -8,6 +8,7 @@ use std::{any::Any, io, net::SocketAddr};
use actix_web::{dev::Extensions, rt::net::TcpStream, web, App, HttpServer};
#[allow(dead_code)]
#[derive(Debug, Clone)]
struct ConnectionInfo {
bind: SocketAddr,

View File

@ -137,7 +137,7 @@ impl<T: ?Sized + 'static> FromRequest for Data<T> {
type_name::<T>(),
);
err(ErrorInternalServerError(
"App data is not configured, to configure use App::data()",
"App data is not configured, to configure construct it with web::Data::new() and pass it to App::app_data()",
))
}
}

View File

@ -20,7 +20,7 @@ pub use actix_http::body::{AnyBody, Body, BodySize, MessageBody, ResponseBody, S
pub use actix_http::encoding::Decoder as Decompress;
pub use actix_http::{Extensions, Payload, PayloadStream, RequestHead, Response, ResponseHead};
pub use actix_router::{Path, ResourceDef, ResourcePath, Url};
pub use actix_server::Server;
pub use actix_server::{Server, ServerHandle};
pub use actix_service::{
always_ready, fn_factory, fn_service, forward_ready, Service, ServiceFactory, Transform,
};

View File

@ -60,52 +60,53 @@ crate::http::header::common_header! {
}
impl ContentType {
/// A constructor to easily create a `Content-Type: application/json`
/// A constructor to easily create a `Content-Type: application/json`
/// header.
#[inline]
pub fn json() -> ContentType {
ContentType(mime::APPLICATION_JSON)
}
/// A constructor to easily create a `Content-Type: text/plain;
/// A constructor to easily create a `Content-Type: text/plain;
/// charset=utf-8` header.
#[inline]
pub fn plaintext() -> ContentType {
ContentType(mime::TEXT_PLAIN_UTF_8)
}
/// A constructor to easily create a `Content-Type: text/html` header.
/// A constructor to easily create a `Content-Type: text/html; charset=utf-8`
/// header.
#[inline]
pub fn html() -> ContentType {
ContentType(mime::TEXT_HTML)
ContentType(mime::TEXT_HTML_UTF_8)
}
/// A constructor to easily create a `Content-Type: text/xml` header.
/// A constructor to easily create a `Content-Type: text/xml` header.
#[inline]
pub fn xml() -> ContentType {
ContentType(mime::TEXT_XML)
}
/// A constructor to easily create a `Content-Type:
/// A constructor to easily create a `Content-Type:
/// application/www-form-url-encoded` header.
#[inline]
pub fn form_url_encoded() -> ContentType {
ContentType(mime::APPLICATION_WWW_FORM_URLENCODED)
}
/// A constructor to easily create a `Content-Type: image/jpeg` header.
/// A constructor to easily create a `Content-Type: image/jpeg` header.
#[inline]
pub fn jpeg() -> ContentType {
ContentType(mime::IMAGE_JPEG)
}
/// A constructor to easily create a `Content-Type: image/png` header.
/// A constructor to easily create a `Content-Type: image/png` header.
#[inline]
pub fn png() -> ContentType {
ContentType(mime::IMAGE_PNG)
}
/// A constructor to easily create a `Content-Type:
/// A constructor to easily create a `Content-Type:
/// application/octet-stream` header.
#[inline]
pub fn octet_stream() -> ContentType {

View File

@ -22,7 +22,7 @@ use time::{format_description::well_known::Rfc3339, OffsetDateTime};
use crate::{
dev::{BodySize, MessageBody},
http::{HeaderName, StatusCode},
http::HeaderName,
service::{ServiceRequest, ServiceResponse},
Error, HttpResponse, Result,
};
@ -275,9 +275,7 @@ where
};
if let Some(error) = res.response().error() {
if res.response().head().status != StatusCode::INTERNAL_SERVER_ERROR {
debug!("Error in response: {:?}", error);
}
debug!("Error in response: {:?}", error);
}
if let Some(ref mut format) = this.format {

View File

@ -159,7 +159,7 @@ where
///
/// By default max connections is set to a 25k.
pub fn max_connections(mut self, num: usize) -> Self {
self.builder = self.builder.maxconn(num);
self.builder = self.builder.max_concurrent_connections(num);
self
}
@ -233,7 +233,7 @@ where
self
}
/// Stop actix system.
/// Stop Actix `System` after server shutdown.
pub fn system_exit(mut self) -> Self {
self.builder = self.builder.system_exit();
self

View File

@ -14,57 +14,45 @@ async fn test_start() {
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
let sys = actix_rt::System::new();
actix_rt::System::new()
.block_on(async {
let srv = HttpServer::new(|| {
App::new().service(
web::resource("/").route(web::to(|| HttpResponse::Ok().body("test"))),
)
})
.workers(1)
.backlog(1)
.max_connections(10)
.max_connection_rate(10)
.keep_alive(10)
.client_timeout(5000)
.client_shutdown(0)
.server_hostname("localhost")
.system_exit()
.disable_signals()
.bind(format!("{}", addr))
.unwrap()
.run();
sys.block_on(async {
let srv = HttpServer::new(|| {
App::new().service(
web::resource("/").route(web::to(|| HttpResponse::Ok().body("test"))),
)
tx.send(srv.handle()).unwrap();
srv.await
})
.workers(1)
.backlog(1)
.max_connections(10)
.max_connection_rate(10)
.keep_alive(10)
.client_timeout(5000)
.client_shutdown(0)
.server_hostname("localhost")
.system_exit()
.disable_signals()
.bind(format!("{}", addr))
.unwrap()
.run();
let _ = tx.send((srv, actix_rt::System::current()));
});
let _ = sys.run();
.unwrap();
});
let (srv, sys) = rx.recv().unwrap();
#[cfg(feature = "client")]
{
use actix_http::client;
let srv = rx.recv().unwrap();
let client = awc::Client::builder()
.connector(
client::Connector::new()
.timeout(Duration::from_millis(100))
.finish(),
)
.finish();
let client = awc::Client::builder()
.connector(awc::Connector::new().timeout(Duration::from_millis(100)))
.finish();
let host = format!("http://{}", addr);
let response = client.get(host.clone()).send().await.unwrap();
assert!(response.status().is_success());
}
let host = format!("http://{}", addr);
let response = client.get(host.clone()).send().await.unwrap();
assert!(response.status().is_success());
// stop
let _ = srv.stop(false);
thread::sleep(Duration::from_millis(100));
let _ = sys.stop();
srv.stop(false).await;
}
#[cfg(feature = "openssl")]
@ -92,37 +80,38 @@ fn ssl_acceptor() -> openssl::ssl::SslAcceptorBuilder {
#[cfg(feature = "openssl")]
async fn test_start_ssl() {
use actix_web::HttpRequest;
use openssl::ssl::{SslConnector, SslMethod, SslVerifyMode};
let addr = actix_test::unused_addr();
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
let sys = actix_rt::System::new();
let builder = ssl_acceptor();
actix_rt::System::new()
.block_on(async {
let builder = ssl_acceptor();
let srv = HttpServer::new(|| {
App::new().service(web::resource("/").route(web::to(|req: HttpRequest| {
assert!(req.app_config().secure());
HttpResponse::Ok().body("test")
})))
})
.workers(1)
.shutdown_timeout(1)
.system_exit()
.disable_signals()
.bind_openssl(format!("{}", addr), builder)
.unwrap();
let srv = HttpServer::new(|| {
App::new().service(web::resource("/").route(web::to(|req: HttpRequest| {
assert!(req.app_config().secure());
HttpResponse::Ok().body("test")
})))
})
.workers(1)
.shutdown_timeout(1)
.system_exit()
.disable_signals()
.bind_openssl(format!("{}", addr), builder)
.unwrap();
sys.block_on(async {
let srv = srv.run();
let _ = tx.send((srv, actix_rt::System::current()));
});
let srv = srv.run();
tx.send(srv.handle()).unwrap();
let _ = sys.run();
srv.await
})
.unwrap()
});
let (srv, sys) = rx.recv().unwrap();
let srv = rx.recv().unwrap();
use openssl::ssl::{SslConnector, SslMethod, SslVerifyMode};
let mut builder = SslConnector::builder(SslMethod::tls()).unwrap();
builder.set_verify(SslVerifyMode::NONE);
let _ = builder
@ -141,9 +130,5 @@ async fn test_start_ssl() {
let response = client.get(host.clone()).send().await.unwrap();
assert!(response.status().is_success());
// stop
let _ = srv.stop(false);
thread::sleep(Duration::from_millis(100));
let _ = sys.stop();
srv.stop(false).await;
}

View File

@ -127,6 +127,8 @@ async fn test_body() {
// read response
let bytes = response.body().await.unwrap();
assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
srv.stop().await;
}
#[actix_rt::test]
@ -154,6 +156,8 @@ async fn test_body_gzip() {
let mut dec = Vec::new();
e.read_to_end(&mut dec).unwrap();
assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref()));
srv.stop().await;
}
#[actix_rt::test]
@ -181,6 +185,8 @@ async fn test_body_gzip2() {
let mut dec = Vec::new();
e.read_to_end(&mut dec).unwrap();
assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref()));
srv.stop().await;
}
#[actix_rt::test]
@ -241,6 +247,8 @@ async fn test_body_encoding_override() {
e.write_all(bytes.as_ref()).unwrap();
let dec = e.finish().unwrap();
assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref()));
srv.stop().await;
}
#[actix_rt::test]
@ -275,6 +283,8 @@ async fn test_body_gzip_large() {
let mut dec = Vec::new();
e.read_to_end(&mut dec).unwrap();
assert_eq!(Bytes::from(dec), Bytes::from(data));
srv.stop().await;
}
#[actix_rt::test]
@ -314,6 +324,8 @@ async fn test_body_gzip_large_random() {
e.read_to_end(&mut dec).unwrap();
assert_eq!(dec.len(), data.len());
assert_eq!(Bytes::from(dec), Bytes::from(data));
srv.stop().await;
}
#[actix_rt::test]
@ -348,6 +360,8 @@ async fn test_body_chunked_implicit() {
let mut dec = Vec::new();
e.read_to_end(&mut dec).unwrap();
assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref()));
srv.stop().await;
}
#[actix_rt::test]
@ -380,6 +394,8 @@ async fn test_body_br_streaming() {
let dec = e.finish().unwrap();
println!("T: {:?}", Bytes::copy_from_slice(&dec));
assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref()));
srv.stop().await;
}
#[actix_rt::test]
@ -401,6 +417,8 @@ async fn test_head_binary() {
// read response
let bytes = response.body().await.unwrap();
assert!(bytes.is_empty());
srv.stop().await;
}
#[actix_rt::test]
@ -420,6 +438,8 @@ async fn test_no_chunking() {
// read response
let bytes = response.body().await.unwrap();
assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
srv.stop().await;
}
#[actix_rt::test]
@ -447,6 +467,8 @@ async fn test_body_deflate() {
e.write_all(bytes.as_ref()).unwrap();
let dec = e.finish().unwrap();
assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref()));
srv.stop().await;
}
#[actix_rt::test]
@ -475,6 +497,8 @@ async fn test_body_brotli() {
e.write_all(bytes.as_ref()).unwrap();
let dec = e.finish().unwrap();
assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref()));
srv.stop().await;
}
#[actix_rt::test]
@ -503,6 +527,8 @@ async fn test_body_zstd() {
let mut dec = Vec::new();
e.read_to_end(&mut dec).unwrap();
assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref()));
srv.stop().await;
}
#[actix_rt::test]
@ -534,6 +560,8 @@ async fn test_body_zstd_streaming() {
let mut dec = Vec::new();
e.read_to_end(&mut dec).unwrap();
assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref()));
srv.stop().await;
}
#[actix_rt::test]
@ -559,6 +587,8 @@ async fn test_zstd_encoding() {
// read response
let bytes = response.body().await.unwrap();
assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
srv.stop().await;
}
#[actix_rt::test]
@ -594,6 +624,8 @@ async fn test_zstd_encoding_large() {
// read response
let bytes = response.body().limit(320_000).await.unwrap();
assert_eq!(bytes, Bytes::from(data));
srv.stop().await;
}
#[actix_rt::test]
@ -619,6 +651,8 @@ async fn test_encoding() {
// read response
let bytes = response.body().await.unwrap();
assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
srv.stop().await;
}
#[actix_rt::test]
@ -644,6 +678,8 @@ async fn test_gzip_encoding() {
// read response
let bytes = response.body().await.unwrap();
assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
srv.stop().await;
}
#[actix_rt::test]
@ -670,6 +706,8 @@ async fn test_gzip_encoding_large() {
// read response
let bytes = response.body().await.unwrap();
assert_eq!(bytes, Bytes::from(data));
srv.stop().await;
}
#[actix_rt::test]
@ -702,6 +740,8 @@ async fn test_reading_gzip_encoding_large_random() {
let bytes = response.body().await.unwrap();
assert_eq!(bytes.len(), data.len());
assert_eq!(bytes, Bytes::from(data));
srv.stop().await;
}
#[actix_rt::test]
@ -727,6 +767,8 @@ async fn test_reading_deflate_encoding() {
// read response
let bytes = response.body().await.unwrap();
assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
srv.stop().await;
}
#[actix_rt::test]
@ -753,6 +795,8 @@ async fn test_reading_deflate_encoding_large() {
// read response
let bytes = response.body().await.unwrap();
assert_eq!(bytes, Bytes::from(data));
srv.stop().await;
}
#[actix_rt::test]
@ -785,6 +829,8 @@ async fn test_reading_deflate_encoding_large_random() {
let bytes = response.body().await.unwrap();
assert_eq!(bytes.len(), data.len());
assert_eq!(bytes, Bytes::from(data));
srv.stop().await;
}
#[actix_rt::test]
@ -810,6 +856,8 @@ async fn test_brotli_encoding() {
// read response
let bytes = response.body().await.unwrap();
assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
srv.stop().await;
}
#[actix_rt::test]
@ -845,6 +893,8 @@ async fn test_brotli_encoding_large() {
// read response
let bytes = response.body().limit(320_000).await.unwrap();
assert_eq!(bytes, Bytes::from(data));
srv.stop().await;
}
#[cfg(feature = "openssl")]
@ -861,9 +911,9 @@ async fn test_brotli_encoding_large_openssl() {
});
// body
let mut e = BrotliEncoder::new(Vec::new(), 3);
e.write_all(data.as_ref()).unwrap();
let enc = e.finish().unwrap();
let mut enc = BrotliEncoder::new(Vec::new(), 3);
enc.write_all(data.as_ref()).unwrap();
let enc = enc.finish().unwrap();
// client request
let mut response = srv
@ -877,6 +927,8 @@ async fn test_brotli_encoding_large_openssl() {
// read response
let bytes = response.body().await.unwrap();
assert_eq!(bytes, Bytes::from(data));
srv.stop().await;
}
#[cfg(feature = "rustls")]
@ -944,6 +996,8 @@ mod plus_rustls {
let bytes = response.body().await.unwrap();
assert_eq!(bytes.len(), data.len());
assert_eq!(bytes, Bytes::from(data));
srv.stop().await;
}
}
@ -998,6 +1052,8 @@ async fn test_server_cookies() {
assert_eq!(cookies[0], second_cookie);
assert_eq!(cookies[1], first_cookie);
}
srv.stop().await;
}
#[actix_rt::test]
@ -1018,6 +1074,8 @@ async fn test_slow_request() {
let mut data = String::new();
let _ = stream.read_to_string(&mut data);
assert!(data.starts_with("HTTP/1.1 408 Request Timeout"));
srv.stop().await;
}
#[actix_rt::test]
@ -1030,6 +1088,8 @@ async fn test_normalize() {
let response = srv.get("/one/").send().await.unwrap();
assert!(response.status().is_success());
srv.stop().await
}
// allow deprecated App::data
@ -1099,4 +1159,6 @@ async fn test_accept_encoding_no_match() {
.unwrap();
assert_eq!(response.status().as_u16(), 406);
srv.stop().await;
}