1
0
Fork 0
mirror of https://github.com/actix/actix-web.git synced 2024-11-25 11:01:14 +00:00

migrate actix-web to std::future

This commit is contained in:
Nikolay Kim 2019-11-20 23:33:22 +06:00
parent d081e57316
commit 3127dd4db6
46 changed files with 4134 additions and 3720 deletions

View file

@ -1,6 +1,6 @@
[package] [package]
name = "actix-web" name = "actix-web"
version = "1.0.9" version = "2.0.0-alpha.1"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"] authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Actix web is a simple, pragmatic and extremely fast web framework for Rust." description = "Actix web is a simple, pragmatic and extremely fast web framework for Rust."
readme = "README.md" readme = "README.md"
@ -16,7 +16,7 @@ exclude = [".gitignore", ".travis.yml", ".cargo/config", "appveyor.yml"]
edition = "2018" edition = "2018"
[package.metadata.docs.rs] [package.metadata.docs.rs]
features = ["ssl", "brotli", "flate2-zlib", "secure-cookies", "client", "rust-tls", "uds"] features = ["openssl", "rustls", "brotli", "flate2-zlib", "secure-cookies", "client"]
[badges] [badges]
travis-ci = { repository = "actix/actix-web", branch = "master" } travis-ci = { repository = "actix/actix-web", branch = "master" }
@ -28,20 +28,19 @@ path = "src/lib.rs"
[workspace] [workspace]
members = [ members = [
# ".", ".",
# "awc", "awc",
# #"actix-http", "actix-http",
# "actix-cors", "actix-cors",
# "actix-files", "actix-files",
# "actix-framed", "actix-framed",
# "actix-session", "actix-session",
# "actix-identity", "actix-identity",
# "actix-multipart", "actix-multipart",
# "actix-web-actors", "actix-web-actors",
# "actix-web-codegen", "actix-web-codegen",
# "test-server", "test-server",
] ]
exclude = ["awc", "actix-http", "test-server"]
[features] [features]
default = ["brotli", "flate2-zlib", "client", "fail"] default = ["brotli", "flate2-zlib", "client", "fail"]
@ -64,37 +63,35 @@ secure-cookies = ["actix-http/secure-cookies"]
fail = ["actix-http/fail"] fail = ["actix-http/fail"]
# openssl # openssl
ssl = ["openssl", "actix-server/ssl", "awc/ssl"] openssl = ["open-ssl", "actix-server/openssl", "awc/openssl"]
# rustls # rustls
rust-tls = ["rustls", "actix-server/rust-tls", "awc/rust-tls"] rustls = ["rust-tls", "actix-server/rustls", "awc/rustls"]
# unix domain sockets support
uds = ["actix-server/uds"]
[dependencies] [dependencies]
actix-codec = "0.1.2" actix-codec = "0.2.0-alpha.1"
actix-service = "0.4.1" actix-service = "1.0.0-alpha.1"
actix-utils = "0.4.4" actix-utils = "0.5.0-alpha.1"
actix-router = "0.1.5" actix-router = "0.1.5"
actix-rt = "0.2.4" actix-rt = "1.0.0-alpha.1"
actix-web-codegen = "0.1.2" actix-web-codegen = "0.1.2"
actix-http = "0.2.11" actix-http = "0.3.0-alpha.1"
actix-server = "0.6.1" actix-server = "0.8.0-alpha.1"
actix-server-config = "0.1.2" actix-server-config = "0.3.0-alpha.1"
actix-testing = "0.1.0" actix-testing = "0.3.0-alpha.1"
actix-threadpool = "0.1.1" actix-threadpool = "0.2.0-alpha.1"
awc = { version = "0.2.7", optional = true } awc = { version = "0.3.0-alpha.1", optional = true }
bytes = "0.4" bytes = "0.4"
derive_more = "0.15.0" derive_more = "0.15.0"
encoding_rs = "0.8" encoding_rs = "0.8"
futures = "0.1.25" futures = "0.3.1"
hashbrown = "0.6.3" hashbrown = "0.6.3"
log = "0.4" log = "0.4"
mime = "0.3" mime = "0.3"
net2 = "0.2.33" net2 = "0.2.33"
parking_lot = "0.9" parking_lot = "0.9"
pin-project = "0.4.5"
regex = "1.0" regex = "1.0"
serde = { version = "1.0", features=["derive"] } serde = { version = "1.0", features=["derive"] }
serde_json = "1.0" serde_json = "1.0"
@ -103,17 +100,17 @@ time = "0.1.42"
url = "2.1" url = "2.1"
# ssl support # ssl support
openssl = { version="0.10", optional = true } open-ssl = { version="0.10", package="openssl", optional = true }
rustls = { version = "0.15", optional = true } rust-tls = { version = "0.16", package="rustls", optional = true }
[dev-dependencies] [dev-dependencies]
actix = "0.8.3" # actix = "0.8.3"
actix-connect = "0.2.2" actix-connect = "0.3.0-alpha.1"
actix-http-test = "0.2.4" actix-http-test = "0.3.0-alpha.1"
rand = "0.7" rand = "0.7"
env_logger = "0.6" env_logger = "0.6"
serde_derive = "1.0" serde_derive = "1.0"
tokio-timer = "0.2.8" tokio-timer = "0.3.0-alpha.6"
brotli2 = "0.3.2" brotli2 = "0.3.2"
flate2 = "1.0.2" flate2 = "1.0.2"
@ -123,19 +120,18 @@ opt-level = 3
codegen-units = 1 codegen-units = 1
[patch.crates-io] [patch.crates-io]
# actix-web = { path = "." } actix-web = { path = "." }
# actix-http = { path = "actix-http" } actix-http = { path = "actix-http" }
# actix-http-test = { path = "test-server" } actix-http-test = { path = "test-server" }
# actix-web-codegen = { path = "actix-web-codegen" } actix-web-codegen = { path = "actix-web-codegen" }
# actix-web-actors = { path = "actix-web-actors" } # actix-web-actors = { path = "actix-web-actors" }
# actix-session = { path = "actix-session" } actix-session = { path = "actix-session" }
# actix-files = { path = "actix-files" } actix-files = { path = "actix-files" }
# actix-multipart = { path = "actix-multipart" } actix-multipart = { path = "actix-multipart" }
# awc = { path = "awc" } awc = { path = "awc" }
actix-codec = { path = "../actix-net/actix-codec" } actix-codec = { path = "../actix-net/actix-codec" }
actix-connect = { path = "../actix-net/actix-connect" } actix-connect = { path = "../actix-net/actix-connect" }
actix-ioframe = { path = "../actix-net/actix-ioframe" }
actix-rt = { path = "../actix-net/actix-rt" } actix-rt = { path = "../actix-net/actix-rt" }
actix-server = { path = "../actix-net/actix-server" } actix-server = { path = "../actix-net/actix-server" }
actix-server-config = { path = "../actix-net/actix-server-config" } actix-server-config = { path = "../actix-net/actix-server-config" }

View file

@ -1,6 +1,6 @@
[package] [package]
name = "actix-cors" name = "actix-cors"
version = "0.1.0" version = "0.2.0-alpha.1"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"] authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Cross-origin resource sharing (CORS) for Actix applications." description = "Cross-origin resource sharing (CORS) for Actix applications."
readme = "README.md" readme = "README.md"
@ -10,14 +10,14 @@ repository = "https://github.com/actix/actix-web.git"
documentation = "https://docs.rs/actix-cors/" documentation = "https://docs.rs/actix-cors/"
license = "MIT/Apache-2.0" license = "MIT/Apache-2.0"
edition = "2018" edition = "2018"
#workspace = ".." workspace = ".."
[lib] [lib]
name = "actix_cors" name = "actix_cors"
path = "src/lib.rs" path = "src/lib.rs"
[dependencies] [dependencies]
actix-web = "1.0.0" actix-web = "2.0.0-alpha.1"
actix-service = "0.4.0" actix-service = "1.0.0-alpha.1"
derive_more = "0.15.0" derive_more = "0.15.0"
futures = "0.1.25" futures = "0.3.1"

View file

@ -1,6 +1,6 @@
[package] [package]
name = "actix-files" name = "actix-files"
version = "0.1.7" version = "0.2.0-alpha.1"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"] authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Static files support for actix web." description = "Static files support for actix web."
readme = "README.md" readme = "README.md"
@ -18,12 +18,12 @@ name = "actix_files"
path = "src/lib.rs" path = "src/lib.rs"
[dependencies] [dependencies]
actix-web = { version = "1.0.8", default-features = false } actix-web = { version = "2.0.0-alpha.1", default-features = false }
actix-http = "0.2.11" actix-http = "0.3.0-alpha.1"
actix-service = "0.4.1" actix-service = "1.0.0-alpha.1"
bitflags = "1" bitflags = "1"
bytes = "0.4" bytes = "0.4"
futures = "0.1.25" futures = "0.3.1"
derive_more = "0.15.0" derive_more = "0.15.0"
log = "0.4" log = "0.4"
mime = "0.3" mime = "0.3"
@ -32,4 +32,4 @@ percent-encoding = "2.1"
v_htmlescape = "0.4" v_htmlescape = "0.4"
[dev-dependencies] [dev-dependencies]
actix-web = { version = "1.0.8", features=["ssl"] } actix-web = { version = "2.0.0-alpha.1", features=["openssl"] }

View file

@ -303,8 +303,7 @@ impl Files {
/// Set custom directory renderer /// Set custom directory renderer
pub fn files_listing_renderer<F>(mut self, f: F) -> Self pub fn files_listing_renderer<F>(mut self, f: F) -> Self
where where
for<'r, 's> F: for<'r, 's> F: Fn(&'r Directory, &'s HttpRequest) -> Result<ServiceResponse, io::Error>
Fn(&'r Directory, &'s HttpRequest) -> Result<ServiceResponse, io::Error>
+ 'static, + 'static,
{ {
self.renderer = Rc::new(f); self.renderer = Rc::new(f);

View file

@ -1,6 +1,6 @@
[package] [package]
name = "actix-framed" name = "actix-framed"
version = "0.2.1" version = "0.3.0-alpha.1"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"] authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Actix framed app server" description = "Actix framed app server"
readme = "README.md" readme = "README.md"
@ -20,19 +20,19 @@ name = "actix_framed"
path = "src/lib.rs" path = "src/lib.rs"
[dependencies] [dependencies]
actix-codec = "0.1.2" actix-codec = "0.2.0-alpha.1"
actix-service = "0.4.1" actix-service = "1.0.0-alpha.1"
actix-router = "0.1.2" actix-router = "0.1.2"
actix-rt = "0.2.2" actix-rt = "1.0.0-alpha.1"
actix-http = "0.2.7" actix-http = "0.3.0-alpha.1"
actix-server-config = "0.1.2" actix-server-config = "0.2.0-alpha.1"
bytes = "0.4" bytes = "0.4"
futures = "0.1.25" futures = "0.1.25"
log = "0.4" log = "0.4"
[dev-dependencies] [dev-dependencies]
actix-server = { version = "0.6.0", features=["ssl"] } actix-server = { version = "0.8.0-alpha.1", features=["openssl"] }
actix-connect = { version = "0.2.0", features=["ssl"] } actix-connect = { version = "0.3.0-alpha.1", features=["openssl"] }
actix-http-test = { version = "0.2.4", features=["ssl"] } actix-http-test = { version = "0.3.0-alpha.1", features=["openssl"] }
actix-utils = "0.4.4" actix-utils = "0.5.0-alpha.1"

View file

@ -13,8 +13,7 @@ categories = ["network-programming", "asynchronous",
"web-programming::websocket"] "web-programming::websocket"]
license = "MIT/Apache-2.0" license = "MIT/Apache-2.0"
edition = "2018" edition = "2018"
workspace = ".."
# workspace = ".."
[package.metadata.docs.rs] [package.metadata.docs.rs]
features = ["openssl", "fail", "brotli", "flate2-zlib", "secure-cookies"] features = ["openssl", "fail", "brotli", "flate2-zlib", "secure-cookies"]
@ -114,18 +113,3 @@ actix-http-test = { version = "0.3.0-alpha.1", features=["openssl"] }
env_logger = "0.6" env_logger = "0.6"
serde_derive = "1.0" serde_derive = "1.0"
open-ssl = { version="0.10", package="openssl" } open-ssl = { version="0.10", package="openssl" }
[patch.crates-io]
awc = { path = "../awc" }
actix-http = { path = "." }
actix-http-test = { path = "../test-server" }
actix-codec = { path = "../../actix-net/actix-codec" }
actix-connect = { path = "../../actix-net/actix-connect" }
actix-rt = { path = "../../actix-net/actix-rt" }
actix-server = { path = "../../actix-net/actix-server" }
actix-server-config = { path = "../../actix-net/actix-server-config" }
actix-service = { path = "../../actix-net/actix-service" }
actix-testing = { path = "../../actix-net/actix-testing" }
actix-threadpool = { path = "../../actix-net/actix-threadpool" }
actix-utils = { path = "../../actix-net/actix-utils" }

View file

@ -172,12 +172,11 @@ where
/// Finish service configuration and create *http service* for HTTP/1 protocol. /// Finish service configuration and create *http service* for HTTP/1 protocol.
pub fn h1<F, P, B>(self, service: F) -> H1Service<T, P, S, B, X, U> pub fn h1<F, P, B>(self, service: F) -> H1Service<T, P, S, B, X, U>
where where
B: MessageBody + 'static, B: MessageBody,
F: IntoServiceFactory<S>, F: IntoServiceFactory<S>,
S::Error: Into<Error> + 'static, S::Error: Into<Error>,
S::InitError: fmt::Debug, S::InitError: fmt::Debug,
S::Response: Into<Response<B>> + 'static, S::Response: Into<Response<B>>,
<S::Service as Service>::Future: 'static,
{ {
let cfg = ServiceConfig::new( let cfg = ServiceConfig::new(
self.keep_alive, self.keep_alive,

View file

@ -51,10 +51,10 @@ impl<T, S, B> Dispatcher<T, S, B>
where where
T: IoStream, T: IoStream,
S: Service<Request = Request>, S: Service<Request = Request>,
S::Error: Into<Error> + 'static, S::Error: Into<Error>,
S::Future: 'static, // S::Future: 'static,
S::Response: Into<Response<B>> + 'static, S::Response: Into<Response<B>>,
B: MessageBody + 'static, B: MessageBody,
{ {
pub(crate) fn new( pub(crate) fn new(
service: CloneableService<S>, service: CloneableService<S>,
@ -176,9 +176,9 @@ enum ServiceResponseState<F, B> {
impl<F, I, E, B> ServiceResponse<F, I, E, B> impl<F, I, E, B> ServiceResponse<F, I, E, B>
where where
F: Future<Output = Result<I, E>>, F: Future<Output = Result<I, E>>,
E: Into<Error> + 'static, E: Into<Error>,
I: Into<Response<B>> + 'static, I: Into<Response<B>>,
B: MessageBody + 'static, B: MessageBody,
{ {
fn prepare_response( fn prepare_response(
&self, &self,
@ -244,9 +244,9 @@ where
impl<F, I, E, B> Future for ServiceResponse<F, I, E, B> impl<F, I, E, B> Future for ServiceResponse<F, I, E, B>
where where
F: Future<Output = Result<I, E>>, F: Future<Output = Result<I, E>>,
E: Into<Error> + 'static, E: Into<Error>,
I: Into<Response<B>> + 'static, I: Into<Response<B>>,
B: MessageBody + 'static, B: MessageBody,
{ {
type Output = (); type Output = ();

View file

@ -1,6 +1,6 @@
[package] [package]
name = "actix-identity" name = "actix-identity"
version = "0.1.0" version = "0.2.0-alpha.1"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"] authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Identity service for actix web framework." description = "Identity service for actix web framework."
readme = "README.md" readme = "README.md"
@ -17,14 +17,14 @@ name = "actix_identity"
path = "src/lib.rs" path = "src/lib.rs"
[dependencies] [dependencies]
actix-web = { version = "1.0.0", default-features = false, features = ["secure-cookies"] } actix-web = { version = "2.0.0-alpha.1", default-features = false, features = ["secure-cookies"] }
actix-service = "0.4.0" actix-service = "1.0.0-alpha.1"
futures = "0.1.25" futures = "0.3.1"
serde = "1.0" serde = "1.0"
serde_json = "1.0" serde_json = "1.0"
time = "0.1.42" time = "0.1.42"
[dev-dependencies] [dev-dependencies]
actix-rt = "0.2.2" actix-rt = "1.0.0-alpha.1"
actix-http = "0.2.3" actix-http = "0.3.0-alpha.1"
bytes = "0.4" bytes = "0.4"

View file

@ -1,6 +1,6 @@
[package] [package]
name = "actix-multipart" name = "actix-multipart"
version = "0.1.4" version = "0.2.0-alpha.1"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"] authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Multipart support for actix web framework." description = "Multipart support for actix web framework."
readme = "README.md" readme = "README.md"
@ -18,17 +18,17 @@ name = "actix_multipart"
path = "src/lib.rs" path = "src/lib.rs"
[dependencies] [dependencies]
actix-web = { version = "1.0.0", default-features = false } actix-web = { version = "2.0.0-alpha.1", default-features = false }
actix-service = "0.4.1" actix-service = "1.0.0-alpha.1"
bytes = "0.4" bytes = "0.4"
derive_more = "0.15.0" derive_more = "0.15.0"
httparse = "1.3" httparse = "1.3"
futures = "0.1.25" futures = "0.3.1"
log = "0.4" log = "0.4"
mime = "0.3" mime = "0.3"
time = "0.1" time = "0.1"
twoway = "0.2" twoway = "0.2"
[dev-dependencies] [dev-dependencies]
actix-rt = "0.2.2" actix-rt = "1.0.0-alpha.1"
actix-http = "0.2.4" actix-http = "0.3.0-alpha.1"

View file

@ -1,6 +1,6 @@
[package] [package]
name = "actix-session" name = "actix-session"
version = "0.2.0" version = "0.3.0-alpha.1"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"] authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Session for actix web framework." description = "Session for actix web framework."
readme = "README.md" readme = "README.md"
@ -24,15 +24,15 @@ default = ["cookie-session"]
cookie-session = ["actix-web/secure-cookies"] cookie-session = ["actix-web/secure-cookies"]
[dependencies] [dependencies]
actix-web = "1.0.0" actix-web = "2.0.0-alpha.1"
actix-service = "0.4.1" actix-service = "1.0.0-alpha.1"
bytes = "0.4" bytes = "0.4"
derive_more = "0.15.0" derive_more = "0.15.0"
futures = "0.1.25" futures = "0.3.1"
hashbrown = "0.5.0" hashbrown = "0.6.3"
serde = "1.0" serde = "1.0"
serde_json = "1.0" serde_json = "1.0"
time = "0.1.42" time = "0.1.42"
[dev-dependencies] [dev-dependencies]
actix-rt = "0.2.2" actix-rt = "1.0.0-alpha.1"

View file

@ -63,7 +63,7 @@ rust-tls = { version = "0.16.0", package="rustls", optional = true }
[dev-dependencies] [dev-dependencies]
actix-rt = "1.0.0-alpha.1" actix-rt = "1.0.0-alpha.1"
#actix-web = { version = "1.0.8", features=["ssl"] } actix-web = { version = "2.0.0-alpha.1", features=["openssl"] }
actix-http = { version = "0.3.0-alpha.1", features=["openssl"] } actix-http = { version = "0.3.0-alpha.1", features=["openssl"] }
actix-http-test = { version = "0.3.0-alpha.1", features=["openssl"] } actix-http-test = { version = "0.3.0-alpha.1", features=["openssl"] }
actix-utils = "0.5.0-alpha.1" actix-utils = "0.5.0-alpha.1"
@ -75,17 +75,3 @@ rand = "0.7"
tokio-tcp = "0.1" tokio-tcp = "0.1"
webpki = { version = "0.21" } webpki = { version = "0.21" }
rus-tls = { version = "0.16.0", package="rustls", features = ["dangerous_configuration"] } rus-tls = { version = "0.16.0", package="rustls", features = ["dangerous_configuration"] }
[patch.crates-io]
awc = { path = "." }
actix-http = { path = "../actix-http" }
actix-http-test = { path = "../test-server" }
actix-codec = { path = "../../actix-net/actix-codec" }
actix-connect = { path = "../../actix-net/actix-connect" }
actix-rt = { path = "../../actix-net/actix-rt" }
actix-server = { path = "../../actix-net/actix-server" }
actix-server-config = { path = "../../actix-net/actix-server-config" }
actix-service = { path = "../../actix-net/actix-service" }
actix-threadpool = { path = "../../actix-net/actix-threadpool" }
actix-utils = { path = "../../actix-net/actix-utils" }

View file

@ -13,8 +13,8 @@ use futures::Future;
use rand::Rng; use rand::Rng;
use actix_http::HttpService; use actix_http::HttpService;
use actix_http_test::TestServer; use actix_http_test::{bloxk_on, TestServer};
use actix_service::{service_fn, NewService}; use actix_service::{service_fn, ServiceFactory};
use actix_web::http::Cookie; use actix_web::http::Cookie;
use actix_web::middleware::{BodyEncoding, Compress}; use actix_web::middleware::{BodyEncoding, Compress};
use actix_web::{http::header, web, App, Error, HttpMessage, HttpRequest, HttpResponse}; use actix_web::{http::header, web, App, Error, HttpMessage, HttpRequest, HttpResponse};
@ -44,8 +44,8 @@ const STR: &str = "Hello World Hello World Hello World Hello World Hello World \
#[test] #[test]
fn test_simple() { fn test_simple() {
let mut srv = block_on(async {
TestServer::new(|| { let mut srv = TestServer::start(|| {
HttpService::new(App::new().service( HttpService::new(App::new().service(
web::resource("/").route(web::to(|| HttpResponse::Ok().body(STR))), web::resource("/").route(web::to(|| HttpResponse::Ok().body(STR))),
)) ))
@ -69,14 +69,19 @@ fn test_simple() {
// camel case // camel case
let response = srv.block_on(srv.post("/").camel_case().send()).unwrap(); let response = srv.block_on(srv.post("/").camel_case().send()).unwrap();
assert!(response.status().is_success()); assert!(response.status().is_success());
})
} }
#[test] #[test]
fn test_json() { fn test_json() {
let mut srv = TestServer::new(|| { block_on(async {
HttpService::new(App::new().service( let mut srv = TestServer::start(|| {
web::resource("/").route(web::to(|_: web::Json<String>| HttpResponse::Ok())), HttpService::new(
)) App::new().service(
web::resource("/")
.route(web::to(|_: web::Json<String>| HttpResponse::Ok())),
),
)
}); });
let request = srv let request = srv
@ -85,11 +90,13 @@ fn test_json() {
.send_json(&"TEST".to_string()); .send_json(&"TEST".to_string());
let response = srv.block_on(request).unwrap(); let response = srv.block_on(request).unwrap();
assert!(response.status().is_success()); assert!(response.status().is_success());
})
} }
#[test] #[test]
fn test_form() { fn test_form() {
let mut srv = TestServer::new(|| { block_on(async {
let mut srv = TestServer::start(|| {
HttpService::new(App::new().service(web::resource("/").route(web::to( HttpService::new(App::new().service(web::resource("/").route(web::to(
|_: web::Form<HashMap<String, String>>| HttpResponse::Ok(), |_: web::Form<HashMap<String, String>>| HttpResponse::Ok(),
)))) ))))
@ -101,17 +108,19 @@ fn test_form() {
let request = srv.get("/").header("x-test", "111").send_form(&data); let request = srv.get("/").header("x-test", "111").send_form(&data);
let response = srv.block_on(request).unwrap(); let response = srv.block_on(request).unwrap();
assert!(response.status().is_success()); assert!(response.status().is_success());
})
} }
#[test] #[test]
fn test_timeout() { fn test_timeout() {
let mut srv = TestServer::new(|| { block_on(async {
HttpService::new(App::new().service(web::resource("/").route(web::to_async( let mut srv = TestServer::start(|| {
|| { HttpService::new(App::new().service(web::resource("/").route(
web::to_async(|| {
tokio_timer::sleep(Duration::from_millis(200)) tokio_timer::sleep(Duration::from_millis(200))
.then(|_| Ok::<_, Error>(HttpResponse::Ok().body(STR))) .then(|_| Ok::<_, Error>(HttpResponse::Ok().body(STR)))
}, }),
)))) )))
}); });
let client = srv.execute(|| { let client = srv.execute(|| {
@ -124,17 +133,19 @@ fn test_timeout() {
Err(SendRequestError::Timeout) => (), Err(SendRequestError::Timeout) => (),
_ => panic!(), _ => panic!(),
} }
})
} }
#[test] #[test]
fn test_timeout_override() { fn test_timeout_override() {
let mut srv = TestServer::new(|| { block_on(async {
HttpService::new(App::new().service(web::resource("/").route(web::to_async( let mut srv = TestServer::start(|| {
|| { HttpService::new(App::new().service(web::resource("/").route(
web::to_async(|| {
tokio_timer::sleep(Duration::from_millis(200)) tokio_timer::sleep(Duration::from_millis(200))
.then(|_| Ok::<_, Error>(HttpResponse::Ok().body(STR))) .then(|_| Ok::<_, Error>(HttpResponse::Ok().body(STR)))
}, }),
)))) )))
}); });
let client = awc::Client::build() let client = awc::Client::build()
@ -148,22 +159,24 @@ fn test_timeout_override() {
Err(SendRequestError::Timeout) => (), Err(SendRequestError::Timeout) => (),
_ => panic!(), _ => panic!(),
} }
})
} }
#[test] #[test]
fn test_connection_reuse() { fn test_connection_reuse() {
block_on(async {
let num = Arc::new(AtomicUsize::new(0)); let num = Arc::new(AtomicUsize::new(0));
let num2 = num.clone(); let num2 = num.clone();
let mut srv = TestServer::new(move || { let mut srv = TestServer::start(move || {
let num2 = num2.clone(); let num2 = num2.clone();
service_fn(move |io| { service_fn(move |io| {
num2.fetch_add(1, Ordering::Relaxed); num2.fetch_add(1, Ordering::Relaxed);
Ok(io) Ok(io)
}) })
.and_then(HttpService::new( .and_then(HttpService::new(App::new().service(
App::new().service(web::resource("/").route(web::to(|| HttpResponse::Ok()))), web::resource("/").route(web::to(|| HttpResponse::Ok())),
)) )))
}); });
let client = awc::Client::default(); let client = awc::Client::default();
@ -180,10 +193,12 @@ fn test_connection_reuse() {
// one connection // one connection
assert_eq!(num.load(Ordering::Relaxed), 1); assert_eq!(num.load(Ordering::Relaxed), 1);
})
} }
#[test] #[test]
fn test_connection_force_close() { fn test_connection_force_close() {
block_on(async {
let num = Arc::new(AtomicUsize::new(0)); let num = Arc::new(AtomicUsize::new(0));
let num2 = num.clone(); let num2 = num.clone();
@ -193,9 +208,9 @@ fn test_connection_force_close() {
num2.fetch_add(1, Ordering::Relaxed); num2.fetch_add(1, Ordering::Relaxed);
Ok(io) Ok(io)
}) })
.and_then(HttpService::new( .and_then(HttpService::new(App::new().service(
App::new().service(web::resource("/").route(web::to(|| HttpResponse::Ok()))), web::resource("/").route(web::to(|| HttpResponse::Ok())),
)) )))
}); });
let client = awc::Client::default(); let client = awc::Client::default();
@ -212,10 +227,12 @@ fn test_connection_force_close() {
// two connection // two connection
assert_eq!(num.load(Ordering::Relaxed), 2); assert_eq!(num.load(Ordering::Relaxed), 2);
})
} }
#[test] #[test]
fn test_connection_server_close() { fn test_connection_server_close() {
block_on(async {
let num = Arc::new(AtomicUsize::new(0)); let num = Arc::new(AtomicUsize::new(0));
let num2 = num.clone(); let num2 = num.clone();
@ -247,10 +264,12 @@ fn test_connection_server_close() {
// two connection // two connection
assert_eq!(num.load(Ordering::Relaxed), 2); assert_eq!(num.load(Ordering::Relaxed), 2);
})
} }
#[test] #[test]
fn test_connection_wait_queue() { fn test_connection_wait_queue() {
block_on(async {
let num = Arc::new(AtomicUsize::new(0)); let num = Arc::new(AtomicUsize::new(0));
let num2 = num.clone(); let num2 = num.clone();
@ -292,10 +311,12 @@ fn test_connection_wait_queue() {
// two connection // two connection
assert_eq!(num.load(Ordering::Relaxed), 1); assert_eq!(num.load(Ordering::Relaxed), 1);
})
} }
#[test] #[test]
fn test_connection_wait_queue_force_close() { fn test_connection_wait_queue_force_close() {
block_on(async {
let num = Arc::new(AtomicUsize::new(0)); let num = Arc::new(AtomicUsize::new(0));
let num2 = num.clone(); let num2 = num.clone();
@ -340,10 +361,12 @@ fn test_connection_wait_queue_force_close() {
// two connection // two connection
assert_eq!(num.load(Ordering::Relaxed), 2); assert_eq!(num.load(Ordering::Relaxed), 2);
})
} }
#[test] #[test]
fn test_with_query_parameter() { fn test_with_query_parameter() {
block_on(async {
let mut srv = TestServer::new(|| { let mut srv = TestServer::new(|| {
HttpService::new(App::new().service(web::resource("/").to( HttpService::new(App::new().service(web::resource("/").to(
|req: HttpRequest| { |req: HttpRequest| {
@ -360,10 +383,12 @@ fn test_with_query_parameter() {
.block_on(awc::Client::new().get(srv.url("/?qp=5")).send()) .block_on(awc::Client::new().get(srv.url("/?qp=5")).send())
.unwrap(); .unwrap();
assert!(res.status().is_success()); assert!(res.status().is_success());
})
} }
#[test] #[test]
fn test_no_decompress() { fn test_no_decompress() {
block_on(async {
let mut srv = TestServer::new(|| { let mut srv = TestServer::new(|| {
HttpService::new(App::new().wrap(Compress::default()).service( HttpService::new(App::new().wrap(Compress::default()).service(
web::resource("/").route(web::to(|| { web::resource("/").route(web::to(|| {
@ -398,12 +423,15 @@ fn test_no_decompress() {
let mut dec = Vec::new(); let mut dec = Vec::new();
e.read_to_end(&mut dec).unwrap(); e.read_to_end(&mut dec).unwrap();
assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref())); assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref()));
})
} }
#[test] #[test]
fn test_client_gzip_encoding() { fn test_client_gzip_encoding() {
block_on(async {
let mut srv = TestServer::new(|| { let mut srv = TestServer::new(|| {
HttpService::new(App::new().service(web::resource("/").route(web::to(|| { HttpService::new(App::new().service(web::resource("/").route(web::to(
|| {
let mut e = GzEncoder::new(Vec::new(), Compression::default()); let mut e = GzEncoder::new(Vec::new(), Compression::default());
e.write_all(STR.as_ref()).unwrap(); e.write_all(STR.as_ref()).unwrap();
let data = e.finish().unwrap(); let data = e.finish().unwrap();
@ -411,7 +439,8 @@ fn test_client_gzip_encoding() {
HttpResponse::Ok() HttpResponse::Ok()
.header("content-encoding", "gzip") .header("content-encoding", "gzip")
.body(data) .body(data)
})))) },
))))
}); });
// client request // client request
@ -421,12 +450,15 @@ fn test_client_gzip_encoding() {
// read response // read response
let bytes = srv.block_on(response.body()).unwrap(); let bytes = srv.block_on(response.body()).unwrap();
assert_eq!(bytes, Bytes::from_static(STR.as_ref())); assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
})
} }
#[test] #[test]
fn test_client_gzip_encoding_large() { fn test_client_gzip_encoding_large() {
block_on(async {
let mut srv = TestServer::new(|| { let mut srv = TestServer::new(|| {
HttpService::new(App::new().service(web::resource("/").route(web::to(|| { HttpService::new(App::new().service(web::resource("/").route(web::to(
|| {
let mut e = GzEncoder::new(Vec::new(), Compression::default()); let mut e = GzEncoder::new(Vec::new(), Compression::default());
e.write_all(STR.repeat(10).as_ref()).unwrap(); e.write_all(STR.repeat(10).as_ref()).unwrap();
let data = e.finish().unwrap(); let data = e.finish().unwrap();
@ -434,7 +466,8 @@ fn test_client_gzip_encoding_large() {
HttpResponse::Ok() HttpResponse::Ok()
.header("content-encoding", "gzip") .header("content-encoding", "gzip")
.body(data) .body(data)
})))) },
))))
}); });
// client request // client request
@ -444,10 +477,12 @@ fn test_client_gzip_encoding_large() {
// read response // read response
let bytes = srv.block_on(response.body()).unwrap(); let bytes = srv.block_on(response.body()).unwrap();
assert_eq!(bytes, Bytes::from(STR.repeat(10))); assert_eq!(bytes, Bytes::from(STR.repeat(10)));
})
} }
#[test] #[test]
fn test_client_gzip_encoding_large_random() { fn test_client_gzip_encoding_large_random() {
block_on(async {
let data = rand::thread_rng() let data = rand::thread_rng()
.sample_iter(&rand::distributions::Alphanumeric) .sample_iter(&rand::distributions::Alphanumeric)
.take(100_000) .take(100_000)
@ -473,10 +508,12 @@ fn test_client_gzip_encoding_large_random() {
// read response // read response
let bytes = srv.block_on(response.body()).unwrap(); let bytes = srv.block_on(response.body()).unwrap();
assert_eq!(bytes, Bytes::from(data)); assert_eq!(bytes, Bytes::from(data));
})
} }
#[test] #[test]
fn test_client_brotli_encoding() { fn test_client_brotli_encoding() {
block_on(async {
let mut srv = TestServer::new(|| { let mut srv = TestServer::new(|| {
HttpService::new(App::new().service(web::resource("/").route(web::to( HttpService::new(App::new().service(web::resource("/").route(web::to(
|data: Bytes| { |data: Bytes| {
@ -497,6 +534,7 @@ fn test_client_brotli_encoding() {
// read response // read response
let bytes = srv.block_on(response.body()).unwrap(); let bytes = srv.block_on(response.body()).unwrap();
assert_eq!(bytes, Bytes::from_static(STR.as_ref())); assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
})
} }
// #[test] // #[test]
@ -644,6 +682,7 @@ fn test_client_brotli_encoding() {
#[test] #[test]
fn test_client_cookie_handling() { fn test_client_cookie_handling() {
block_on(async {
fn err() -> Error { fn err() -> Error {
use std::io::{Error as IoError, ErrorKind}; use std::io::{Error as IoError, ErrorKind};
// stub some generic error // stub some generic error
@ -703,6 +742,7 @@ fn test_client_cookie_handling() {
assert_eq!(c1, cookie1); assert_eq!(c1, cookie1);
let c2 = response.cookie("cookie2").expect("Missing cookie2"); let c2 = response.cookie("cookie2").expect("Missing cookie2");
assert_eq!(c2, cookie2); assert_eq!(c2, cookie2);
})
} }
// #[test] // #[test]
@ -737,6 +777,7 @@ fn test_client_cookie_handling() {
#[test] #[test]
fn client_basic_auth() { fn client_basic_auth() {
block_on(async {
let mut srv = TestServer::new(|| { let mut srv = TestServer::new(|| {
HttpService::new(App::new().route( HttpService::new(App::new().route(
"/", "/",
@ -761,10 +802,12 @@ fn client_basic_auth() {
let request = srv.get("/").basic_auth("username", Some("password")); let request = srv.get("/").basic_auth("username", Some("password"));
let response = srv.block_on(request.send()).unwrap(); let response = srv.block_on(request.send()).unwrap();
assert!(response.status().is_success()); assert!(response.status().is_success());
})
} }
#[test] #[test]
fn client_bearer_auth() { fn client_bearer_auth() {
block_on(async {
let mut srv = TestServer::new(|| { let mut srv = TestServer::new(|| {
HttpService::new(App::new().route( HttpService::new(App::new().route(
"/", "/",
@ -789,4 +832,5 @@ fn client_bearer_auth() {
let request = srv.get("/").bearer_auth("someS3cr3tAutht0k3n"); let request = srv.get("/").bearer_auth("someS3cr3tAutht0k3n");
let response = srv.block_on(request.send()).unwrap(); let response = srv.block_on(request.send()).unwrap();
assert!(response.status().is_success()); assert!(response.status().is_success());
})
} }

View file

@ -1,5 +1,3 @@
use futures::IntoFuture;
use actix_web::{ use actix_web::{
get, middleware, web, App, Error, HttpRequest, HttpResponse, HttpServer, get, middleware, web, App, Error, HttpRequest, HttpResponse, HttpServer,
}; };
@ -10,7 +8,7 @@ fn index(req: HttpRequest, name: web::Path<String>) -> String {
format!("Hello: {}!\r\n", name) format!("Hello: {}!\r\n", name)
} }
fn index_async(req: HttpRequest) -> impl IntoFuture<Item = &'static str, Error = Error> { async fn index_async(req: HttpRequest) -> Result<&'static str, Error> {
println!("REQ: {:?}", req); println!("REQ: {:?}", req);
Ok("Hello world!\r\n") Ok("Hello world!\r\n")
} }
@ -28,7 +26,7 @@ fn main() -> std::io::Result<()> {
App::new() App::new()
.wrap(middleware::DefaultHeaders::new().header("X-Version", "0.2")) .wrap(middleware::DefaultHeaders::new().header("X-Version", "0.2"))
.wrap(middleware::Compress::default()) .wrap(middleware::Compress::default())
.wrap(middleware::Logger::default()) // .wrap(middleware::Logger::default())
.service(index) .service(index)
.service(no_params) .service(no_params)
.service( .service(

View file

@ -1,26 +1,27 @@
use actix_http::Error; use actix_http::Error;
use actix_rt::System; use actix_rt::System;
use futures::{future::lazy, Future};
fn main() -> Result<(), Error> { fn main() -> Result<(), Error> {
std::env::set_var("RUST_LOG", "actix_http=trace"); std::env::set_var("RUST_LOG", "actix_http=trace");
env_logger::init(); env_logger::init();
System::new("test").block_on(lazy(|| { System::new("test").block_on(async {
awc::Client::new() let client = awc::Client::new();
.get("https://www.rust-lang.org/") // <- Create request builder
// Create request builder, configure request and send
let mut response = client
.get("https://www.rust-lang.org/")
.header("User-Agent", "Actix-web") .header("User-Agent", "Actix-web")
.send() // <- Send http request .send()
.from_err() .await?;
.and_then(|mut response| {
// <- server http response // server http response
println!("Response: {:?}", response); println!("Response: {:?}", response);
// read response body // read response body
response let body = response.body().await?;
.body() println!("Downloaded: {:?} bytes", body.len());
.from_err()
.map(|body| println!("Downloaded: {:?} bytes", body.len())) Ok(())
}) })
}))
} }

View file

@ -1,5 +1,3 @@
use futures::IntoFuture;
use actix_web::{ use actix_web::{
get, middleware, web, App, Error, HttpRequest, HttpResponse, HttpServer, get, middleware, web, App, Error, HttpRequest, HttpResponse, HttpServer,
}; };
@ -10,7 +8,7 @@ fn index(req: HttpRequest, name: web::Path<String>) -> String {
format!("Hello: {}!\r\n", name) format!("Hello: {}!\r\n", name)
} }
fn index_async(req: HttpRequest) -> impl IntoFuture<Item = &'static str, Error = Error> { async fn index_async(req: HttpRequest) -> Result<&'static str, Error> {
println!("REQ: {:?}", req); println!("REQ: {:?}", req);
Ok("Hello world!\r\n") Ok("Hello world!\r\n")
} }
@ -29,7 +27,7 @@ fn main() -> std::io::Result<()> {
App::new() App::new()
.wrap(middleware::DefaultHeaders::new().header("X-Version", "0.2")) .wrap(middleware::DefaultHeaders::new().header("X-Version", "0.2"))
.wrap(middleware::Compress::default()) .wrap(middleware::Compress::default())
.wrap(middleware::Logger::default()) // .wrap(middleware::Logger::default())
.service(index) .service(index)
.service(no_params) .service(no_params)
.service( .service(
@ -38,7 +36,7 @@ fn main() -> std::io::Result<()> {
middleware::DefaultHeaders::new().header("X-Version-R2", "0.3"), middleware::DefaultHeaders::new().header("X-Version-R2", "0.3"),
) )
.default_service( .default_service(
web::route().to(|| HttpResponse::MethodNotAllowed()), web::route().to(|| ok(HttpResponse::MethodNotAllowed())),
) )
.route(web::get().to_async(index_async)), .route(web::get().to_async(index_async)),
) )

View file

@ -1,14 +1,17 @@
use std::cell::RefCell; use std::cell::RefCell;
use std::fmt; use std::fmt;
use std::future::Future;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::pin::Pin;
use std::rc::Rc; use std::rc::Rc;
use std::task::{Context, Poll};
use actix_http::body::{Body, MessageBody}; use actix_http::body::{Body, MessageBody};
use actix_service::boxed::{self, BoxedNewService}; use actix_service::boxed::{self, BoxedNewService};
use actix_service::{ use actix_service::{
apply_transform, IntoNewService, IntoTransform, NewService, Transform, apply, apply_fn_factory, IntoServiceFactory, ServiceFactory, Transform,
}; };
use futures::{Future, IntoFuture}; use futures::future::{FutureExt, LocalBoxFuture};
use crate::app_service::{AppEntry, AppInit, AppRoutingFactory}; use crate::app_service::{AppEntry, AppInit, AppRoutingFactory};
use crate::config::{AppConfig, AppConfigInner, ServiceConfig}; use crate::config::{AppConfig, AppConfigInner, ServiceConfig};
@ -18,19 +21,19 @@ use crate::error::Error;
use crate::resource::Resource; use crate::resource::Resource;
use crate::route::Route; use crate::route::Route;
use crate::service::{ use crate::service::{
HttpServiceFactory, ServiceFactory, ServiceFactoryWrapper, ServiceRequest, AppServiceFactory, HttpServiceFactory, ServiceFactoryWrapper, ServiceRequest,
ServiceResponse, ServiceResponse,
}; };
type HttpNewService = BoxedNewService<(), ServiceRequest, ServiceResponse, Error, ()>; type HttpNewService = BoxedNewService<(), ServiceRequest, ServiceResponse, Error, ()>;
type FnDataFactory = type FnDataFactory =
Box<dyn Fn() -> Box<dyn Future<Item = Box<dyn DataFactory>, Error = ()>>>; Box<dyn Fn() -> LocalBoxFuture<'static, Result<Box<dyn DataFactory>, ()>>>;
/// Application builder - structure that follows the builder pattern /// Application builder - structure that follows the builder pattern
/// for building application instances. /// for building application instances.
pub struct App<T, B> { pub struct App<T, B> {
endpoint: T, endpoint: T,
services: Vec<Box<dyn ServiceFactory>>, services: Vec<Box<dyn AppServiceFactory>>,
default: Option<Rc<HttpNewService>>, default: Option<Rc<HttpNewService>>,
factory_ref: Rc<RefCell<Option<AppRoutingFactory>>>, factory_ref: Rc<RefCell<Option<AppRoutingFactory>>>,
data: Vec<Box<dyn DataFactory>>, data: Vec<Box<dyn DataFactory>>,
@ -61,7 +64,7 @@ impl App<AppEntry, Body> {
impl<T, B> App<T, B> impl<T, B> App<T, B>
where where
B: MessageBody, B: MessageBody,
T: NewService< T: ServiceFactory<
Config = (), Config = (),
Request = ServiceRequest, Request = ServiceRequest,
Response = ServiceResponse<B>, Response = ServiceResponse<B>,
@ -107,24 +110,30 @@ where
/// Set application data factory. This function is /// Set application data factory. This function is
/// similar to `.data()` but it accepts data factory. Data object get /// similar to `.data()` but it accepts data factory. Data object get
/// constructed asynchronously during application initialization. /// constructed asynchronously during application initialization.
pub fn data_factory<F, Out>(mut self, data: F) -> Self pub fn data_factory<F, Out, D, E>(mut self, data: F) -> Self
where where
F: Fn() -> Out + 'static, F: Fn() -> Out + 'static,
Out: IntoFuture + 'static, Out: Future<Output = Result<D, E>> + 'static,
Out::Error: std::fmt::Debug, D: 'static,
E: std::fmt::Debug,
{ {
self.data_factories.push(Box::new(move || { self.data_factories.push(Box::new(move || {
Box::new( {
data() let fut = data();
.into_future() async move {
.map_err(|e| { match fut.await {
Err(e) => {
log::error!("Can not construct data instance: {:?}", e); log::error!("Can not construct data instance: {:?}", e);
}) Err(())
.map(|data| { }
Ok(data) => {
let data: Box<dyn DataFactory> = Box::new(Data::new(data)); let data: Box<dyn DataFactory> = Box::new(Data::new(data));
data Ok(data)
}), }
) }
}
}
.boxed_local()
})); }));
self self
} }
@ -267,8 +276,8 @@ where
/// ``` /// ```
pub fn default_service<F, U>(mut self, f: F) -> Self pub fn default_service<F, U>(mut self, f: F) -> Self
where where
F: IntoNewService<U>, F: IntoServiceFactory<U>,
U: NewService< U: ServiceFactory<
Config = (), Config = (),
Request = ServiceRequest, Request = ServiceRequest,
Response = ServiceResponse, Response = ServiceResponse,
@ -277,11 +286,9 @@ where
U::InitError: fmt::Debug, U::InitError: fmt::Debug,
{ {
// create and configure default resource // create and configure default resource
self.default = Some(Rc::new(boxed::new_service( self.default = Some(Rc::new(boxed::factory(f.into_factory().map_init_err(
f.into_new_service().map_init_err(|e| { |e| log::error!("Can not construct default service: {:?}", e),
log::error!("Can not construct default service: {:?}", e) ))));
}),
)));
self self
} }
@ -350,11 +357,11 @@ where
/// .route("/index.html", web::get().to(index)); /// .route("/index.html", web::get().to(index));
/// } /// }
/// ``` /// ```
pub fn wrap<M, B1, F>( pub fn wrap<M, B1>(
self, self,
mw: F, mw: M,
) -> App< ) -> App<
impl NewService< impl ServiceFactory<
Config = (), Config = (),
Request = ServiceRequest, Request = ServiceRequest,
Response = ServiceResponse<B1>, Response = ServiceResponse<B1>,
@ -372,11 +379,9 @@ where
InitError = (), InitError = (),
>, >,
B1: MessageBody, B1: MessageBody,
F: IntoTransform<M, T::Service>,
{ {
let endpoint = apply_transform(mw, self.endpoint);
App { App {
endpoint, endpoint: apply(mw, self.endpoint),
data: self.data, data: self.data,
data_factories: self.data_factories, data_factories: self.data_factories,
services: self.services, services: self.services,
@ -407,13 +412,16 @@ where
/// ///
/// fn main() { /// fn main() {
/// let app = App::new() /// let app = App::new()
/// .wrap_fn(|req, srv| /// .wrap_fn(|req, srv| {
/// srv.call(req).map(|mut res| { /// let fut = srv.call(req);
/// async {
/// let mut res = fut.await?;
/// res.headers_mut().insert( /// res.headers_mut().insert(
/// CONTENT_TYPE, HeaderValue::from_static("text/plain"), /// CONTENT_TYPE, HeaderValue::from_static("text/plain"),
/// ); /// );
/// res /// Ok(res)
/// })) /// }
/// })
/// .route("/index.html", web::get().to(index)); /// .route("/index.html", web::get().to(index));
/// } /// }
/// ``` /// ```
@ -421,7 +429,7 @@ where
self, self,
mw: F, mw: F,
) -> App< ) -> App<
impl NewService< impl ServiceFactory<
Config = (), Config = (),
Request = ServiceRequest, Request = ServiceRequest,
Response = ServiceResponse<B1>, Response = ServiceResponse<B1>,
@ -433,16 +441,26 @@ where
where where
B1: MessageBody, B1: MessageBody,
F: FnMut(ServiceRequest, &mut T::Service) -> R + Clone, F: FnMut(ServiceRequest, &mut T::Service) -> R + Clone,
R: IntoFuture<Item = ServiceResponse<B1>, Error = Error>, R: Future<Output = Result<ServiceResponse<B1>, Error>>,
{ {
self.wrap(mw) App {
endpoint: apply_fn_factory(self.endpoint, mw),
data: self.data,
data_factories: self.data_factories,
services: self.services,
default: self.default,
factory_ref: self.factory_ref,
config: self.config,
external: self.external,
_t: PhantomData,
}
} }
} }
impl<T, B> IntoNewService<AppInit<T, B>> for App<T, B> impl<T, B> IntoServiceFactory<AppInit<T, B>> for App<T, B>
where where
B: MessageBody, B: MessageBody,
T: NewService< T: ServiceFactory<
Config = (), Config = (),
Request = ServiceRequest, Request = ServiceRequest,
Response = ServiceResponse<B>, Response = ServiceResponse<B>,
@ -450,7 +468,7 @@ where
InitError = (), InitError = (),
>, >,
{ {
fn into_new_service(self) -> AppInit<T, B> { fn into_factory(self) -> AppInit<T, B> {
AppInit { AppInit {
data: Rc::new(self.data), data: Rc::new(self.data),
data_factories: Rc::new(self.data_factories), data_factories: Rc::new(self.data_factories),
@ -468,27 +486,28 @@ where
mod tests { mod tests {
use actix_service::Service; use actix_service::Service;
use bytes::Bytes; use bytes::Bytes;
use futures::{Future, IntoFuture}; use futures::future::{ok, Future};
use super::*; use super::*;
use crate::http::{header, HeaderValue, Method, StatusCode}; use crate::http::{header, HeaderValue, Method, StatusCode};
use crate::middleware::DefaultHeaders;
use crate::service::{ServiceRequest, ServiceResponse}; use crate::service::{ServiceRequest, ServiceResponse};
use crate::test::{ use crate::test::{block_on, call_service, init_service, read_body, TestRequest};
block_fn, block_on, call_service, init_service, read_body, TestRequest,
};
use crate::{web, Error, HttpRequest, HttpResponse}; use crate::{web, Error, HttpRequest, HttpResponse};
#[test] #[test]
fn test_default_resource() { fn test_default_resource() {
block_on(async {
let mut srv = init_service( let mut srv = init_service(
App::new().service(web::resource("/test").to(|| HttpResponse::Ok())), App::new().service(web::resource("/test").to(|| HttpResponse::Ok())),
); )
.await;
let req = TestRequest::with_uri("/test").to_request(); let req = TestRequest::with_uri("/test").to_request();
let resp = block_fn(|| srv.call(req)).unwrap(); let resp = srv.call(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
let req = TestRequest::with_uri("/blah").to_request(); let req = TestRequest::with_uri("/blah").to_request();
let resp = block_on(srv.call(req)).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 mut srv = init_service(
@ -497,53 +516,59 @@ mod tests {
.service( .service(
web::resource("/test2") web::resource("/test2")
.default_service(|r: ServiceRequest| { .default_service(|r: ServiceRequest| {
r.into_response(HttpResponse::Created()) ok(r.into_response(HttpResponse::Created()))
}) })
.route(web::get().to(|| HttpResponse::Ok())), .route(web::get().to(|| HttpResponse::Ok())),
) )
.default_service(|r: ServiceRequest| { .default_service(|r: ServiceRequest| {
r.into_response(HttpResponse::MethodNotAllowed()) ok(r.into_response(HttpResponse::MethodNotAllowed()))
}), }),
); )
.await;
let req = TestRequest::with_uri("/blah").to_request(); let req = TestRequest::with_uri("/blah").to_request();
let resp = block_on(srv.call(req)).unwrap(); let resp = srv.call(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED); assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED);
let req = TestRequest::with_uri("/test2").to_request(); let req = TestRequest::with_uri("/test2").to_request();
let resp = block_on(srv.call(req)).unwrap(); let resp = srv.call(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
let req = TestRequest::with_uri("/test2") let req = TestRequest::with_uri("/test2")
.method(Method::POST) .method(Method::POST)
.to_request(); .to_request();
let resp = block_on(srv.call(req)).unwrap(); let resp = srv.call(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::CREATED); assert_eq!(resp.status(), StatusCode::CREATED);
})
} }
#[test] #[test]
fn test_data_factory() { fn test_data_factory() {
block_on(async {
let mut srv = let mut 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()),
)); ))
.await;
let req = TestRequest::default().to_request(); let req = TestRequest::default().to_request();
let resp = block_on(srv.call(req)).unwrap(); let resp = srv.call(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
let mut srv = let mut 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()),
)); ))
.await;
let req = TestRequest::default().to_request(); let req = TestRequest::default().to_request();
let resp = block_on(srv.call(req)).unwrap(); let resp = srv.call(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR); assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
})
} }
fn md<S, B>( fn md<S, B>(
req: ServiceRequest, req: ServiceRequest,
srv: &mut S, srv: &mut S,
) -> impl IntoFuture<Item = ServiceResponse<B>, Error = Error> ) -> impl Future<Output = Result<ServiceResponse<B>, Error>>
where where
S: Service< S: Service<
Request = ServiceRequest, Request = ServiceRequest,
@ -551,95 +576,122 @@ mod tests {
Error = Error, Error = Error,
>, >,
{ {
srv.call(req).map(|mut res| { let fut = srv.call(req);
async move {
let mut res = fut.await?;
res.headers_mut() res.headers_mut()
.insert(header::CONTENT_TYPE, HeaderValue::from_static("0001")); .insert(header::CONTENT_TYPE, HeaderValue::from_static("0001"));
res Ok(res)
}) }
} }
#[test] #[test]
fn test_wrap() { fn test_wrap() {
let mut srv = init_service( block_on(async {
let mut srv =
init_service(
App::new() App::new()
.wrap(md) .wrap(DefaultHeaders::new().header(
header::CONTENT_TYPE,
HeaderValue::from_static("0001"),
))
.route("/test", web::get().to(|| HttpResponse::Ok())), .route("/test", web::get().to(|| HttpResponse::Ok())),
); )
.await;
let req = TestRequest::with_uri("/test").to_request(); let req = TestRequest::with_uri("/test").to_request();
let resp = call_service(&mut srv, req); let resp = call_service(&mut 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(),
HeaderValue::from_static("0001") HeaderValue::from_static("0001")
); );
})
} }
#[test] #[test]
fn test_router_wrap() { fn test_router_wrap() {
let mut srv = init_service( block_on(async {
let mut srv =
init_service(
App::new() App::new()
.route("/test", web::get().to(|| HttpResponse::Ok())) .route("/test", web::get().to(|| HttpResponse::Ok()))
.wrap(md), .wrap(DefaultHeaders::new().header(
); header::CONTENT_TYPE,
HeaderValue::from_static("0001"),
)),
)
.await;
let req = TestRequest::with_uri("/test").to_request(); let req = TestRequest::with_uri("/test").to_request();
let resp = call_service(&mut srv, req); let resp = call_service(&mut 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(),
HeaderValue::from_static("0001") HeaderValue::from_static("0001")
); );
})
} }
#[test] #[test]
fn test_wrap_fn() { fn test_wrap_fn() {
block_on(async {
let mut srv = init_service( let mut srv = init_service(
App::new() App::new()
.wrap_fn(|req, srv| { .wrap_fn(|req, srv| {
srv.call(req).map(|mut res| { let fut = srv.call(req);
async move {
let mut res = fut.await?;
res.headers_mut().insert( res.headers_mut().insert(
header::CONTENT_TYPE, header::CONTENT_TYPE,
HeaderValue::from_static("0001"), HeaderValue::from_static("0001"),
); );
res Ok(res)
}) }
}) })
.service(web::resource("/test").to(|| HttpResponse::Ok())), .service(web::resource("/test").to(|| HttpResponse::Ok())),
); )
.await;
let req = TestRequest::with_uri("/test").to_request(); let req = TestRequest::with_uri("/test").to_request();
let resp = call_service(&mut srv, req); let resp = call_service(&mut 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(),
HeaderValue::from_static("0001") HeaderValue::from_static("0001")
); );
})
} }
#[test] #[test]
fn test_router_wrap_fn() { fn test_router_wrap_fn() {
block_on(async {
let mut srv = init_service( let mut srv = init_service(
App::new() App::new()
.route("/test", web::get().to(|| HttpResponse::Ok())) .route("/test", web::get().to(|| HttpResponse::Ok()))
.wrap_fn(|req, srv| { .wrap_fn(|req, srv| {
srv.call(req).map(|mut res| { let fut = srv.call(req);
async {
let mut res = fut.await?;
res.headers_mut().insert( res.headers_mut().insert(
header::CONTENT_TYPE, header::CONTENT_TYPE,
HeaderValue::from_static("0001"), HeaderValue::from_static("0001"),
); );
res Ok(res)
}) }
}), }),
); )
.await;
let req = TestRequest::with_uri("/test").to_request(); let req = TestRequest::with_uri("/test").to_request();
let resp = call_service(&mut srv, req); let resp = call_service(&mut 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(),
HeaderValue::from_static("0001") HeaderValue::from_static("0001")
); );
})
} }
#[test] #[test]
fn test_external_resource() { fn test_external_resource() {
block_on(async {
let mut srv = init_service( let mut 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}")
@ -652,11 +704,13 @@ mod tests {
)) ))
}), }),
), ),
); )
.await;
let req = TestRequest::with_uri("/test").to_request(); let req = TestRequest::with_uri("/test").to_request();
let resp = call_service(&mut srv, req); let resp = call_service(&mut srv, req).await;
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
let body = read_body(resp); 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"));
})
} }
} }

View file

@ -1,14 +1,16 @@
use std::cell::RefCell; use std::cell::RefCell;
use std::future::Future;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::pin::Pin;
use std::rc::Rc; use std::rc::Rc;
use std::task::{Context, Poll};
use actix_http::{Extensions, Request, Response}; use actix_http::{Extensions, Request, Response};
use actix_router::{Path, ResourceDef, ResourceInfo, Router, Url}; use actix_router::{Path, ResourceDef, ResourceInfo, Router, Url};
use actix_server_config::ServerConfig; use actix_server_config::ServerConfig;
use actix_service::boxed::{self, BoxedNewService, BoxedService}; use actix_service::boxed::{self, BoxedNewService, BoxedService};
use actix_service::{service_fn, NewService, Service}; use actix_service::{service_fn, Service, ServiceFactory};
use futures::future::{ok, Either, FutureResult}; use futures::future::{ok, Either, FutureExt, LocalBoxFuture, Ready};
use futures::{Async, Future, Poll};
use crate::config::{AppConfig, AppService}; use crate::config::{AppConfig, AppService};
use crate::data::DataFactory; use crate::data::DataFactory;
@ -16,23 +18,20 @@ use crate::error::Error;
use crate::guard::Guard; use crate::guard::Guard;
use crate::request::{HttpRequest, HttpRequestPool}; use crate::request::{HttpRequest, HttpRequestPool};
use crate::rmap::ResourceMap; use crate::rmap::ResourceMap;
use crate::service::{ServiceFactory, ServiceRequest, ServiceResponse}; use crate::service::{AppServiceFactory, ServiceRequest, ServiceResponse};
type Guards = Vec<Box<dyn Guard>>; type Guards = Vec<Box<dyn Guard>>;
type HttpService = BoxedService<ServiceRequest, ServiceResponse, Error>; type HttpService = BoxedService<ServiceRequest, ServiceResponse, Error>;
type HttpNewService = BoxedNewService<(), ServiceRequest, ServiceResponse, Error, ()>; type HttpNewService = BoxedNewService<(), ServiceRequest, ServiceResponse, Error, ()>;
type BoxedResponse = Either< type BoxedResponse = LocalBoxFuture<'static, Result<ServiceResponse, Error>>;
FutureResult<ServiceResponse, Error>,
Box<dyn Future<Item = ServiceResponse, Error = Error>>,
>;
type FnDataFactory = type FnDataFactory =
Box<dyn Fn() -> Box<dyn Future<Item = Box<dyn DataFactory>, Error = ()>>>; Box<dyn Fn() -> LocalBoxFuture<'static, Result<Box<dyn DataFactory>, ()>>>;
/// Service factory to convert `Request` to a `ServiceRequest<S>`. /// Service factory to convert `Request` to a `ServiceRequest<S>`.
/// It also executes data factories. /// It also executes data factories.
pub struct AppInit<T, B> pub struct AppInit<T, B>
where where
T: NewService< T: ServiceFactory<
Config = (), Config = (),
Request = ServiceRequest, Request = ServiceRequest,
Response = ServiceResponse<B>, Response = ServiceResponse<B>,
@ -44,15 +43,15 @@ where
pub(crate) data: Rc<Vec<Box<dyn DataFactory>>>, pub(crate) data: Rc<Vec<Box<dyn DataFactory>>>,
pub(crate) data_factories: Rc<Vec<FnDataFactory>>, pub(crate) data_factories: Rc<Vec<FnDataFactory>>,
pub(crate) config: RefCell<AppConfig>, pub(crate) config: RefCell<AppConfig>,
pub(crate) services: Rc<RefCell<Vec<Box<dyn ServiceFactory>>>>, pub(crate) services: Rc<RefCell<Vec<Box<dyn AppServiceFactory>>>>,
pub(crate) default: Option<Rc<HttpNewService>>, pub(crate) default: Option<Rc<HttpNewService>>,
pub(crate) factory_ref: Rc<RefCell<Option<AppRoutingFactory>>>, pub(crate) factory_ref: Rc<RefCell<Option<AppRoutingFactory>>>,
pub(crate) external: RefCell<Vec<ResourceDef>>, pub(crate) external: RefCell<Vec<ResourceDef>>,
} }
impl<T, B> NewService for AppInit<T, B> impl<T, B> ServiceFactory for AppInit<T, B>
where where
T: NewService< T: ServiceFactory<
Config = (), Config = (),
Request = ServiceRequest, Request = ServiceRequest,
Response = ServiceResponse<B>, Response = ServiceResponse<B>,
@ -71,8 +70,8 @@ where
fn new_service(&self, cfg: &ServerConfig) -> Self::Future { fn new_service(&self, cfg: &ServerConfig) -> Self::Future {
// update resource default service // update resource default service
let default = self.default.clone().unwrap_or_else(|| { let default = self.default.clone().unwrap_or_else(|| {
Rc::new(boxed::new_service(service_fn(|req: ServiceRequest| { Rc::new(boxed::factory(service_fn(|req: ServiceRequest| {
Ok(req.into_response(Response::NotFound().finish())) ok(req.into_response(Response::NotFound().finish()))
}))) })))
}); });
@ -135,23 +134,25 @@ where
} }
} }
#[pin_project::pin_project]
pub struct AppInitResult<T, B> pub struct AppInitResult<T, B>
where where
T: NewService, T: ServiceFactory,
{ {
endpoint: Option<T::Service>, endpoint: Option<T::Service>,
#[pin]
endpoint_fut: T::Future, endpoint_fut: T::Future,
rmap: Rc<ResourceMap>, rmap: Rc<ResourceMap>,
config: AppConfig, config: AppConfig,
data: Rc<Vec<Box<dyn DataFactory>>>, data: Rc<Vec<Box<dyn DataFactory>>>,
data_factories: Vec<Box<dyn DataFactory>>, data_factories: Vec<Box<dyn DataFactory>>,
data_factories_fut: Vec<Box<dyn Future<Item = Box<dyn DataFactory>, Error = ()>>>, data_factories_fut: Vec<LocalBoxFuture<'static, Result<Box<dyn DataFactory>, ()>>>,
_t: PhantomData<B>, _t: PhantomData<B>,
} }
impl<T, B> Future for AppInitResult<T, B> impl<T, B> Future for AppInitResult<T, B>
where where
T: NewService< T: ServiceFactory<
Config = (), Config = (),
Request = ServiceRequest, Request = ServiceRequest,
Response = ServiceResponse<B>, Response = ServiceResponse<B>,
@ -159,48 +160,49 @@ where
InitError = (), InitError = (),
>, >,
{ {
type Item = AppInitService<T::Service, B>; type Output = Result<AppInitService<T::Service, B>, ()>;
type Error = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
let this = self.project();
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
// async data factories // async data factories
let mut idx = 0; let mut idx = 0;
while idx < self.data_factories_fut.len() { while idx < this.data_factories_fut.len() {
match self.data_factories_fut[idx].poll()? { match Pin::new(&mut this.data_factories_fut[idx]).poll(cx)? {
Async::Ready(f) => { Poll::Ready(f) => {
self.data_factories.push(f); this.data_factories.push(f);
let _ = self.data_factories_fut.remove(idx); let _ = this.data_factories_fut.remove(idx);
} }
Async::NotReady => idx += 1, Poll::Pending => idx += 1,
} }
} }
if self.endpoint.is_none() { if this.endpoint.is_none() {
if let Async::Ready(srv) = self.endpoint_fut.poll()? { if let Poll::Ready(srv) = this.endpoint_fut.poll(cx)? {
self.endpoint = Some(srv); *this.endpoint = Some(srv);
} }
} }
if self.endpoint.is_some() && self.data_factories_fut.is_empty() { if this.endpoint.is_some() && this.data_factories_fut.is_empty() {
// create app data container // create app data container
let mut data = Extensions::new(); let mut data = Extensions::new();
for f in self.data.iter() { for f in this.data.iter() {
f.create(&mut data); f.create(&mut data);
} }
for f in &self.data_factories { for f in this.data_factories.iter() {
f.create(&mut data); f.create(&mut data);
} }
Ok(Async::Ready(AppInitService { Poll::Ready(Ok(AppInitService {
service: self.endpoint.take().unwrap(), service: this.endpoint.take().unwrap(),
rmap: self.rmap.clone(), rmap: this.rmap.clone(),
config: self.config.clone(), config: this.config.clone(),
data: Rc::new(data), data: Rc::new(data),
pool: HttpRequestPool::create(), pool: HttpRequestPool::create(),
})) }))
} else { } else {
Ok(Async::NotReady) Poll::Pending
} }
} }
} }
@ -226,8 +228,8 @@ where
type Error = T::Error; type Error = T::Error;
type Future = T::Future; type Future = T::Future;
fn poll_ready(&mut self) -> Poll<(), Self::Error> { fn poll_ready(&mut self, cx: &mut Context) -> Poll<Result<(), Self::Error>> {
self.service.poll_ready() self.service.poll_ready(cx)
} }
fn call(&mut self, req: Request) -> Self::Future { fn call(&mut self, req: Request) -> Self::Future {
@ -270,7 +272,7 @@ pub struct AppRoutingFactory {
default: Rc<HttpNewService>, default: Rc<HttpNewService>,
} }
impl NewService for AppRoutingFactory { impl ServiceFactory for AppRoutingFactory {
type Config = (); type Config = ();
type Request = ServiceRequest; type Request = ServiceRequest;
type Response = ServiceResponse; type Response = ServiceResponse;
@ -288,7 +290,7 @@ impl NewService for AppRoutingFactory {
CreateAppRoutingItem::Future( CreateAppRoutingItem::Future(
Some(path.clone()), Some(path.clone()),
guards.borrow_mut().take(), guards.borrow_mut().take(),
service.new_service(&()), service.new_service(&()).boxed_local(),
) )
}) })
.collect(), .collect(),
@ -298,14 +300,14 @@ impl NewService for AppRoutingFactory {
} }
} }
type HttpServiceFut = Box<dyn Future<Item = HttpService, Error = ()>>; type HttpServiceFut = LocalBoxFuture<'static, Result<HttpService, ()>>;
/// Create app service /// Create app service
#[doc(hidden)] #[doc(hidden)]
pub struct AppRoutingFactoryResponse { pub struct AppRoutingFactoryResponse {
fut: Vec<CreateAppRoutingItem>, fut: Vec<CreateAppRoutingItem>,
default: Option<HttpService>, default: Option<HttpService>,
default_fut: Option<Box<dyn Future<Item = HttpService, Error = ()>>>, default_fut: Option<LocalBoxFuture<'static, Result<HttpService, ()>>>,
} }
enum CreateAppRoutingItem { enum CreateAppRoutingItem {
@ -314,16 +316,15 @@ enum CreateAppRoutingItem {
} }
impl Future for AppRoutingFactoryResponse { impl Future for AppRoutingFactoryResponse {
type Item = AppRouting; type Output = Result<AppRouting, ()>;
type Error = ();
fn poll(&mut self) -> Poll<Self::Item, Self::Error> { fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
let mut done = true; let mut done = true;
if let Some(ref mut fut) = self.default_fut { if let Some(ref mut fut) = self.default_fut {
match fut.poll()? { match Pin::new(fut).poll(cx)? {
Async::Ready(default) => self.default = Some(default), Poll::Ready(default) => self.default = Some(default),
Async::NotReady => done = false, Poll::Pending => done = false,
} }
} }
@ -334,11 +335,12 @@ impl Future for AppRoutingFactoryResponse {
ref mut path, ref mut path,
ref mut guards, ref mut guards,
ref mut fut, ref mut fut,
) => match fut.poll()? { ) => match Pin::new(fut).poll(cx) {
Async::Ready(service) => { Poll::Ready(Ok(service)) => {
Some((path.take().unwrap(), guards.take(), service)) Some((path.take().unwrap(), guards.take(), service))
} }
Async::NotReady => { Poll::Ready(Err(_)) => return Poll::Ready(Err(())),
Poll::Pending => {
done = false; done = false;
None None
} }
@ -364,13 +366,13 @@ impl Future for AppRoutingFactoryResponse {
} }
router router
}); });
Ok(Async::Ready(AppRouting { Poll::Ready(Ok(AppRouting {
ready: None, ready: None,
router: router.finish(), router: router.finish(),
default: self.default.take(), default: self.default.take(),
})) }))
} else { } else {
Ok(Async::NotReady) Poll::Pending
} }
} }
} }
@ -387,11 +389,11 @@ impl Service for AppRouting {
type Error = Error; type Error = Error;
type Future = BoxedResponse; type Future = BoxedResponse;
fn poll_ready(&mut self) -> Poll<(), Self::Error> { fn poll_ready(&mut self, _: &mut Context) -> Poll<Result<(), Self::Error>> {
if self.ready.is_none() { if self.ready.is_none() {
Ok(Async::Ready(())) Poll::Ready(Ok(()))
} else { } else {
Ok(Async::NotReady) Poll::Pending
} }
} }
@ -413,7 +415,7 @@ impl Service for AppRouting {
default.call(req) default.call(req)
} else { } else {
let req = req.into_parts().0; let req = req.into_parts().0;
Either::A(ok(ServiceResponse::new(req, Response::NotFound().finish()))) ok(ServiceResponse::new(req, Response::NotFound().finish())).boxed_local()
} }
} }
} }
@ -429,7 +431,7 @@ impl AppEntry {
} }
} }
impl NewService for AppEntry { impl ServiceFactory for AppEntry {
type Config = (); type Config = ();
type Request = ServiceRequest; type Request = ServiceRequest;
type Response = ServiceResponse; type Response = ServiceResponse;
@ -464,15 +466,16 @@ mod tests {
#[test] #[test]
fn drop_data() { fn drop_data() {
let data = Arc::new(AtomicBool::new(false)); let data = Arc::new(AtomicBool::new(false));
{ test::block_on(async {
let mut app = test::init_service( let mut app = test::init_service(
App::new() App::new()
.data(DropData(data.clone())) .data(DropData(data.clone()))
.service(web::resource("/test").to(|| HttpResponse::Ok())), .service(web::resource("/test").to(|| HttpResponse::Ok())),
); )
.await;
let req = test::TestRequest::with_uri("/test").to_request(); let req = test::TestRequest::with_uri("/test").to_request();
let _ = test::block_on(app.call(req)).unwrap(); let _ = app.call(req).await.unwrap();
} });
assert!(data.load(Ordering::Relaxed)); assert!(data.load(Ordering::Relaxed));
} }
} }

View file

@ -3,7 +3,7 @@ use std::rc::Rc;
use actix_http::Extensions; use actix_http::Extensions;
use actix_router::ResourceDef; use actix_router::ResourceDef;
use actix_service::{boxed, IntoNewService, NewService}; use actix_service::{boxed, IntoServiceFactory, ServiceFactory};
use crate::data::{Data, DataFactory}; use crate::data::{Data, DataFactory};
use crate::error::Error; use crate::error::Error;
@ -12,7 +12,7 @@ use crate::resource::Resource;
use crate::rmap::ResourceMap; use crate::rmap::ResourceMap;
use crate::route::Route; use crate::route::Route;
use crate::service::{ use crate::service::{
HttpServiceFactory, ServiceFactory, ServiceFactoryWrapper, ServiceRequest, AppServiceFactory, HttpServiceFactory, ServiceFactoryWrapper, ServiceRequest,
ServiceResponse, ServiceResponse,
}; };
@ -102,11 +102,11 @@ impl AppService {
&mut self, &mut self,
rdef: ResourceDef, rdef: ResourceDef,
guards: Option<Vec<Box<dyn Guard>>>, guards: Option<Vec<Box<dyn Guard>>>,
service: F, factory: F,
nested: Option<Rc<ResourceMap>>, nested: Option<Rc<ResourceMap>>,
) where ) where
F: IntoNewService<S>, F: IntoServiceFactory<S>,
S: NewService< S: ServiceFactory<
Config = (), Config = (),
Request = ServiceRequest, Request = ServiceRequest,
Response = ServiceResponse, Response = ServiceResponse,
@ -116,7 +116,7 @@ impl AppService {
{ {
self.services.push(( self.services.push((
rdef, rdef,
boxed::new_service(service.into_new_service()), boxed::factory(factory.into_factory()),
guards, guards,
nested, nested,
)); ));
@ -174,7 +174,7 @@ impl Default for AppConfigInner {
/// to set of external methods. This could help with /// to set of external methods. This could help with
/// modularization of big application configuration. /// modularization of big application configuration.
pub struct ServiceConfig { pub struct ServiceConfig {
pub(crate) services: Vec<Box<dyn ServiceFactory>>, pub(crate) services: Vec<Box<dyn AppServiceFactory>>,
pub(crate) data: Vec<Box<dyn DataFactory>>, pub(crate) data: Vec<Box<dyn DataFactory>>,
pub(crate) external: Vec<ResourceDef>, pub(crate) external: Vec<ResourceDef>,
} }
@ -251,17 +251,19 @@ mod tests {
#[test] #[test]
fn test_data() { fn test_data() {
block_on(async {
let cfg = |cfg: &mut ServiceConfig| { let cfg = |cfg: &mut ServiceConfig| {
cfg.data(10usize); cfg.data(10usize);
}; };
let mut srv = let mut 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()),
)); ))
.await;
let req = TestRequest::default().to_request(); let req = TestRequest::default().to_request();
let resp = block_on(srv.call(req)).unwrap(); let resp = srv.call(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
})
} }
// #[test] // #[test]
@ -298,6 +300,7 @@ mod tests {
#[test] #[test]
fn test_external_resource() { fn test_external_resource() {
block_on(async {
let mut srv = init_service( let mut srv = init_service(
App::new() App::new()
.configure(|cfg| { .configure(|cfg| {
@ -315,33 +318,39 @@ mod tests {
)) ))
}), }),
), ),
); )
.await;
let req = TestRequest::with_uri("/test").to_request(); let req = TestRequest::with_uri("/test").to_request();
let resp = call_service(&mut srv, req); let resp = call_service(&mut srv, req).await;
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
let body = read_body(resp); 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"));
})
} }
#[test] #[test]
fn test_service() { fn test_service() {
block_on(async {
let mut srv = init_service(App::new().configure(|cfg| { let mut srv = init_service(App::new().configure(|cfg| {
cfg.service( cfg.service(
web::resource("/test").route(web::get().to(|| HttpResponse::Created())), web::resource("/test")
.route(web::get().to(|| HttpResponse::Created())),
) )
.route("/index.html", web::get().to(|| HttpResponse::Ok())); .route("/index.html", web::get().to(|| HttpResponse::Ok()));
})); }))
.await;
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); let resp = call_service(&mut 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); let resp = call_service(&mut srv, req).await;
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
})
} }
} }

View file

@ -3,6 +3,7 @@ use std::sync::Arc;
use actix_http::error::{Error, ErrorInternalServerError}; use actix_http::error::{Error, ErrorInternalServerError};
use actix_http::Extensions; use actix_http::Extensions;
use futures::future::{err, ok, Ready};
use crate::dev::Payload; use crate::dev::Payload;
use crate::extract::FromRequest; use crate::extract::FromRequest;
@ -101,19 +102,19 @@ impl<T> Clone for Data<T> {
impl<T: 'static> FromRequest for Data<T> { impl<T: 'static> FromRequest for Data<T> {
type Config = (); type Config = ();
type Error = Error; type Error = Error;
type Future = Result<Self, Error>; type Future = Ready<Result<Self, Error>>;
#[inline] #[inline]
fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future { fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
if let Some(st) = req.get_app_data::<T>() { if let Some(st) = req.get_app_data::<T>() {
Ok(st) ok(st)
} else { } else {
log::debug!( log::debug!(
"Failed to construct App-level Data extractor. \ "Failed to construct App-level Data extractor. \
Request path: {:?}", Request path: {:?}",
req.path() req.path()
); );
Err(ErrorInternalServerError( err(ErrorInternalServerError(
"App data is not configured, to configure use App::data()", "App data is not configured, to configure use App::data()",
)) ))
} }
@ -142,56 +143,65 @@ mod tests {
#[test] #[test]
fn test_data_extractor() { fn test_data_extractor() {
let mut srv = block_on(async {
init_service(App::new().data(10usize).service( let mut srv = init_service(App::new().data(10usize).service(
web::resource("/").to(|_: web::Data<usize>| HttpResponse::Ok()), web::resource("/").to(|_: web::Data<usize>| HttpResponse::Ok()),
)); ))
.await;
let req = TestRequest::default().to_request(); let req = TestRequest::default().to_request();
let resp = block_on(srv.call(req)).unwrap(); let resp = srv.call(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
let mut srv = let mut 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()),
)); ))
.await;
let req = TestRequest::default().to_request(); let req = TestRequest::default().to_request();
let resp = block_on(srv.call(req)).unwrap(); let resp = srv.call(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR); assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
})
} }
#[test] #[test]
fn test_register_data_extractor() { fn test_register_data_extractor() {
block_on(async {
let mut srv = let mut srv =
init_service(App::new().register_data(Data::new(10usize)).service( init_service(App::new().register_data(Data::new(10usize)).service(
web::resource("/").to(|_: web::Data<usize>| HttpResponse::Ok()), web::resource("/").to(|_: web::Data<usize>| HttpResponse::Ok()),
)); ))
.await;
let req = TestRequest::default().to_request(); let req = TestRequest::default().to_request();
let resp = block_on(srv.call(req)).unwrap(); let resp = srv.call(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
let mut srv = let mut srv =
init_service(App::new().register_data(Data::new(10u32)).service( init_service(App::new().register_data(Data::new(10u32)).service(
web::resource("/").to(|_: web::Data<usize>| HttpResponse::Ok()), web::resource("/").to(|_: web::Data<usize>| HttpResponse::Ok()),
)); ))
.await;
let req = TestRequest::default().to_request(); let req = TestRequest::default().to_request();
let resp = block_on(srv.call(req)).unwrap(); let resp = srv.call(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR); assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
})
} }
#[test] #[test]
fn test_route_data_extractor() { fn test_route_data_extractor() {
let mut srv = block_on(async {
init_service(App::new().service(web::resource("/").data(10usize).route( let mut srv = init_service(App::new().service(
web::get().to(|data: web::Data<usize>| { web::resource("/").data(10usize).route(web::get().to(
|data: web::Data<usize>| {
let _ = data.clone(); let _ = data.clone();
HttpResponse::Ok() HttpResponse::Ok()
}), },
))); )),
))
.await;
let req = TestRequest::default().to_request(); let req = TestRequest::default().to_request();
let resp = block_on(srv.call(req)).unwrap(); let resp = srv.call(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
// different type // different type
@ -201,14 +211,17 @@ mod tests {
.data(10u32) .data(10u32)
.route(web::get().to(|_: web::Data<usize>| HttpResponse::Ok())), .route(web::get().to(|_: web::Data<usize>| HttpResponse::Ok())),
), ),
); )
.await;
let req = TestRequest::default().to_request(); let req = TestRequest::default().to_request();
let resp = block_on(srv.call(req)).unwrap(); let resp = srv.call(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR); assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
})
} }
#[test] #[test]
fn test_override_data() { fn test_override_data() {
block_on(async {
let mut srv = init_service(App::new().data(1usize).service( let mut 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>| {
@ -217,10 +230,12 @@ mod tests {
HttpResponse::Ok() HttpResponse::Ok()
}, },
)), )),
)); ))
.await;
let req = TestRequest::default().to_request(); let req = TestRequest::default().to_request();
let resp = block_on(srv.call(req)).unwrap(); let resp = srv.call(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
})
} }
} }

View file

@ -1,8 +1,10 @@
//! Request extractors //! Request extractors
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use actix_http::error::Error; use actix_http::error::Error;
use futures::future::ok; use futures::future::{ok, FutureExt, LocalBoxFuture, Ready};
use futures::{future, Async, Future, IntoFuture, Poll};
use crate::dev::Payload; use crate::dev::Payload;
use crate::request::HttpRequest; use crate::request::HttpRequest;
@ -15,7 +17,7 @@ pub trait FromRequest: Sized {
type Error: Into<Error>; type Error: Into<Error>;
/// Future that resolves to a Self /// Future that resolves to a Self
type Future: IntoFuture<Item = Self, Error = Self::Error>; type Future: Future<Output = Result<Self, Self::Error>>;
/// Configuration for this extractor /// Configuration for this extractor
type Config: Default + 'static; type Config: Default + 'static;
@ -48,6 +50,7 @@ pub trait FromRequest: Sized {
/// ```rust /// ```rust
/// use actix_web::{web, dev, App, Error, HttpRequest, FromRequest}; /// use actix_web::{web, dev, App, Error, HttpRequest, FromRequest};
/// use actix_web::error::ErrorBadRequest; /// use actix_web::error::ErrorBadRequest;
/// use futures::future::{ok, err, Ready};
/// use serde_derive::Deserialize; /// use serde_derive::Deserialize;
/// use rand; /// use rand;
/// ///
@ -58,14 +61,14 @@ pub trait FromRequest: Sized {
/// ///
/// impl FromRequest for Thing { /// impl FromRequest for Thing {
/// type Error = Error; /// type Error = Error;
/// type Future = Result<Self, Self::Error>; /// type Future = Ready<Result<Self, Self::Error>>;
/// type Config = (); /// type Config = ();
/// ///
/// fn from_request(req: &HttpRequest, payload: &mut dev::Payload) -> Self::Future { /// fn from_request(req: &HttpRequest, payload: &mut dev::Payload) -> Self::Future {
/// if rand::random() { /// if rand::random() {
/// Ok(Thing { name: "thingy".into() }) /// ok(Thing { name: "thingy".into() })
/// } else { /// } else {
/// Err(ErrorBadRequest("no luck")) /// err(ErrorBadRequest("no luck"))
/// } /// }
/// ///
/// } /// }
@ -94,21 +97,19 @@ where
{ {
type Config = T::Config; type Config = T::Config;
type Error = Error; type Error = Error;
type Future = Box<dyn Future<Item = Option<T>, Error = Error>>; type Future = LocalBoxFuture<'static, Result<Option<T>, Error>>;
#[inline] #[inline]
fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future { fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {
Box::new(
T::from_request(req, payload) T::from_request(req, payload)
.into_future()
.then(|r| match r { .then(|r| match r {
Ok(v) => future::ok(Some(v)), Ok(v) => ok(Some(v)),
Err(e) => { Err(e) => {
log::debug!("Error for Option<T> extractor: {}", e.into()); log::debug!("Error for Option<T> extractor: {}", e.into());
future::ok(None) ok(None)
} }
}), })
) .boxed_local()
} }
} }
@ -121,6 +122,7 @@ where
/// ```rust /// ```rust
/// use actix_web::{web, dev, App, Result, Error, HttpRequest, FromRequest}; /// use actix_web::{web, dev, App, Result, Error, HttpRequest, FromRequest};
/// use actix_web::error::ErrorBadRequest; /// use actix_web::error::ErrorBadRequest;
/// use futures::future::{ok, err, Ready};
/// use serde_derive::Deserialize; /// use serde_derive::Deserialize;
/// use rand; /// use rand;
/// ///
@ -131,14 +133,14 @@ where
/// ///
/// impl FromRequest for Thing { /// impl FromRequest for Thing {
/// type Error = Error; /// type Error = Error;
/// type Future = Result<Thing, Error>; /// type Future = Ready<Result<Thing, Error>>;
/// type Config = (); /// type Config = ();
/// ///
/// fn from_request(req: &HttpRequest, payload: &mut dev::Payload) -> Self::Future { /// fn from_request(req: &HttpRequest, payload: &mut dev::Payload) -> Self::Future {
/// if rand::random() { /// if rand::random() {
/// Ok(Thing { name: "thingy".into() }) /// ok(Thing { name: "thingy".into() })
/// } else { /// } else {
/// Err(ErrorBadRequest("no luck")) /// err(ErrorBadRequest("no luck"))
/// } /// }
/// } /// }
/// } /// }
@ -157,26 +159,24 @@ where
/// ); /// );
/// } /// }
/// ``` /// ```
impl<T: 'static> FromRequest for Result<T, T::Error> impl<T> FromRequest for Result<T, T::Error>
where where
T: FromRequest, T: FromRequest + 'static,
T::Future: 'static,
T::Error: 'static, T::Error: 'static,
T::Future: 'static,
{ {
type Config = T::Config; type Config = T::Config;
type Error = Error; type Error = Error;
type Future = Box<dyn Future<Item = Result<T, T::Error>, Error = Error>>; type Future = LocalBoxFuture<'static, Result<Result<T, T::Error>, Error>>;
#[inline] #[inline]
fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future { fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {
Box::new(
T::from_request(req, payload) T::from_request(req, payload)
.into_future()
.then(|res| match res { .then(|res| match res {
Ok(v) => ok(Ok(v)), Ok(v) => ok(Ok(v)),
Err(e) => ok(Err(e)), Err(e) => ok(Err(e)),
}), })
) .boxed_local()
} }
} }
@ -184,10 +184,10 @@ where
impl FromRequest for () { impl FromRequest for () {
type Config = (); type Config = ();
type Error = Error; type Error = Error;
type Future = Result<(), Error>; type Future = Ready<Result<(), Error>>;
fn from_request(_: &HttpRequest, _: &mut Payload) -> Self::Future { fn from_request(_: &HttpRequest, _: &mut Payload) -> Self::Future {
Ok(()) ok(())
} }
} }
@ -204,43 +204,44 @@ macro_rules! tuple_from_req ({$fut_type:ident, $(($n:tt, $T:ident)),+} => {
fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future { fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {
$fut_type { $fut_type {
items: <($(Option<$T>,)+)>::default(), items: <($(Option<$T>,)+)>::default(),
futs: ($($T::from_request(req, payload).into_future(),)+), futs: ($($T::from_request(req, payload),)+),
} }
} }
} }
#[doc(hidden)] #[doc(hidden)]
#[pin_project::pin_project]
pub struct $fut_type<$($T: FromRequest),+> { pub struct $fut_type<$($T: FromRequest),+> {
items: ($(Option<$T>,)+), items: ($(Option<$T>,)+),
futs: ($(<$T::Future as futures::IntoFuture>::Future,)+), futs: ($($T::Future,)+),
} }
impl<$($T: FromRequest),+> Future for $fut_type<$($T),+> impl<$($T: FromRequest),+> Future for $fut_type<$($T),+>
{ {
type Item = ($($T,)+); type Output = Result<($($T,)+), Error>;
type Error = Error;
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
let this = self.project();
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
let mut ready = true; let mut ready = true;
$( $(
if self.items.$n.is_none() { if this.items.$n.is_none() {
match self.futs.$n.poll() { match unsafe { Pin::new_unchecked(&mut this.futs.$n) }.poll(cx) {
Ok(Async::Ready(item)) => { Poll::Ready(Ok(item)) => {
self.items.$n = Some(item); this.items.$n = Some(item);
} }
Ok(Async::NotReady) => ready = false, Poll::Pending => ready = false,
Err(e) => return Err(e.into()), Poll::Ready(Err(e)) => return Poll::Ready(Err(e.into())),
} }
} }
)+ )+
if ready { if ready {
Ok(Async::Ready( Poll::Ready(Ok(
($(self.items.$n.take().unwrap(),)+) ($(this.items.$n.take().unwrap(),)+)
)) ))
} else { } else {
Ok(Async::NotReady) Poll::Pending
} }
} }
} }

View file

@ -1,10 +1,14 @@
use std::convert::Infallible; use std::convert::Infallible;
use std::future::Future;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::pin::Pin;
use std::task::{Context, Poll};
use actix_http::{Error, Response}; use actix_http::{Error, Response};
use actix_service::{NewService, Service}; use actix_service::{Service, ServiceFactory};
use futures::future::{ok, FutureResult}; use futures::future::{ok, Ready};
use futures::{try_ready, Async, Future, IntoFuture, Poll}; use futures::ready;
use pin_project::pin_project;
use crate::extract::FromRequest; use crate::extract::FromRequest;
use crate::request::HttpRequest; use crate::request::HttpRequest;
@ -73,14 +77,14 @@ where
type Request = (T, HttpRequest); type Request = (T, HttpRequest);
type Response = ServiceResponse; type Response = ServiceResponse;
type Error = Infallible; type Error = Infallible;
type Future = HandlerServiceResponse<<R::Future as IntoFuture>::Future>; type Future = HandlerServiceResponse<R>;
fn poll_ready(&mut self) -> Poll<(), Self::Error> { fn poll_ready(&mut self, _: &mut Context) -> Poll<Result<(), Self::Error>> {
Ok(Async::Ready(())) Poll::Ready(Ok(()))
} }
fn call(&mut self, (param, req): (T, HttpRequest)) -> Self::Future { fn call(&mut self, (param, req): (T, HttpRequest)) -> Self::Future {
let fut = self.hnd.call(param).respond_to(&req).into_future(); let fut = self.hnd.call(param).respond_to(&req);
HandlerServiceResponse { HandlerServiceResponse {
fut, fut,
req: Some(req), req: Some(req),
@ -88,53 +92,48 @@ where
} }
} }
pub struct HandlerServiceResponse<T> { #[pin_project]
fut: T, pub struct HandlerServiceResponse<T: Responder> {
#[pin]
fut: T::Future,
req: Option<HttpRequest>, req: Option<HttpRequest>,
} }
impl<T> Future for HandlerServiceResponse<T> impl<T: Responder> Future for HandlerServiceResponse<T> {
where type Output = Result<ServiceResponse, Infallible>;
T: Future<Item = Response>,
T::Error: Into<Error>,
{
type Item = ServiceResponse;
type Error = Infallible;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> { fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
match self.fut.poll() { let this = self.project();
Ok(Async::Ready(res)) => Ok(Async::Ready(ServiceResponse::new(
self.req.take().unwrap(), match this.fut.poll(cx) {
res, Poll::Ready(Ok(res)) => {
))), Poll::Ready(Ok(ServiceResponse::new(this.req.take().unwrap(), res)))
Ok(Async::NotReady) => Ok(Async::NotReady), }
Err(e) => { Poll::Pending => Poll::Pending,
Poll::Ready(Err(e)) => {
let res: Response = e.into().into(); let res: Response = e.into().into();
Ok(Async::Ready(ServiceResponse::new( Poll::Ready(Ok(ServiceResponse::new(this.req.take().unwrap(), res)))
self.req.take().unwrap(),
res,
)))
} }
} }
} }
} }
/// Async handler converter factory /// Async handler converter factory
pub trait AsyncFactory<T, R>: Clone + 'static pub trait AsyncFactory<T, R, O, E>: Clone + 'static
where where
R: IntoFuture, R: Future<Output = Result<O, E>>,
R::Item: Responder, O: Responder,
R::Error: Into<Error>, E: Into<Error>,
{ {
fn call(&self, param: T) -> R; fn call(&self, param: T) -> R;
} }
impl<F, R> AsyncFactory<(), R> for F impl<F, R, O, E> AsyncFactory<(), R, O, E> for F
where where
F: Fn() -> R + Clone + 'static, F: Fn() -> R + Clone + 'static,
R: IntoFuture, R: Future<Output = Result<O, E>>,
R::Item: Responder, O: Responder,
R::Error: Into<Error>, E: Into<Error>,
{ {
fn call(&self, _: ()) -> R { fn call(&self, _: ()) -> R {
(self)() (self)()
@ -142,23 +141,23 @@ where
} }
#[doc(hidden)] #[doc(hidden)]
pub struct AsyncHandler<F, T, R> pub struct AsyncHandler<F, T, R, O, E>
where where
F: AsyncFactory<T, R>, F: AsyncFactory<T, R, O, E>,
R: IntoFuture, R: Future<Output = Result<O, E>>,
R::Item: Responder, O: Responder,
R::Error: Into<Error>, E: Into<Error>,
{ {
hnd: F, hnd: F,
_t: PhantomData<(T, R)>, _t: PhantomData<(T, R, O, E)>,
} }
impl<F, T, R> AsyncHandler<F, T, R> impl<F, T, R, O, E> AsyncHandler<F, T, R, O, E>
where where
F: AsyncFactory<T, R>, F: AsyncFactory<T, R, O, E>,
R: IntoFuture, R: Future<Output = Result<O, E>>,
R::Item: Responder, O: Responder,
R::Error: Into<Error>, E: Into<Error>,
{ {
pub fn new(hnd: F) -> Self { pub fn new(hnd: F) -> Self {
AsyncHandler { AsyncHandler {
@ -168,12 +167,12 @@ where
} }
} }
impl<F, T, R> Clone for AsyncHandler<F, T, R> impl<F, T, R, O, E> Clone for AsyncHandler<F, T, R, O, E>
where where
F: AsyncFactory<T, R>, F: AsyncFactory<T, R, O, E>,
R: IntoFuture, R: Future<Output = Result<O, E>>,
R::Item: Responder, O: Responder,
R::Error: Into<Error>, E: Into<Error>,
{ {
fn clone(&self) -> Self { fn clone(&self) -> Self {
AsyncHandler { AsyncHandler {
@ -183,25 +182,25 @@ where
} }
} }
impl<F, T, R> Service for AsyncHandler<F, T, R> impl<F, T, R, O, E> Service for AsyncHandler<F, T, R, O, E>
where where
F: AsyncFactory<T, R>, F: AsyncFactory<T, R, O, E>,
R: IntoFuture, R: Future<Output = Result<O, E>>,
R::Item: Responder, O: Responder,
R::Error: Into<Error>, E: Into<Error>,
{ {
type Request = (T, HttpRequest); type Request = (T, HttpRequest);
type Response = ServiceResponse; type Response = ServiceResponse;
type Error = Infallible; type Error = Infallible;
type Future = AsyncHandlerServiceResponse<R::Future>; type Future = AsyncHandlerServiceResponse<R, O, E>;
fn poll_ready(&mut self) -> Poll<(), Self::Error> { fn poll_ready(&mut self, _: &mut Context) -> Poll<Result<(), Self::Error>> {
Ok(Async::Ready(())) Poll::Ready(Ok(()))
} }
fn call(&mut self, (param, req): (T, HttpRequest)) -> Self::Future { fn call(&mut self, (param, req): (T, HttpRequest)) -> Self::Future {
AsyncHandlerServiceResponse { AsyncHandlerServiceResponse {
fut: self.hnd.call(param).into_future(), fut: self.hnd.call(param),
fut2: None, fut2: None,
req: Some(req), req: Some(req),
} }
@ -209,56 +208,54 @@ where
} }
#[doc(hidden)] #[doc(hidden)]
pub struct AsyncHandlerServiceResponse<T> #[pin_project]
pub struct AsyncHandlerServiceResponse<T, R, E>
where where
T: Future, T: Future<Output = Result<R, E>>,
T::Item: Responder, R: Responder,
E: Into<Error>,
{ {
#[pin]
fut: T, fut: T,
fut2: Option<<<T::Item as Responder>::Future as IntoFuture>::Future>, #[pin]
fut2: Option<R::Future>,
req: Option<HttpRequest>, req: Option<HttpRequest>,
} }
impl<T> Future for AsyncHandlerServiceResponse<T> impl<T, R, E> Future for AsyncHandlerServiceResponse<T, R, E>
where where
T: Future, T: Future<Output = Result<R, E>>,
T::Item: Responder, R: Responder,
T::Error: Into<Error>, E: Into<Error>,
{ {
type Item = ServiceResponse; type Output = Result<ServiceResponse, Infallible>;
type Error = Infallible;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> { fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
if let Some(ref mut fut) = self.fut2 { let this = self.as_mut().project();
return match fut.poll() {
Ok(Async::Ready(res)) => Ok(Async::Ready(ServiceResponse::new( if let Some(fut) = this.fut2.as_pin_mut() {
self.req.take().unwrap(), return match fut.poll(cx) {
res, Poll::Ready(Ok(res)) => {
))), Poll::Ready(Ok(ServiceResponse::new(this.req.take().unwrap(), res)))
Ok(Async::NotReady) => Ok(Async::NotReady), }
Err(e) => { Poll::Pending => Poll::Pending,
Poll::Ready(Err(e)) => {
let res: Response = e.into().into(); let res: Response = e.into().into();
Ok(Async::Ready(ServiceResponse::new( Poll::Ready(Ok(ServiceResponse::new(this.req.take().unwrap(), res)))
self.req.take().unwrap(),
res,
)))
} }
}; };
} }
match self.fut.poll() { match this.fut.poll(cx) {
Ok(Async::Ready(res)) => { Poll::Ready(Ok(res)) => {
self.fut2 = let fut = res.respond_to(this.req.as_ref().unwrap());
Some(res.respond_to(self.req.as_ref().unwrap()).into_future()); self.as_mut().project().fut2.set(Some(fut));
self.poll() self.poll(cx)
} }
Ok(Async::NotReady) => Ok(Async::NotReady), Poll::Pending => Poll::Pending,
Err(e) => { Poll::Ready(Err(e)) => {
let res: Response = e.into().into(); let res: Response = e.into().into();
Ok(Async::Ready(ServiceResponse::new( Poll::Ready(Ok(ServiceResponse::new(this.req.take().unwrap(), res)))
self.req.take().unwrap(),
res,
)))
} }
} }
} }
@ -279,7 +276,7 @@ impl<T: FromRequest, S> Extract<T, S> {
} }
} }
impl<T: FromRequest, S> NewService for Extract<T, S> impl<T: FromRequest, S> ServiceFactory for Extract<T, S>
where where
S: Service< S: Service<
Request = (T, HttpRequest), Request = (T, HttpRequest),
@ -293,7 +290,7 @@ where
type Error = (Error, ServiceRequest); type Error = (Error, ServiceRequest);
type InitError = (); type InitError = ();
type Service = ExtractService<T, S>; type Service = ExtractService<T, S>;
type Future = FutureResult<Self::Service, ()>; type Future = Ready<Result<Self::Service, ()>>;
fn new_service(&self, _: &()) -> Self::Future { fn new_service(&self, _: &()) -> Self::Future {
ok(ExtractService { ok(ExtractService {
@ -321,13 +318,13 @@ where
type Error = (Error, ServiceRequest); type Error = (Error, ServiceRequest);
type Future = ExtractResponse<T, S>; type Future = ExtractResponse<T, S>;
fn poll_ready(&mut self) -> Poll<(), Self::Error> { fn poll_ready(&mut self, _: &mut Context) -> Poll<Result<(), Self::Error>> {
Ok(Async::Ready(())) Poll::Ready(Ok(()))
} }
fn call(&mut self, req: ServiceRequest) -> Self::Future { fn call(&mut 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).into_future(); let fut = T::from_request(&req, &mut payload);
ExtractResponse { ExtractResponse {
fut, fut,
@ -338,10 +335,13 @@ where
} }
} }
#[pin_project]
pub struct ExtractResponse<T: FromRequest, S: Service> { pub struct ExtractResponse<T: FromRequest, S: Service> {
req: HttpRequest, req: HttpRequest,
service: S, service: S,
fut: <T::Future as IntoFuture>::Future, #[pin]
fut: T::Future,
#[pin]
fut_s: Option<S::Future>, fut_s: Option<S::Future>,
} }
@ -353,21 +353,26 @@ where
Error = Infallible, Error = Infallible,
>, >,
{ {
type Item = ServiceResponse; type Output = Result<ServiceResponse, (Error, ServiceRequest)>;
type Error = (Error, ServiceRequest);
fn poll(&mut self) -> Poll<Self::Item, Self::Error> { fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
if let Some(ref mut fut) = self.fut_s { let this = self.as_mut().project();
return fut.poll().map_err(|_| panic!());
if let Some(fut) = this.fut_s.as_pin_mut() {
return fut.poll(cx).map_err(|_| panic!());
} }
let item = try_ready!(self.fut.poll().map_err(|e| { match ready!(this.fut.poll(cx)) {
let req = ServiceRequest::new(self.req.clone()); Err(e) => {
(e.into(), req) let req = ServiceRequest::new(this.req.clone());
})); Poll::Ready(Err((e.into(), req)))
}
self.fut_s = Some(self.service.call((item, self.req.clone()))); Ok(item) => {
self.poll() let fut = Some(this.service.call((item, this.req.clone())));
self.as_mut().project().fut_s.set(fut);
self.poll(cx)
}
}
} }
} }
@ -382,11 +387,11 @@ macro_rules! factory_tuple ({ $(($n:tt, $T:ident)),+} => {
} }
} }
impl<Func, $($T,)+ Res> AsyncFactory<($($T,)+), Res> for Func impl<Func, $($T,)+ Res, O, E1> AsyncFactory<($($T,)+), Res, O, E1> for Func
where Func: Fn($($T,)+) -> Res + Clone + 'static, where Func: Fn($($T,)+) -> Res + Clone + 'static,
Res: IntoFuture, Res: Future<Output = Result<O, E1>>,
Res::Item: Responder, O: Responder,
Res::Error: Into<Error>, E1: Into<Error>,
{ {
fn call(&self, param: ($($T,)+)) -> Res { fn call(&self, param: ($($T,)+)) -> Res {
(self)($(param.$n,)+) (self)($(param.$n,)+)

View file

@ -1,4 +1,4 @@
#![allow(clippy::borrow_interior_mutable_const)] #![allow(clippy::borrow_interior_mutable_const, unused_imports, dead_code)]
//! Actix web is a small, pragmatic, and extremely fast web framework //! Actix web is a small, pragmatic, and extremely fast web framework
//! for Rust. //! for Rust.
//! //!
@ -68,8 +68,8 @@
//! ## Package feature //! ## Package feature
//! //!
//! * `client` - enables http client (default enabled) //! * `client` - enables http client (default enabled)
//! * `ssl` - enables ssl support via `openssl` crate, supports `http/2` //! * `openssl` - enables ssl support via `openssl` crate, supports `http/2`
//! * `rust-tls` - enables ssl support via `rustls` crate, supports `http/2` //! * `rustls` - enables ssl support via `rustls` crate, supports `http/2`
//! * `secure-cookies` - enables secure cookies support, includes `ring` crate as //! * `secure-cookies` - enables secure cookies support, includes `ring` crate as
//! dependency //! dependency
//! * `brotli` - enables `brotli` compression support, requires `c` //! * `brotli` - enables `brotli` compression support, requires `c`
@ -78,7 +78,6 @@
//! `c` compiler (default enabled) //! `c` compiler (default enabled)
//! * `flate2-rust` - experimental rust based implementation for //! * `flate2-rust` - experimental rust based implementation for
//! `gzip`, `deflate` compression. //! `gzip`, `deflate` compression.
//! * `uds` - Unix domain support, enables `HttpServer::bind_uds()` method.
//! //!
#![allow(clippy::type_complexity, clippy::new_without_default)] #![allow(clippy::type_complexity, clippy::new_without_default)]
@ -143,9 +142,10 @@ pub mod dev {
pub use crate::service::{ pub use crate::service::{
HttpServiceFactory, ServiceRequest, ServiceResponse, WebService, HttpServiceFactory, ServiceRequest, ServiceResponse, WebService,
}; };
pub use crate::types::form::UrlEncoded;
pub use crate::types::json::JsonBody; //pub use crate::types::form::UrlEncoded;
pub use crate::types::readlines::Readlines; //pub use crate::types::json::JsonBody;
//pub use crate::types::readlines::Readlines;
pub use actix_http::body::{Body, BodySize, MessageBody, ResponseBody, SizedStream}; pub use actix_http::body::{Body, BodySize, MessageBody, ResponseBody, SizedStream};
pub use actix_http::encoding::Decoder as Decompress; pub use actix_http::encoding::Decoder as Decompress;
@ -176,18 +176,16 @@ pub mod client {
//! use actix_web::client::Client; //! use actix_web::client::Client;
//! //!
//! fn main() { //! fn main() {
//! System::new("test").block_on(lazy(|| { //! System::new("test").block_on(async {
//! let mut client = Client::default(); //! let mut client = Client::default();
//! //!
//! client.get("http://www.rust-lang.org") // <- Create request builder //! // Create request builder and send request
//! let response = client.get("http://www.rust-lang.org")
//! .header("User-Agent", "Actix-web") //! .header("User-Agent", "Actix-web")
//! .send() // <- Send http request //! .send().await; // <- Send http request
//! .map_err(|_| ()) //!
//! .and_then(|response| { // <- server http response
//! println!("Response: {:?}", response); //! println!("Response: {:?}", response);
//! Ok(()) //! });
//! })
//! }));
//! } //! }
//! ``` //! ```
pub use awc::error::{ pub use awc::error::{

View file

@ -1,15 +1,18 @@
//! `Middleware` for compressing response body. //! `Middleware` for compressing response body.
use std::cmp; use std::cmp;
use std::future::Future;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::pin::Pin;
use std::str::FromStr; use std::str::FromStr;
use std::task::{Context, Poll};
use actix_http::body::MessageBody; use actix_http::body::MessageBody;
use actix_http::encoding::Encoder; use actix_http::encoding::Encoder;
use actix_http::http::header::{ContentEncoding, ACCEPT_ENCODING}; use actix_http::http::header::{ContentEncoding, ACCEPT_ENCODING};
use actix_http::{Error, Response, ResponseBuilder}; use actix_http::{Error, Response, ResponseBuilder};
use actix_service::{Service, Transform}; use actix_service::{Service, Transform};
use futures::future::{ok, FutureResult}; use futures::future::{ok, Ready};
use futures::{Async, Future, Poll}; use pin_project::pin_project;
use crate::service::{ServiceRequest, ServiceResponse}; use crate::service::{ServiceRequest, ServiceResponse};
@ -78,7 +81,7 @@ where
type Error = Error; type Error = Error;
type InitError = (); type InitError = ();
type Transform = CompressMiddleware<S>; type Transform = CompressMiddleware<S>;
type Future = FutureResult<Self::Transform, Self::InitError>; type Future = Ready<Result<Self::Transform, Self::InitError>>;
fn new_transform(&self, service: S) -> Self::Future { fn new_transform(&self, service: S) -> Self::Future {
ok(CompressMiddleware { ok(CompressMiddleware {
@ -103,8 +106,8 @@ where
type Error = Error; type Error = Error;
type Future = CompressResponse<S, B>; type Future = CompressResponse<S, B>;
fn poll_ready(&mut self) -> Poll<(), Self::Error> { fn poll_ready(&mut self, cx: &mut Context) -> Poll<Result<(), Self::Error>> {
self.service.poll_ready() self.service.poll_ready(cx)
} }
fn call(&mut self, req: ServiceRequest) -> Self::Future { fn call(&mut self, req: ServiceRequest) -> Self::Future {
@ -128,11 +131,13 @@ where
} }
#[doc(hidden)] #[doc(hidden)]
#[pin_project]
pub struct CompressResponse<S, B> pub struct CompressResponse<S, B>
where where
S: Service, S: Service,
B: MessageBody, B: MessageBody,
{ {
#[pin]
fut: S::Future, fut: S::Future,
encoding: ContentEncoding, encoding: ContentEncoding,
_t: PhantomData<(B)>, _t: PhantomData<(B)>,
@ -143,21 +148,25 @@ where
B: MessageBody, B: MessageBody,
S: Service<Request = ServiceRequest, Response = ServiceResponse<B>, Error = Error>, S: Service<Request = ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
{ {
type Item = ServiceResponse<Encoder<B>>; type Output = Result<ServiceResponse<Encoder<B>>, Error>;
type Error = Error;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> { fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
let resp = futures::try_ready!(self.fut.poll()); let this = self.project();
match futures::ready!(this.fut.poll(cx)) {
Ok(resp) => {
let enc = if let Some(enc) = resp.response().extensions().get::<Enc>() { let enc = if let Some(enc) = resp.response().extensions().get::<Enc>() {
enc.0 enc.0
} else { } else {
self.encoding *this.encoding
}; };
Ok(Async::Ready(resp.map_body(move |head, body| { Poll::Ready(Ok(
Encoder::response(enc, head, body) resp.map_body(move |head, body| Encoder::response(enc, head, body))
}))) ))
}
Err(e) => Poll::Ready(Err(e)),
}
} }
} }

View file

@ -1,9 +1,11 @@
//! Middleware for setting default response headers //! Middleware for setting default response headers
use std::future::Future;
use std::pin::Pin;
use std::rc::Rc; use std::rc::Rc;
use std::task::{Context, Poll};
use actix_service::{Service, Transform}; use actix_service::{Service, Transform};
use futures::future::{ok, FutureResult}; use futures::future::{ok, FutureExt, LocalBoxFuture, Ready};
use futures::{Future, Poll};
use crate::http::header::{HeaderName, HeaderValue, CONTENT_TYPE}; use crate::http::header::{HeaderName, HeaderValue, CONTENT_TYPE};
use crate::http::{HeaderMap, HttpTryFrom}; use crate::http::{HeaderMap, HttpTryFrom};
@ -96,7 +98,7 @@ where
type Error = Error; type Error = Error;
type InitError = (); type InitError = ();
type Transform = DefaultHeadersMiddleware<S>; type Transform = DefaultHeadersMiddleware<S>;
type Future = FutureResult<Self::Transform, Self::InitError>; type Future = Ready<Result<Self::Transform, Self::InitError>>;
fn new_transform(&self, service: S) -> Self::Future { fn new_transform(&self, service: S) -> Self::Future {
ok(DefaultHeadersMiddleware { ok(DefaultHeadersMiddleware {
@ -119,16 +121,19 @@ where
type Request = ServiceRequest; type Request = ServiceRequest;
type Response = ServiceResponse<B>; type Response = ServiceResponse<B>;
type Error = Error; type Error = Error;
type Future = Box<dyn Future<Item = Self::Response, Error = Self::Error>>; type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
fn poll_ready(&mut self) -> Poll<(), Self::Error> { fn poll_ready(&mut self, cx: &mut Context) -> Poll<Result<(), Self::Error>> {
self.service.poll_ready() self.service.poll_ready(cx)
} }
fn call(&mut self, req: ServiceRequest) -> Self::Future { fn call(&mut self, req: ServiceRequest) -> Self::Future {
let inner = self.inner.clone(); let inner = self.inner.clone();
let fut = self.service.call(req);
async move {
let mut res = fut.await?;
Box::new(self.service.call(req).map(move |mut res| {
// set response headers // set response headers
for (key, value) in inner.headers.iter() { for (key, value) in inner.headers.iter() {
if !res.headers().contains_key(key) { if !res.headers().contains_key(key) {
@ -142,15 +147,16 @@ where
HeaderValue::from_static("application/octet-stream"), HeaderValue::from_static("application/octet-stream"),
); );
} }
Ok(res)
res }
})) .boxed_local()
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use actix_service::IntoService; use actix_service::IntoService;
use futures::future::ok;
use super::*; use super::*;
use crate::dev::ServiceRequest; use crate::dev::ServiceRequest;
@ -160,46 +166,50 @@ mod tests {
#[test] #[test]
fn test_default_headers() { fn test_default_headers() {
let mut mw = block_on( block_on(async {
DefaultHeaders::new() let mut mw = DefaultHeaders::new()
.header(CONTENT_TYPE, "0001") .header(CONTENT_TYPE, "0001")
.new_transform(ok_service()), .new_transform(ok_service())
) .await
.unwrap(); .unwrap();
let req = TestRequest::default().to_srv_request(); let req = TestRequest::default().to_srv_request();
let resp = block_on(mw.call(req)).unwrap(); let resp = mw.call(req).await.unwrap();
assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), "0001"); assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), "0001");
let req = TestRequest::default().to_srv_request(); let req = TestRequest::default().to_srv_request();
let srv = |req: ServiceRequest| { let srv = |req: ServiceRequest| {
req.into_response(HttpResponse::Ok().header(CONTENT_TYPE, "0002").finish()) ok(req.into_response(
HttpResponse::Ok().header(CONTENT_TYPE, "0002").finish(),
))
}; };
let mut mw = block_on( let mut mw = DefaultHeaders::new()
DefaultHeaders::new()
.header(CONTENT_TYPE, "0001") .header(CONTENT_TYPE, "0001")
.new_transform(srv.into_service()), .new_transform(srv.into_service())
) .await
.unwrap(); .unwrap();
let resp = block_on(mw.call(req)).unwrap(); let resp = mw.call(req).await.unwrap();
assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), "0002"); assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), "0002");
})
} }
#[test] #[test]
fn test_content_type() { fn test_content_type() {
let srv = |req: ServiceRequest| req.into_response(HttpResponse::Ok().finish()); block_on(async {
let mut mw = block_on( let srv =
DefaultHeaders::new() |req: ServiceRequest| ok(req.into_response(HttpResponse::Ok().finish()));
let mut mw = DefaultHeaders::new()
.content_type() .content_type()
.new_transform(srv.into_service()), .new_transform(srv.into_service())
) .await
.unwrap(); .unwrap();
let req = TestRequest::default().to_srv_request(); let req = TestRequest::default().to_srv_request();
let resp = block_on(mw.call(req)).unwrap(); let resp = mw.call(req).await.unwrap();
assert_eq!( assert_eq!(
resp.headers().get(CONTENT_TYPE).unwrap(), resp.headers().get(CONTENT_TYPE).unwrap(),
"application/octet-stream" "application/octet-stream"
); );
})
} }
} }

View file

@ -2,13 +2,15 @@
use std::collections::HashSet; use std::collections::HashSet;
use std::env; use std::env;
use std::fmt::{self, Display, Formatter}; use std::fmt::{self, Display, Formatter};
use std::future::Future;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::pin::Pin;
use std::rc::Rc; use std::rc::Rc;
use std::task::{Context, Poll};
use actix_service::{Service, Transform}; use actix_service::{Service, Transform};
use bytes::Bytes; use bytes::Bytes;
use futures::future::{ok, FutureResult}; use futures::future::{ok, Ready};
use futures::{Async, Future, Poll};
use log::debug; use log::debug;
use regex::Regex; use regex::Regex;
use time; use time;
@ -125,7 +127,7 @@ where
type Error = Error; type Error = Error;
type InitError = (); type InitError = ();
type Transform = LoggerMiddleware<S>; type Transform = LoggerMiddleware<S>;
type Future = FutureResult<Self::Transform, Self::InitError>; type Future = Ready<Result<Self::Transform, Self::InitError>>;
fn new_transform(&self, service: S) -> Self::Future { fn new_transform(&self, service: S) -> Self::Future {
ok(LoggerMiddleware { ok(LoggerMiddleware {
@ -151,8 +153,8 @@ where
type Error = Error; type Error = Error;
type Future = LoggerResponse<S, B>; type Future = LoggerResponse<S, B>;
fn poll_ready(&mut self) -> Poll<(), Self::Error> { fn poll_ready(&mut self, cx: &mut Context) -> Poll<Result<(), Self::Error>> {
self.service.poll_ready() self.service.poll_ready(cx)
} }
fn call(&mut self, req: ServiceRequest) -> Self::Future { fn call(&mut self, req: ServiceRequest) -> Self::Future {
@ -181,11 +183,13 @@ where
} }
#[doc(hidden)] #[doc(hidden)]
#[pin_project::pin_project]
pub struct LoggerResponse<S, B> pub struct LoggerResponse<S, B>
where where
B: MessageBody, B: MessageBody,
S: Service, S: Service,
{ {
#[pin]
fut: S::Future, fut: S::Future,
time: time::Tm, time: time::Tm,
format: Option<Format>, format: Option<Format>,
@ -197,11 +201,15 @@ where
B: MessageBody, B: MessageBody,
S: Service<Request = ServiceRequest, Response = ServiceResponse<B>, Error = Error>, S: Service<Request = ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
{ {
type Item = ServiceResponse<StreamLog<B>>; type Output = Result<ServiceResponse<StreamLog<B>>, Error>;
type Error = Error;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> { fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
let res = futures::try_ready!(self.fut.poll()); let this = self.project();
let res = match futures::ready!(this.fut.poll(cx)) {
Ok(res) => res,
Err(e) => return Poll::Ready(Err(e)),
};
if let Some(error) = res.response().error() { if let Some(error) = res.response().error() {
if res.response().head().status != StatusCode::INTERNAL_SERVER_ERROR { if res.response().head().status != StatusCode::INTERNAL_SERVER_ERROR {
@ -209,18 +217,21 @@ where
} }
} }
if let Some(ref mut format) = self.format { if let Some(ref mut format) = this.format {
for unit in &mut format.0 { for unit in &mut format.0 {
unit.render_response(res.response()); unit.render_response(res.response());
} }
} }
Ok(Async::Ready(res.map_body(move |_, body| { let time = *this.time;
let format = this.format.take();
Poll::Ready(Ok(res.map_body(move |_, body| {
ResponseBody::Body(StreamLog { ResponseBody::Body(StreamLog {
body, body,
time,
format,
size: 0, size: 0,
time: self.time,
format: self.format.take(),
}) })
}))) })))
} }
@ -252,13 +263,13 @@ impl<B: MessageBody> MessageBody for StreamLog<B> {
self.body.size() self.body.size()
} }
fn poll_next(&mut self) -> Poll<Option<Bytes>, Error> { fn poll_next(&mut self, cx: &mut Context) -> Poll<Option<Result<Bytes, Error>>> {
match self.body.poll_next()? { match self.body.poll_next(cx) {
Async::Ready(Some(chunk)) => { Poll::Ready(Some(Ok(chunk))) => {
self.size += chunk.len(); self.size += chunk.len();
Ok(Async::Ready(Some(chunk))) Poll::Ready(Some(Ok(chunk)))
} }
val => Ok(val), val => val,
} }
} }
} }
@ -464,6 +475,7 @@ impl<'a> fmt::Display for FormatDisplay<'a> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use actix_service::{IntoService, Service, Transform}; use actix_service::{IntoService, Service, Transform};
use futures::future::ok;
use super::*; use super::*;
use crate::http::{header, StatusCode}; use crate::http::{header, StatusCode};
@ -472,11 +484,11 @@ mod tests {
#[test] #[test]
fn test_logger() { fn test_logger() {
let srv = |req: ServiceRequest| { let srv = |req: ServiceRequest| {
req.into_response( ok(req.into_response(
HttpResponse::build(StatusCode::OK) HttpResponse::build(StatusCode::OK)
.header("X-Test", "ttt") .header("X-Test", "ttt")
.finish(), .finish(),
) ))
}; };
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");

View file

@ -2,13 +2,13 @@
mod compress; mod compress;
pub use self::compress::{BodyEncoding, Compress}; pub use self::compress::{BodyEncoding, Compress};
mod condition; //mod condition;
mod defaultheaders; mod defaultheaders;
pub mod errhandlers; //pub mod errhandlers;
mod logger; mod logger;
mod normalize; //mod normalize;
pub use self::condition::Condition; //pub use self::condition::Condition;
pub use self::defaultheaders::DefaultHeaders; pub use self::defaultheaders::DefaultHeaders;
pub use self::logger::Logger; pub use self::logger::Logger;
pub use self::normalize::NormalizePath; //pub use self::normalize::NormalizePath;

View file

@ -5,6 +5,7 @@ use std::{fmt, net};
use actix_http::http::{HeaderMap, Method, Uri, Version}; use actix_http::http::{HeaderMap, Method, Uri, Version};
use actix_http::{Error, Extensions, HttpMessage, Message, Payload, RequestHead}; use actix_http::{Error, Extensions, HttpMessage, Message, Payload, RequestHead};
use actix_router::{Path, Url}; use actix_router::{Path, Url};
use futures::future::{ok, Ready};
use crate::config::AppConfig; use crate::config::AppConfig;
use crate::data::Data; use crate::data::Data;
@ -289,11 +290,11 @@ impl Drop for HttpRequest {
impl FromRequest for HttpRequest { impl FromRequest for HttpRequest {
type Config = (); type Config = ();
type Error = Error; type Error = Error;
type Future = Result<Self, Error>; type Future = Ready<Result<Self, Error>>;
#[inline] #[inline]
fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future { fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
Ok(req.clone()) ok(req.clone())
} }
} }
@ -349,7 +350,7 @@ mod tests {
use super::*; use super::*;
use crate::dev::{ResourceDef, ResourceMap}; use crate::dev::{ResourceDef, ResourceMap};
use crate::http::{header, StatusCode}; use crate::http::{header, StatusCode};
use crate::test::{call_service, init_service, TestRequest}; use crate::test::{block_on, call_service, init_service, TestRequest};
use crate::{web, App, HttpResponse}; use crate::{web, App, HttpResponse};
#[test] #[test]
@ -467,6 +468,7 @@ mod tests {
#[test] #[test]
fn test_app_data() { fn test_app_data() {
block_on(async {
let mut srv = init_service(App::new().data(10usize).service( let mut srv = init_service(App::new().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() {
@ -475,10 +477,11 @@ mod tests {
HttpResponse::BadRequest() HttpResponse::BadRequest()
} }
}), }),
)); ))
.await;
let req = TestRequest::default().to_request(); let req = TestRequest::default().to_request();
let resp = call_service(&mut srv, req); let resp = call_service(&mut srv, req).await;
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
let mut srv = init_service(App::new().data(10u32).service( let mut srv = init_service(App::new().data(10u32).service(
@ -489,15 +492,18 @@ mod tests {
HttpResponse::BadRequest() HttpResponse::BadRequest()
} }
}), }),
)); ))
.await;
let req = TestRequest::default().to_request(); let req = TestRequest::default().to_request();
let resp = call_service(&mut srv, req); let resp = call_service(&mut srv, req).await;
assert_eq!(resp.status(), StatusCode::BAD_REQUEST); assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
})
} }
#[test] #[test]
fn test_extensions_dropped() { fn test_extensions_dropped() {
block_on(async {
struct Tracker { struct Tracker {
pub dropped: bool, pub dropped: bool,
} }
@ -520,13 +526,15 @@ mod tests {
}); });
HttpResponse::Ok() HttpResponse::Ok()
}), }),
)); ))
.await;
let req = TestRequest::default().to_request(); let req = TestRequest::default().to_request();
let resp = call_service(&mut srv, req); let resp = call_service(&mut srv, req).await;
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
} }
assert!(tracker.borrow().dropped); assert!(tracker.borrow().dropped);
})
} }
} }

View file

@ -1,14 +1,17 @@
use std::cell::RefCell; use std::cell::RefCell;
use std::fmt; use std::fmt;
use std::future::Future;
use std::pin::Pin;
use std::rc::Rc; use std::rc::Rc;
use std::task::{Context, Poll};
use actix_http::{Error, Extensions, Response}; use actix_http::{Error, Extensions, Response};
use actix_service::boxed::{self, BoxedNewService, BoxedService}; use actix_service::boxed::{self, BoxedNewService, BoxedService};
use actix_service::{ use actix_service::{
apply_transform, IntoNewService, IntoTransform, NewService, Service, Transform, apply, apply_fn_factory, IntoServiceFactory, Service, ServiceFactory, Transform,
}; };
use futures::future::{ok, Either, FutureResult}; use futures::future::{ok, Either, LocalBoxFuture, Ready};
use futures::{Async, Future, IntoFuture, Poll}; use pin_project::pin_project;
use crate::data::Data; use crate::data::Data;
use crate::dev::{insert_slash, AppService, HttpServiceFactory, ResourceDef}; use crate::dev::{insert_slash, AppService, HttpServiceFactory, ResourceDef};
@ -74,7 +77,7 @@ impl Resource {
impl<T> Resource<T> impl<T> Resource<T>
where where
T: NewService< T: ServiceFactory<
Config = (), Config = (),
Request = ServiceRequest, Request = ServiceRequest,
Response = ServiceResponse, Response = ServiceResponse,
@ -243,8 +246,8 @@ where
/// use actix_web::*; /// use actix_web::*;
/// use futures::future::{ok, Future}; /// use futures::future::{ok, Future};
/// ///
/// fn index(req: HttpRequest) -> impl Future<Item=HttpResponse, Error=Error> { /// async fn index(req: HttpRequest) -> Result<HttpResponse, Error> {
/// ok(HttpResponse::Ok().finish()) /// Ok(HttpResponse::Ok().finish())
/// } /// }
/// ///
/// App::new().service(web::resource("/").to_async(index)); /// App::new().service(web::resource("/").to_async(index));
@ -255,19 +258,19 @@ where
/// ```rust /// ```rust
/// # use actix_web::*; /// # use actix_web::*;
/// # use futures::future::Future; /// # use futures::future::Future;
/// # fn index(req: HttpRequest) -> Box<dyn Future<Item=HttpResponse, Error=Error>> { /// # async fn index(req: HttpRequest) -> Result<HttpResponse, Error> {
/// # unimplemented!() /// # unimplemented!()
/// # } /// # }
/// App::new().service(web::resource("/").route(web::route().to_async(index))); /// App::new().service(web::resource("/").route(web::route().to_async(index)));
/// ``` /// ```
#[allow(clippy::wrong_self_convention)] #[allow(clippy::wrong_self_convention)]
pub fn to_async<F, I, R>(mut self, handler: F) -> Self pub fn to_async<F, I, R, O, E>(mut self, handler: F) -> Self
where where
F: AsyncFactory<I, R>, F: AsyncFactory<I, R, O, E>,
I: FromRequest + 'static, I: FromRequest + 'static,
R: IntoFuture + 'static, R: Future<Output = Result<O, E>> + 'static,
R::Item: Responder, O: Responder + 'static,
R::Error: Into<Error>, E: Into<Error> + 'static,
{ {
self.routes.push(Route::new().to_async(handler)); self.routes.push(Route::new().to_async(handler));
self self
@ -280,11 +283,11 @@ where
/// type (i.e modify response's body). /// type (i.e modify response's body).
/// ///
/// **Note**: middlewares get called in opposite order of middlewares registration. /// **Note**: middlewares get called in opposite order of middlewares registration.
pub fn wrap<M, F>( pub fn wrap<M>(
self, self,
mw: F, mw: M,
) -> Resource< ) -> Resource<
impl NewService< impl ServiceFactory<
Config = (), Config = (),
Request = ServiceRequest, Request = ServiceRequest,
Response = ServiceResponse, Response = ServiceResponse,
@ -300,11 +303,9 @@ where
Error = Error, Error = Error,
InitError = (), InitError = (),
>, >,
F: IntoTransform<M, T::Service>,
{ {
let endpoint = apply_transform(mw, self.endpoint);
Resource { Resource {
endpoint, endpoint: apply(mw, self.endpoint),
rdef: self.rdef, rdef: self.rdef,
name: self.name, name: self.name,
guards: self.guards, guards: self.guards,
@ -337,13 +338,16 @@ where
/// fn main() { /// fn main() {
/// let app = App::new().service( /// let app = App::new().service(
/// web::resource("/index.html") /// web::resource("/index.html")
/// .wrap_fn(|req, srv| /// .wrap_fn(|req, srv| {
/// srv.call(req).map(|mut res| { /// let fut = srv.call(req);
/// async {
/// let mut res = fut.await?;
/// res.headers_mut().insert( /// res.headers_mut().insert(
/// CONTENT_TYPE, HeaderValue::from_static("text/plain"), /// CONTENT_TYPE, HeaderValue::from_static("text/plain"),
/// ); /// );
/// res /// Ok(res)
/// })) /// }
/// })
/// .route(web::get().to(index))); /// .route(web::get().to(index)));
/// } /// }
/// ``` /// ```
@ -351,7 +355,7 @@ where
self, self,
mw: F, mw: F,
) -> Resource< ) -> Resource<
impl NewService< impl ServiceFactory<
Config = (), Config = (),
Request = ServiceRequest, Request = ServiceRequest,
Response = ServiceResponse, Response = ServiceResponse,
@ -361,9 +365,18 @@ where
> >
where where
F: FnMut(ServiceRequest, &mut T::Service) -> R + Clone, F: FnMut(ServiceRequest, &mut T::Service) -> R + Clone,
R: IntoFuture<Item = ServiceResponse, Error = Error>, R: Future<Output = Result<ServiceResponse, Error>>,
{ {
self.wrap(mw) Resource {
endpoint: apply_fn_factory(self.endpoint, mw),
rdef: self.rdef,
name: self.name,
guards: self.guards,
routes: self.routes,
default: self.default,
data: self.data,
factory_ref: self.factory_ref,
}
} }
/// Default service to be used if no matching route could be found. /// Default service to be used if no matching route could be found.
@ -371,8 +384,8 @@ where
/// default handler from `App` or `Scope`. /// default handler from `App` or `Scope`.
pub fn default_service<F, U>(mut self, f: F) -> Self pub fn default_service<F, U>(mut self, f: F) -> Self
where where
F: IntoNewService<U>, F: IntoServiceFactory<U>,
U: NewService< U: ServiceFactory<
Config = (), Config = (),
Request = ServiceRequest, Request = ServiceRequest,
Response = ServiceResponse, Response = ServiceResponse,
@ -381,8 +394,8 @@ where
U::InitError: fmt::Debug, U::InitError: fmt::Debug,
{ {
// create and configure default resource // create and configure default resource
self.default = Rc::new(RefCell::new(Some(Rc::new(boxed::new_service( self.default = Rc::new(RefCell::new(Some(Rc::new(boxed::factory(
f.into_new_service().map_init_err(|e| { f.into_factory().map_init_err(|e| {
log::error!("Can not construct default service: {:?}", e) log::error!("Can not construct default service: {:?}", e)
}), }),
))))); )))));
@ -393,7 +406,7 @@ where
impl<T> HttpServiceFactory for Resource<T> impl<T> HttpServiceFactory for Resource<T>
where where
T: NewService< T: ServiceFactory<
Config = (), Config = (),
Request = ServiceRequest, Request = ServiceRequest,
Response = ServiceResponse, Response = ServiceResponse,
@ -423,9 +436,9 @@ where
} }
} }
impl<T> IntoNewService<T> for Resource<T> impl<T> IntoServiceFactory<T> for Resource<T>
where where
T: NewService< T: ServiceFactory<
Config = (), Config = (),
Request = ServiceRequest, Request = ServiceRequest,
Response = ServiceResponse, Response = ServiceResponse,
@ -433,7 +446,7 @@ where
InitError = (), InitError = (),
>, >,
{ {
fn into_new_service(self) -> T { fn into_factory(self) -> T {
*self.factory_ref.borrow_mut() = Some(ResourceFactory { *self.factory_ref.borrow_mut() = Some(ResourceFactory {
routes: self.routes, routes: self.routes,
data: self.data.map(Rc::new), data: self.data.map(Rc::new),
@ -450,7 +463,7 @@ pub struct ResourceFactory {
default: Rc<RefCell<Option<Rc<HttpNewService>>>>, default: Rc<RefCell<Option<Rc<HttpNewService>>>>,
} }
impl NewService for ResourceFactory { impl ServiceFactory for ResourceFactory {
type Config = (); type Config = ();
type Request = ServiceRequest; type Request = ServiceRequest;
type Response = ServiceResponse; type Response = ServiceResponse;
@ -488,31 +501,30 @@ pub struct CreateResourceService {
fut: Vec<CreateRouteServiceItem>, fut: Vec<CreateRouteServiceItem>,
data: Option<Rc<Extensions>>, data: Option<Rc<Extensions>>,
default: Option<HttpService>, default: Option<HttpService>,
default_fut: Option<Box<dyn Future<Item = HttpService, Error = ()>>>, default_fut: Option<LocalBoxFuture<'static, Result<HttpService, ()>>>,
} }
impl Future for CreateResourceService { impl Future for CreateResourceService {
type Item = ResourceService; type Output = Result<ResourceService, ()>;
type Error = ();
fn poll(&mut self) -> Poll<Self::Item, Self::Error> { fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
let mut done = true; let mut done = true;
if let Some(ref mut fut) = self.default_fut { if let Some(ref mut fut) = self.default_fut {
match fut.poll()? { match Pin::new(fut).poll(cx)? {
Async::Ready(default) => self.default = Some(default), Poll::Ready(default) => self.default = Some(default),
Async::NotReady => done = false, Poll::Pending => done = false,
} }
} }
// poll http services // poll http services
for item in &mut self.fut { for item in &mut self.fut {
match item { match item {
CreateRouteServiceItem::Future(ref mut fut) => match fut.poll()? { CreateRouteServiceItem::Future(ref mut fut) => match Pin::new(fut)
Async::Ready(route) => { .poll(cx)?
*item = CreateRouteServiceItem::Service(route) {
} Poll::Ready(route) => *item = CreateRouteServiceItem::Service(route),
Async::NotReady => { Poll::Pending => {
done = false; done = false;
} }
}, },
@ -529,13 +541,13 @@ impl Future for CreateResourceService {
CreateRouteServiceItem::Future(_) => unreachable!(), CreateRouteServiceItem::Future(_) => unreachable!(),
}) })
.collect(); .collect();
Ok(Async::Ready(ResourceService { Poll::Ready(Ok(ResourceService {
routes, routes,
data: self.data.clone(), data: self.data.clone(),
default: self.default.take(), default: self.default.take(),
})) }))
} else { } else {
Ok(Async::NotReady) Poll::Pending
} }
} }
} }
@ -551,12 +563,12 @@ impl Service for ResourceService {
type Response = ServiceResponse; type Response = ServiceResponse;
type Error = Error; type Error = Error;
type Future = Either< type Future = Either<
FutureResult<ServiceResponse, Error>, Ready<Result<ServiceResponse, Error>>,
Box<dyn Future<Item = ServiceResponse, Error = Error>>, LocalBoxFuture<'static, Result<ServiceResponse, Error>>,
>; >;
fn poll_ready(&mut self) -> Poll<(), Self::Error> { fn poll_ready(&mut self, _: &mut Context) -> Poll<Result<(), Self::Error>> {
Ok(Async::Ready(())) Poll::Ready(Ok(()))
} }
fn call(&mut self, mut req: ServiceRequest) -> Self::Future { fn call(&mut self, mut req: ServiceRequest) -> Self::Future {
@ -565,14 +577,14 @@ impl Service for ResourceService {
if let Some(ref data) = self.data { if let Some(ref data) = self.data {
req.set_data_container(data.clone()); req.set_data_container(data.clone());
} }
return route.call(req); return Either::Right(route.call(req));
} }
} }
if let Some(ref mut default) = self.default { if let Some(ref mut default) = self.default {
default.call(req) Either::Right(default.call(req))
} else { } else {
let req = req.into_parts().0; let req = req.into_parts().0;
Either::A(ok(ServiceResponse::new( Either::Left(ok(ServiceResponse::new(
req, req,
Response::MethodNotAllowed().finish(), Response::MethodNotAllowed().finish(),
))) )))
@ -591,7 +603,7 @@ impl ResourceEndpoint {
} }
} }
impl NewService for ResourceEndpoint { impl ServiceFactory for ResourceEndpoint {
type Config = (); type Config = ();
type Request = ServiceRequest; type Request = ServiceRequest;
type Response = ServiceResponse; type Response = ServiceResponse;
@ -610,18 +622,19 @@ mod tests {
use std::time::Duration; use std::time::Duration;
use actix_service::Service; use actix_service::Service;
use futures::{Future, IntoFuture}; use futures::future::{ok, Future};
use tokio_timer::sleep; use tokio_timer::delay_for;
use crate::http::{header, HeaderValue, Method, StatusCode}; use crate::http::{header, HeaderValue, Method, StatusCode};
use crate::middleware::DefaultHeaders;
use crate::service::{ServiceRequest, ServiceResponse}; use crate::service::{ServiceRequest, ServiceResponse};
use crate::test::{call_service, init_service, TestRequest}; use crate::test::{block_on, call_service, init_service, TestRequest};
use crate::{guard, web, App, Error, HttpResponse}; use crate::{guard, web, App, Error, HttpResponse};
fn md<S, B>( fn md<S, B>(
req: ServiceRequest, req: ServiceRequest,
srv: &mut S, srv: &mut S,
) -> impl IntoFuture<Item = ServiceResponse<B>, Error = Error> ) -> impl Future<Output = Result<ServiceResponse<B>, Error>>
where where
S: Service< S: Service<
Request = ServiceRequest, Request = ServiceRequest,
@ -629,88 +642,112 @@ mod tests {
Error = Error, Error = Error,
>, >,
{ {
srv.call(req).map(|mut res| { let fut = srv.call(req);
async move {
let mut res = fut.await?;
res.headers_mut() res.headers_mut()
.insert(header::CONTENT_TYPE, HeaderValue::from_static("0001")); .insert(header::CONTENT_TYPE, HeaderValue::from_static("0001"));
res Ok(res)
}) }
} }
#[test] #[test]
fn test_middleware() { fn test_middleware() {
block_on(async {
let mut srv = init_service( let mut srv = init_service(
App::new().service( App::new().service(
web::resource("/test") web::resource("/test")
.name("test") .name("test")
.wrap(md) .wrap(DefaultHeaders::new().header(
header::CONTENT_TYPE,
HeaderValue::from_static("0001"),
))
.route(web::get().to(|| HttpResponse::Ok())), .route(web::get().to(|| HttpResponse::Ok())),
), ),
); )
.await;
let req = TestRequest::with_uri("/test").to_request(); let req = TestRequest::with_uri("/test").to_request();
let resp = call_service(&mut srv, req); let resp = call_service(&mut 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(),
HeaderValue::from_static("0001") HeaderValue::from_static("0001")
); );
})
} }
#[test] #[test]
fn test_middleware_fn() { fn test_middleware_fn() {
block_on(async {
let mut srv = init_service( let mut srv = init_service(
App::new().service( App::new().service(
web::resource("/test") web::resource("/test")
.wrap_fn(|req, srv| { .wrap_fn(|req, srv| {
srv.call(req).map(|mut res| { let fut = srv.call(req);
async {
fut.await.map(|mut res| {
res.headers_mut().insert( res.headers_mut().insert(
header::CONTENT_TYPE, header::CONTENT_TYPE,
HeaderValue::from_static("0001"), HeaderValue::from_static("0001"),
); );
res res
}) })
}
}) })
.route(web::get().to(|| HttpResponse::Ok())), .route(web::get().to(|| HttpResponse::Ok())),
), ),
); )
.await;
let req = TestRequest::with_uri("/test").to_request(); let req = TestRequest::with_uri("/test").to_request();
let resp = call_service(&mut srv, req); let resp = call_service(&mut 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(),
HeaderValue::from_static("0001") HeaderValue::from_static("0001")
); );
})
} }
#[test] #[test]
fn test_to_async() { fn test_to_async() {
let mut srv = block_on(async {
init_service(App::new().service(web::resource("/test").to_async(|| { let mut srv = init_service(App::new().service(
sleep(Duration::from_millis(100)).then(|_| HttpResponse::Ok()) web::resource("/test").to_async(|| {
}))); async {
delay_for(Duration::from_millis(100)).await;
Ok::<_, Error>(HttpResponse::Ok())
}
}),
))
.await;
let req = TestRequest::with_uri("/test").to_request(); let req = TestRequest::with_uri("/test").to_request();
let resp = call_service(&mut srv, req); let resp = call_service(&mut srv, req).await;
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
})
} }
#[test] #[test]
fn test_default_resource() { fn test_default_resource() {
block_on(async {
let mut srv = init_service( let mut srv = init_service(
App::new() App::new()
.service( .service(
web::resource("/test").route(web::get().to(|| HttpResponse::Ok())), web::resource("/test")
.route(web::get().to(|| HttpResponse::Ok())),
) )
.default_service(|r: ServiceRequest| { .default_service(|r: ServiceRequest| {
r.into_response(HttpResponse::BadRequest()) ok(r.into_response(HttpResponse::BadRequest()))
}), }),
); )
.await;
let req = TestRequest::with_uri("/test").to_request(); let req = TestRequest::with_uri("/test").to_request();
let resp = call_service(&mut srv, req); let resp = call_service(&mut 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); let resp = call_service(&mut 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 mut srv = init_service(
@ -718,24 +755,27 @@ mod tests {
web::resource("/test") web::resource("/test")
.route(web::get().to(|| HttpResponse::Ok())) .route(web::get().to(|| HttpResponse::Ok()))
.default_service(|r: ServiceRequest| { .default_service(|r: ServiceRequest| {
r.into_response(HttpResponse::BadRequest()) ok(r.into_response(HttpResponse::BadRequest()))
}), }),
), ),
); )
.await;
let req = TestRequest::with_uri("/test").to_request(); let req = TestRequest::with_uri("/test").to_request();
let resp = call_service(&mut srv, req); let resp = call_service(&mut 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); let resp = call_service(&mut srv, req).await;
assert_eq!(resp.status(), StatusCode::BAD_REQUEST); assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
})
} }
#[test] #[test]
fn test_resource_guards() { fn test_resource_guards() {
block_on(async {
let mut srv = init_service( let mut srv = init_service(
App::new() App::new()
.service( .service(
@ -753,29 +793,32 @@ mod tests {
.guard(guard::Delete()) .guard(guard::Delete())
.to(|| HttpResponse::NoContent()), .to(|| HttpResponse::NoContent()),
), ),
); )
.await;
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); let resp = call_service(&mut 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); let resp = call_service(&mut 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); let resp = call_service(&mut srv, req).await;
assert_eq!(resp.status(), StatusCode::NO_CONTENT); assert_eq!(resp.status(), StatusCode::NO_CONTENT);
})
} }
#[test] #[test]
fn test_data() { fn test_data() {
block_on(async {
let mut srv = init_service( let mut srv = init_service(
App::new() App::new()
.data(1.0f64) .data(1.0f64)
@ -797,10 +840,12 @@ mod tests {
}, },
), ),
), ),
); )
.await;
let req = TestRequest::get().uri("/test").to_request(); let req = TestRequest::get().uri("/test").to_request();
let resp = call_service(&mut srv, req); let resp = call_service(&mut srv, req).await;
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
})
} }
} }

View file

@ -1,3 +1,8 @@
use std::future::Future;
use std::marker::PhantomData;
use std::pin::Pin;
use std::task::{Context, Poll};
use actix_http::error::InternalError; use actix_http::error::InternalError;
use actix_http::http::{ use actix_http::http::{
header::IntoHeaderValue, Error as HttpError, HeaderMap, HeaderName, HttpTryFrom, header::IntoHeaderValue, Error as HttpError, HeaderMap, HeaderName, HttpTryFrom,
@ -5,8 +10,9 @@ use actix_http::http::{
}; };
use actix_http::{Error, Response, ResponseBuilder}; use actix_http::{Error, Response, ResponseBuilder};
use bytes::{Bytes, BytesMut}; use bytes::{Bytes, BytesMut};
use futures::future::{err, ok, Either as EitherFuture, FutureResult}; use futures::future::{err, ok, Either as EitherFuture, LocalBoxFuture, Ready};
use futures::{try_ready, Async, Future, IntoFuture, Poll}; use futures::ready;
use pin_project::{pin_project, project};
use crate::request::HttpRequest; use crate::request::HttpRequest;
@ -18,7 +24,7 @@ pub trait Responder {
type Error: Into<Error>; type Error: Into<Error>;
/// The future response value. /// The future response value.
type Future: IntoFuture<Item = Response, Error = Self::Error>; type Future: Future<Output = Result<Response, Self::Error>>;
/// Convert itself to `AsyncResult` or `Error`. /// Convert itself to `AsyncResult` or `Error`.
fn respond_to(self, req: &HttpRequest) -> Self::Future; fn respond_to(self, req: &HttpRequest) -> Self::Future;
@ -71,7 +77,7 @@ pub trait Responder {
impl Responder for Response { impl Responder for Response {
type Error = Error; type Error = Error;
type Future = FutureResult<Response, Error>; type Future = Ready<Result<Response, Error>>;
#[inline] #[inline]
fn respond_to(self, _: &HttpRequest) -> Self::Future { fn respond_to(self, _: &HttpRequest) -> Self::Future {
@ -84,15 +90,14 @@ where
T: Responder, T: Responder,
{ {
type Error = T::Error; type Error = T::Error;
type Future = EitherFuture< type Future = EitherFuture<T::Future, Ready<Result<Response, T::Error>>>;
<T::Future as IntoFuture>::Future,
FutureResult<Response, T::Error>,
>;
fn respond_to(self, req: &HttpRequest) -> Self::Future { fn respond_to(self, req: &HttpRequest) -> Self::Future {
match self { match self {
Some(t) => EitherFuture::A(t.respond_to(req).into_future()), Some(t) => EitherFuture::Left(t.respond_to(req)),
None => EitherFuture::B(ok(Response::build(StatusCode::NOT_FOUND).finish())), None => {
EitherFuture::Right(ok(Response::build(StatusCode::NOT_FOUND).finish()))
}
} }
} }
} }
@ -104,23 +109,21 @@ where
{ {
type Error = Error; type Error = Error;
type Future = EitherFuture< type Future = EitherFuture<
ResponseFuture<<T::Future as IntoFuture>::Future>, ResponseFuture<T::Future, T::Error>,
FutureResult<Response, Error>, Ready<Result<Response, Error>>,
>; >;
fn respond_to(self, req: &HttpRequest) -> Self::Future { fn respond_to(self, req: &HttpRequest) -> Self::Future {
match self { match self {
Ok(val) => { Ok(val) => EitherFuture::Left(ResponseFuture::new(val.respond_to(req))),
EitherFuture::A(ResponseFuture::new(val.respond_to(req).into_future())) Err(e) => EitherFuture::Right(err(e.into())),
}
Err(e) => EitherFuture::B(err(e.into())),
} }
} }
} }
impl Responder for ResponseBuilder { impl Responder for ResponseBuilder {
type Error = Error; type Error = Error;
type Future = FutureResult<Response, Error>; type Future = Ready<Result<Response, Error>>;
#[inline] #[inline]
fn respond_to(mut self, _: &HttpRequest) -> Self::Future { fn respond_to(mut self, _: &HttpRequest) -> Self::Future {
@ -130,7 +133,7 @@ impl Responder for ResponseBuilder {
impl Responder for () { impl Responder for () {
type Error = Error; type Error = Error;
type Future = FutureResult<Response, Error>; type Future = Ready<Result<Response, Error>>;
fn respond_to(self, _: &HttpRequest) -> Self::Future { fn respond_to(self, _: &HttpRequest) -> Self::Future {
ok(Response::build(StatusCode::OK).finish()) ok(Response::build(StatusCode::OK).finish())
@ -146,7 +149,7 @@ where
fn respond_to(self, req: &HttpRequest) -> Self::Future { fn respond_to(self, req: &HttpRequest) -> Self::Future {
CustomResponderFut { CustomResponderFut {
fut: self.0.respond_to(req).into_future(), fut: self.0.respond_to(req),
status: Some(self.1), status: Some(self.1),
headers: None, headers: None,
} }
@ -155,7 +158,7 @@ where
impl Responder for &'static str { impl Responder for &'static str {
type Error = Error; type Error = Error;
type Future = FutureResult<Response, Error>; type Future = Ready<Result<Response, Error>>;
fn respond_to(self, _: &HttpRequest) -> Self::Future { fn respond_to(self, _: &HttpRequest) -> Self::Future {
ok(Response::build(StatusCode::OK) ok(Response::build(StatusCode::OK)
@ -166,7 +169,7 @@ impl Responder for &'static str {
impl Responder for &'static [u8] { impl Responder for &'static [u8] {
type Error = Error; type Error = Error;
type Future = FutureResult<Response, Error>; type Future = Ready<Result<Response, Error>>;
fn respond_to(self, _: &HttpRequest) -> Self::Future { fn respond_to(self, _: &HttpRequest) -> Self::Future {
ok(Response::build(StatusCode::OK) ok(Response::build(StatusCode::OK)
@ -177,7 +180,7 @@ impl Responder for &'static [u8] {
impl Responder for String { impl Responder for String {
type Error = Error; type Error = Error;
type Future = FutureResult<Response, Error>; type Future = Ready<Result<Response, Error>>;
fn respond_to(self, _: &HttpRequest) -> Self::Future { fn respond_to(self, _: &HttpRequest) -> Self::Future {
ok(Response::build(StatusCode::OK) ok(Response::build(StatusCode::OK)
@ -188,7 +191,7 @@ impl Responder for String {
impl<'a> Responder for &'a String { impl<'a> Responder for &'a String {
type Error = Error; type Error = Error;
type Future = FutureResult<Response, Error>; type Future = Ready<Result<Response, Error>>;
fn respond_to(self, _: &HttpRequest) -> Self::Future { fn respond_to(self, _: &HttpRequest) -> Self::Future {
ok(Response::build(StatusCode::OK) ok(Response::build(StatusCode::OK)
@ -199,7 +202,7 @@ impl<'a> Responder for &'a String {
impl Responder for Bytes { impl Responder for Bytes {
type Error = Error; type Error = Error;
type Future = FutureResult<Response, Error>; type Future = Ready<Result<Response, Error>>;
fn respond_to(self, _: &HttpRequest) -> Self::Future { fn respond_to(self, _: &HttpRequest) -> Self::Future {
ok(Response::build(StatusCode::OK) ok(Response::build(StatusCode::OK)
@ -210,7 +213,7 @@ impl Responder for Bytes {
impl Responder for BytesMut { impl Responder for BytesMut {
type Error = Error; type Error = Error;
type Future = FutureResult<Response, Error>; type Future = Ready<Result<Response, Error>>;
fn respond_to(self, _: &HttpRequest) -> Self::Future { fn respond_to(self, _: &HttpRequest) -> Self::Future {
ok(Response::build(StatusCode::OK) ok(Response::build(StatusCode::OK)
@ -299,34 +302,40 @@ impl<T: Responder> Responder for CustomResponder<T> {
fn respond_to(self, req: &HttpRequest) -> Self::Future { fn respond_to(self, req: &HttpRequest) -> Self::Future {
CustomResponderFut { CustomResponderFut {
fut: self.responder.respond_to(req).into_future(), fut: self.responder.respond_to(req),
status: self.status, status: self.status,
headers: self.headers, headers: self.headers,
} }
} }
} }
#[pin_project]
pub struct CustomResponderFut<T: Responder> { pub struct CustomResponderFut<T: Responder> {
fut: <T::Future as IntoFuture>::Future, #[pin]
fut: T::Future,
status: Option<StatusCode>, status: Option<StatusCode>,
headers: Option<HeaderMap>, headers: Option<HeaderMap>,
} }
impl<T: Responder> Future for CustomResponderFut<T> { impl<T: Responder> Future for CustomResponderFut<T> {
type Item = Response; type Output = Result<Response, T::Error>;
type Error = T::Error;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> { fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
let mut res = try_ready!(self.fut.poll()); let this = self.project();
if let Some(status) = self.status {
let mut res = match ready!(this.fut.poll(cx)) {
Ok(res) => res,
Err(e) => return Poll::Ready(Err(e)),
};
if let Some(status) = this.status.take() {
*res.status_mut() = status; *res.status_mut() = status;
} }
if let Some(ref headers) = self.headers { if let Some(ref headers) = this.headers {
for (k, v) in headers { for (k, v) in headers {
res.headers_mut().insert(k.clone(), v.clone()); res.headers_mut().insert(k.clone(), v.clone());
} }
} }
Ok(Async::Ready(res)) Poll::Ready(Ok(res))
} }
} }
@ -336,8 +345,7 @@ impl<T: Responder> Future for CustomResponderFut<T> {
/// # use futures::future::{ok, Future}; /// # use futures::future::{ok, Future};
/// use actix_web::{Either, Error, HttpResponse}; /// use actix_web::{Either, Error, HttpResponse};
/// ///
/// type RegisterResult = /// type RegisterResult = Either<HttpResponse, Result<HttpResponse, Error>>;
/// Either<HttpResponse, Box<dyn Future<Item = HttpResponse, Error = Error>>>;
/// ///
/// fn index() -> RegisterResult { /// fn index() -> RegisterResult {
/// if is_a_variant() { /// if is_a_variant() {
@ -346,9 +354,9 @@ impl<T: Responder> Future for CustomResponderFut<T> {
/// } else { /// } else {
/// Either::B( /// Either::B(
/// // <- Right variant /// // <- Right variant
/// Box::new(ok(HttpResponse::Ok() /// Ok(HttpResponse::Ok()
/// .content_type("text/html") /// .content_type("text/html")
/// .body("Hello!"))) /// .body("Hello!"))
/// ) /// )
/// } /// }
/// } /// }
@ -369,63 +377,44 @@ where
B: Responder, B: Responder,
{ {
type Error = Error; type Error = Error;
type Future = EitherResponder< type Future = EitherResponder<A, B>;
<A::Future as IntoFuture>::Future,
<B::Future as IntoFuture>::Future,
>;
fn respond_to(self, req: &HttpRequest) -> Self::Future { fn respond_to(self, req: &HttpRequest) -> Self::Future {
match self { match self {
Either::A(a) => EitherResponder::A(a.respond_to(req).into_future()), Either::A(a) => EitherResponder::A(a.respond_to(req)),
Either::B(b) => EitherResponder::B(b.respond_to(req).into_future()), Either::B(b) => EitherResponder::B(b.respond_to(req)),
} }
} }
} }
#[pin_project]
pub enum EitherResponder<A, B> pub enum EitherResponder<A, B>
where where
A: Future<Item = Response>, A: Responder,
A::Error: Into<Error>, B: Responder,
B: Future<Item = Response>,
B::Error: Into<Error>,
{ {
A(A), A(#[pin] A::Future),
B(B), B(#[pin] B::Future),
} }
impl<A, B> Future for EitherResponder<A, B> impl<A, B> Future for EitherResponder<A, B>
where where
A: Future<Item = Response>, A: Responder,
A::Error: Into<Error>, B: Responder,
B: Future<Item = Response>,
B::Error: Into<Error>,
{ {
type Item = Response; type Output = Result<Response, Error>;
type Error = Error;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> { #[project]
match self { fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
EitherResponder::A(ref mut fut) => Ok(fut.poll().map_err(|e| e.into())?), #[project]
EitherResponder::B(ref mut fut) => Ok(fut.poll().map_err(|e| e.into())?), match self.project() {
EitherResponder::A(fut) => {
Poll::Ready(ready!(fut.poll(cx)).map_err(|e| e.into()))
}
EitherResponder::B(fut) => {
Poll::Ready(ready!(fut.poll(cx).map_err(|e| e.into())))
} }
} }
}
impl<I, E> Responder for Box<dyn Future<Item = I, Error = E>>
where
I: Responder + 'static,
E: Into<Error> + 'static,
{
type Error = Error;
type Future = Box<dyn Future<Item = Response, Error = Error>>;
#[inline]
fn respond_to(self, req: &HttpRequest) -> Self::Future {
let req = req.clone();
Box::new(
self.map_err(|e| e.into())
.and_then(move |r| ResponseFuture(r.respond_to(&req).into_future())),
)
} }
} }
@ -434,32 +423,39 @@ where
T: std::fmt::Debug + std::fmt::Display + 'static, T: std::fmt::Debug + std::fmt::Display + 'static,
{ {
type Error = Error; type Error = Error;
type Future = Result<Response, Error>; type Future = Ready<Result<Response, Error>>;
fn respond_to(self, _: &HttpRequest) -> Self::Future { fn respond_to(self, _: &HttpRequest) -> Self::Future {
let err: Error = self.into(); let err: Error = self.into();
Ok(err.into()) ok(err.into())
} }
} }
pub struct ResponseFuture<T>(T); #[pin_project]
pub struct ResponseFuture<T, E> {
#[pin]
fut: T,
_t: PhantomData<E>,
}
impl<T> ResponseFuture<T> { impl<T, E> ResponseFuture<T, E> {
pub fn new(fut: T) -> Self { pub fn new(fut: T) -> Self {
ResponseFuture(fut) ResponseFuture {
fut,
_t: PhantomData,
}
} }
} }
impl<T> Future for ResponseFuture<T> impl<T, E> Future for ResponseFuture<T, E>
where where
T: Future<Item = Response>, T: Future<Output = Result<Response, E>>,
T::Error: Into<Error>, E: Into<Error>,
{ {
type Item = Response; type Output = Result<Response, Error>;
type Error = Error;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> { fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
Ok(self.0.poll().map_err(|e| e.into())?) Poll::Ready(ready!(self.project().fut.poll(cx)).map_err(|e| e.into()))
} }
} }
@ -476,18 +472,22 @@ pub(crate) mod tests {
#[test] #[test]
fn test_option_responder() { fn test_option_responder() {
block_on(async {
let mut srv = init_service( let mut srv = init_service(
App::new() App::new()
.service(web::resource("/none").to(|| -> Option<&'static str> { None })) .service(
web::resource("/none").to(|| -> Option<&'static str> { None }),
)
.service(web::resource("/some").to(|| Some("some"))), .service(web::resource("/some").to(|| Some("some"))),
); )
.await;
let req = TestRequest::with_uri("/none").to_request(); let req = TestRequest::with_uri("/none").to_request();
let resp = block_on(srv.call(req)).unwrap(); let resp = srv.call(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::NOT_FOUND); assert_eq!(resp.status(), StatusCode::NOT_FOUND);
let req = TestRequest::with_uri("/some").to_request(); let req = TestRequest::with_uri("/some").to_request();
let resp = block_on(srv.call(req)).unwrap(); let resp = srv.call(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
match resp.response().body() { match resp.response().body() {
ResponseBody::Body(Body::Bytes(ref b)) => { ResponseBody::Body(Body::Bytes(ref b)) => {
@ -496,6 +496,7 @@ pub(crate) mod tests {
} }
_ => panic!(), _ => panic!(),
} }
})
} }
pub(crate) trait BodyTest { pub(crate) trait BodyTest {
@ -526,13 +527,14 @@ pub(crate) mod tests {
#[test] #[test]
fn test_responder() { fn test_responder() {
block_on(async {
let req = TestRequest::default().to_http_request(); let req = TestRequest::default().to_http_request();
let resp: HttpResponse = block_on(().respond_to(&req)).unwrap(); let resp: HttpResponse = ().respond_to(&req).await.unwrap();
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
assert_eq!(*resp.body().body(), Body::Empty); assert_eq!(*resp.body().body(), Body::Empty);
let resp: HttpResponse = block_on("test".respond_to(&req)).unwrap(); let resp: HttpResponse = "test".respond_to(&req).await.unwrap();
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
assert_eq!(resp.body().bin_ref(), b"test"); assert_eq!(resp.body().bin_ref(), b"test");
assert_eq!( assert_eq!(
@ -540,7 +542,7 @@ pub(crate) mod tests {
HeaderValue::from_static("text/plain; charset=utf-8") HeaderValue::from_static("text/plain; charset=utf-8")
); );
let resp: HttpResponse = block_on(b"test".respond_to(&req)).unwrap(); let resp: HttpResponse = b"test".respond_to(&req).await.unwrap();
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
assert_eq!(resp.body().bin_ref(), b"test"); assert_eq!(resp.body().bin_ref(), b"test");
assert_eq!( assert_eq!(
@ -548,7 +550,7 @@ pub(crate) mod tests {
HeaderValue::from_static("application/octet-stream") HeaderValue::from_static("application/octet-stream")
); );
let resp: HttpResponse = block_on("test".to_string().respond_to(&req)).unwrap(); let resp: HttpResponse = "test".to_string().respond_to(&req).await.unwrap();
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
assert_eq!(resp.body().bin_ref(), b"test"); assert_eq!(resp.body().bin_ref(), b"test");
assert_eq!( assert_eq!(
@ -557,7 +559,7 @@ pub(crate) mod tests {
); );
let resp: HttpResponse = let resp: HttpResponse =
block_on((&"test".to_string()).respond_to(&req)).unwrap(); (&"test".to_string()).respond_to(&req).await.unwrap();
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
assert_eq!(resp.body().bin_ref(), b"test"); assert_eq!(resp.body().bin_ref(), b"test");
assert_eq!( assert_eq!(
@ -566,7 +568,7 @@ pub(crate) mod tests {
); );
let resp: HttpResponse = let resp: HttpResponse =
block_on(Bytes::from_static(b"test").respond_to(&req)).unwrap(); Bytes::from_static(b"test").respond_to(&req).await.unwrap();
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
assert_eq!(resp.body().bin_ref(), b"test"); assert_eq!(resp.body().bin_ref(), b"test");
assert_eq!( assert_eq!(
@ -574,8 +576,10 @@ pub(crate) mod tests {
HeaderValue::from_static("application/octet-stream") HeaderValue::from_static("application/octet-stream")
); );
let resp: HttpResponse = let resp: HttpResponse = BytesMut::from(b"test".as_ref())
block_on(BytesMut::from(b"test".as_ref()).respond_to(&req)).unwrap(); .respond_to(&req)
.await
.unwrap();
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
assert_eq!(resp.body().bin_ref(), b"test"); assert_eq!(resp.body().bin_ref(), b"test");
assert_eq!( assert_eq!(
@ -587,17 +591,22 @@ pub(crate) mod tests {
let resp: HttpResponse = let resp: HttpResponse =
error::InternalError::new("err", StatusCode::BAD_REQUEST) error::InternalError::new("err", StatusCode::BAD_REQUEST)
.respond_to(&req) .respond_to(&req)
.await
.unwrap(); .unwrap();
assert_eq!(resp.status(), StatusCode::BAD_REQUEST); assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
})
} }
#[test] #[test]
fn test_result_responder() { fn test_result_responder() {
block_on(async {
let req = TestRequest::default().to_http_request(); let req = TestRequest::default().to_http_request();
// Result<I, E> // Result<I, E>
let resp: HttpResponse = let resp: HttpResponse = Ok::<_, Error>("test".to_string())
block_on(Ok::<_, Error>("test".to_string()).respond_to(&req)).unwrap(); .respond_to(&req)
.await
.unwrap();
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
assert_eq!(resp.body().bin_ref(), b"test"); assert_eq!(resp.body().bin_ref(), b"test");
assert_eq!( assert_eq!(
@ -605,32 +614,34 @@ pub(crate) mod tests {
HeaderValue::from_static("text/plain; charset=utf-8") HeaderValue::from_static("text/plain; charset=utf-8")
); );
let res = block_on( let res = Err::<String, _>(error::InternalError::new(
Err::<String, _>(error::InternalError::new("err", StatusCode::BAD_REQUEST)) "err",
.respond_to(&req), StatusCode::BAD_REQUEST,
); ))
.respond_to(&req)
.await;
assert!(res.is_err()); assert!(res.is_err());
})
} }
#[test] #[test]
fn test_custom_responder() { fn test_custom_responder() {
block_on(async {
let req = TestRequest::default().to_http_request(); let req = TestRequest::default().to_http_request();
let res = block_on( let res = "test"
"test"
.to_string() .to_string()
.with_status(StatusCode::BAD_REQUEST) .with_status(StatusCode::BAD_REQUEST)
.respond_to(&req), .respond_to(&req)
) .await
.unwrap(); .unwrap();
assert_eq!(res.status(), StatusCode::BAD_REQUEST); assert_eq!(res.status(), StatusCode::BAD_REQUEST);
assert_eq!(res.body().bin_ref(), b"test"); assert_eq!(res.body().bin_ref(), b"test");
let res = block_on( let res = "test"
"test"
.to_string() .to_string()
.with_header("content-type", "json") .with_header("content-type", "json")
.respond_to(&req), .respond_to(&req)
) .await
.unwrap(); .unwrap();
assert_eq!(res.status(), StatusCode::OK); assert_eq!(res.status(), StatusCode::OK);
@ -639,23 +650,25 @@ pub(crate) mod tests {
res.headers().get(CONTENT_TYPE).unwrap(), res.headers().get(CONTENT_TYPE).unwrap(),
HeaderValue::from_static("json") HeaderValue::from_static("json")
); );
})
} }
#[test] #[test]
fn test_tuple_responder_with_status_code() { fn test_tuple_responder_with_status_code() {
block_on(async {
let req = TestRequest::default().to_http_request(); let req = TestRequest::default().to_http_request();
let res = let res = ("test".to_string(), StatusCode::BAD_REQUEST)
block_on(("test".to_string(), StatusCode::BAD_REQUEST).respond_to(&req)) .respond_to(&req)
.await
.unwrap(); .unwrap();
assert_eq!(res.status(), StatusCode::BAD_REQUEST); assert_eq!(res.status(), StatusCode::BAD_REQUEST);
assert_eq!(res.body().bin_ref(), b"test"); assert_eq!(res.body().bin_ref(), b"test");
let req = TestRequest::default().to_http_request(); let req = TestRequest::default().to_http_request();
let res = block_on( let res = ("test".to_string(), StatusCode::OK)
("test".to_string(), StatusCode::OK)
.with_header("content-type", "json") .with_header("content-type", "json")
.respond_to(&req), .respond_to(&req)
) .await
.unwrap(); .unwrap();
assert_eq!(res.status(), StatusCode::OK); assert_eq!(res.status(), StatusCode::OK);
assert_eq!(res.body().bin_ref(), b"test"); assert_eq!(res.body().bin_ref(), b"test");
@ -663,5 +676,6 @@ pub(crate) mod tests {
res.headers().get(CONTENT_TYPE).unwrap(), res.headers().get(CONTENT_TYPE).unwrap(),
HeaderValue::from_static("json") HeaderValue::from_static("json")
); );
})
} }
} }

View file

@ -1,9 +1,11 @@
use std::future::Future;
use std::pin::Pin;
use std::rc::Rc; use std::rc::Rc;
use std::task::{Context, Poll};
use actix_http::{http::Method, Error}; use actix_http::{http::Method, Error};
use actix_service::{NewService, Service}; use actix_service::{Service, ServiceFactory};
use futures::future::{ok, Either, FutureResult}; use futures::future::{ok, Either, FutureExt, LocalBoxFuture, Ready};
use futures::{Async, Future, IntoFuture, Poll};
use crate::extract::FromRequest; use crate::extract::FromRequest;
use crate::guard::{self, Guard}; use crate::guard::{self, Guard};
@ -17,22 +19,19 @@ type BoxedRouteService<Req, Res> = Box<
Request = Req, Request = Req,
Response = Res, Response = Res,
Error = Error, Error = Error,
Future = Either< Future = LocalBoxFuture<'static, Result<Res, Error>>,
FutureResult<Res, Error>,
Box<dyn Future<Item = Res, Error = Error>>,
>,
>, >,
>; >;
type BoxedRouteNewService<Req, Res> = Box< type BoxedRouteNewService<Req, Res> = Box<
dyn NewService< dyn ServiceFactory<
Config = (), Config = (),
Request = Req, Request = Req,
Response = Res, Response = Res,
Error = Error, Error = Error,
InitError = (), InitError = (),
Service = BoxedRouteService<Req, Res>, Service = BoxedRouteService<Req, Res>,
Future = Box<dyn Future<Item = BoxedRouteService<Req, Res>, Error = ()>>, Future = LocalBoxFuture<'static, Result<BoxedRouteService<Req, Res>, ()>>,
>, >,
>; >;
@ -61,7 +60,7 @@ impl Route {
} }
} }
impl NewService for Route { impl ServiceFactory for Route {
type Config = (); type Config = ();
type Request = ServiceRequest; type Request = ServiceRequest;
type Response = ServiceResponse; type Response = ServiceResponse;
@ -78,26 +77,30 @@ impl NewService for Route {
} }
} }
type RouteFuture = Box< type RouteFuture = LocalBoxFuture<
dyn Future<Item = BoxedRouteService<ServiceRequest, ServiceResponse>, Error = ()>, 'static,
Result<BoxedRouteService<ServiceRequest, ServiceResponse>, ()>,
>; >;
#[pin_project::pin_project]
pub struct CreateRouteService { pub struct CreateRouteService {
#[pin]
fut: RouteFuture, fut: RouteFuture,
guards: Rc<Vec<Box<dyn Guard>>>, guards: Rc<Vec<Box<dyn Guard>>>,
} }
impl Future for CreateRouteService { impl Future for CreateRouteService {
type Item = RouteService; type Output = Result<RouteService, ()>;
type Error = ();
fn poll(&mut self) -> Poll<Self::Item, Self::Error> { fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
match self.fut.poll()? { let this = self.project();
Async::Ready(service) => Ok(Async::Ready(RouteService {
match this.fut.poll(cx)? {
Poll::Ready(service) => Poll::Ready(Ok(RouteService {
service, service,
guards: self.guards.clone(), guards: this.guards.clone(),
})), })),
Async::NotReady => Ok(Async::NotReady), Poll::Pending => Poll::Pending,
} }
} }
} }
@ -122,17 +125,14 @@ impl Service for RouteService {
type Request = ServiceRequest; type Request = ServiceRequest;
type Response = ServiceResponse; type Response = ServiceResponse;
type Error = Error; type Error = Error;
type Future = Either< type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
FutureResult<Self::Response, Self::Error>,
Box<dyn Future<Item = Self::Response, Error = Self::Error>>,
>;
fn poll_ready(&mut self) -> Poll<(), Self::Error> { fn poll_ready(&mut self, cx: &mut Context) -> Poll<Result<(), Self::Error>> {
self.service.poll_ready() self.service.poll_ready(cx)
} }
fn call(&mut self, req: ServiceRequest) -> Self::Future { fn call(&mut self, req: ServiceRequest) -> Self::Future {
self.service.call(req) self.service.call(req).boxed_local()
} }
} }
@ -249,8 +249,8 @@ impl Route {
/// } /// }
/// ///
/// /// extract path info using serde /// /// extract path info using serde
/// fn index(info: web::Path<Info>) -> impl Future<Item = &'static str, Error = Error> { /// async fn index(info: web::Path<Info>) -> Result<&'static str, Error> {
/// ok("Hello World!") /// Ok("Hello World!")
/// } /// }
/// ///
/// fn main() { /// fn main() {
@ -261,13 +261,13 @@ impl Route {
/// } /// }
/// ``` /// ```
#[allow(clippy::wrong_self_convention)] #[allow(clippy::wrong_self_convention)]
pub fn to_async<F, T, R>(mut self, handler: F) -> Self pub fn to_async<F, T, R, O, E>(mut self, handler: F) -> Self
where where
F: AsyncFactory<T, R>, F: AsyncFactory<T, R, O, E>,
T: FromRequest + 'static, T: FromRequest + 'static,
R: IntoFuture + 'static, R: Future<Output = Result<O, E>> + 'static,
R::Item: Responder, O: Responder + 'static,
R::Error: Into<Error>, E: Into<Error> + 'static,
{ {
self.service = Box::new(RouteNewService::new(Extract::new(AsyncHandler::new( self.service = Box::new(RouteNewService::new(Extract::new(AsyncHandler::new(
handler, handler,
@ -278,14 +278,14 @@ impl Route {
struct RouteNewService<T> struct RouteNewService<T>
where where
T: NewService<Request = ServiceRequest, Error = (Error, ServiceRequest)>, T: ServiceFactory<Request = ServiceRequest, Error = (Error, ServiceRequest)>,
{ {
service: T, service: T,
} }
impl<T> RouteNewService<T> impl<T> RouteNewService<T>
where where
T: NewService< T: ServiceFactory<
Config = (), Config = (),
Request = ServiceRequest, Request = ServiceRequest,
Response = ServiceResponse, Response = ServiceResponse,
@ -300,9 +300,9 @@ where
} }
} }
impl<T> NewService for RouteNewService<T> impl<T> ServiceFactory for RouteNewService<T>
where where
T: NewService< T: ServiceFactory<
Config = (), Config = (),
Request = ServiceRequest, Request = ServiceRequest,
Response = ServiceResponse, Response = ServiceResponse,
@ -318,19 +318,20 @@ where
type Error = Error; type Error = Error;
type InitError = (); type InitError = ();
type Service = BoxedRouteService<ServiceRequest, Self::Response>; type Service = BoxedRouteService<ServiceRequest, Self::Response>;
type Future = Box<dyn Future<Item = Self::Service, Error = Self::InitError>>; type Future = LocalBoxFuture<'static, Result<Self::Service, Self::InitError>>;
fn new_service(&self, _: &()) -> Self::Future { fn new_service(&self, _: &()) -> Self::Future {
Box::new(
self.service self.service
.new_service(&()) .new_service(&())
.map_err(|_| ()) .map(|result| match result {
.and_then(|service| { Ok(service) => {
let service: BoxedRouteService<_, _> = let service: BoxedRouteService<_, _> =
Box::new(RouteServiceWrapper { service }); Box::new(RouteServiceWrapper { service });
Ok(service) Ok(service)
}), }
) Err(_) => Err(()),
})
.boxed_local()
} }
} }
@ -350,25 +351,30 @@ where
type Request = ServiceRequest; type Request = ServiceRequest;
type Response = ServiceResponse; type Response = ServiceResponse;
type Error = Error; type Error = Error;
type Future = Either< type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
FutureResult<Self::Response, Self::Error>,
Box<dyn Future<Item = Self::Response, Error = Self::Error>>,
>;
fn poll_ready(&mut self) -> Poll<(), Self::Error> { fn poll_ready(&mut self, cx: &mut Context) -> Poll<Result<(), Self::Error>> {
self.service.poll_ready().map_err(|(e, _)| e) self.service.poll_ready(cx).map_err(|(e, _)| e)
} }
fn call(&mut self, req: ServiceRequest) -> Self::Future { fn call(&mut self, req: ServiceRequest) -> Self::Future {
let mut fut = self.service.call(req); // let mut fut = self.service.call(req);
match fut.poll() { self.service
Ok(Async::Ready(res)) => Either::A(ok(res)), .call(req)
Err((e, req)) => Either::A(ok(req.error_response(e))), .map(|res| match res {
Ok(Async::NotReady) => Either::B(Box::new(fut.then(|res| match res {
Ok(res) => Ok(res), Ok(res) => Ok(res),
Err((err, req)) => Ok(req.error_response(err)), Err((err, req)) => Ok(req.error_response(err)),
}))), })
} .boxed_local()
// match fut.poll() {
// Poll::Ready(Ok(res)) => Either::Left(ok(res)),
// Poll::Ready(Err((e, req))) => Either::Left(ok(req.error_response(e))),
// Poll::Pending => Either::Right(Box::new(fut.then(|res| match res {
// Ok(res) => Ok(res),
// Err((err, req)) => Ok(req.error_response(err)),
// }))),
// }
} }
} }
@ -379,11 +385,11 @@ mod tests {
use bytes::Bytes; use bytes::Bytes;
use futures::Future; use futures::Future;
use serde_derive::Serialize; use serde_derive::Serialize;
use tokio_timer::sleep; use tokio_timer::delay_for;
use crate::http::{Method, StatusCode}; use crate::http::{Method, StatusCode};
use crate::test::{call_service, init_service, read_body, TestRequest}; use crate::test::{block_on, call_service, init_service, read_body, TestRequest};
use crate::{error, web, App, HttpResponse}; use crate::{error, web, App, Error, HttpResponse};
#[derive(Serialize, PartialEq, Debug)] #[derive(Serialize, PartialEq, Debug)]
struct MyObject { struct MyObject {
@ -392,6 +398,7 @@ mod tests {
#[test] #[test]
fn test_route() { fn test_route() {
block_on(async {
let mut srv = init_service( let mut srv = init_service(
App::new() App::new()
.service( .service(
@ -401,59 +408,65 @@ mod tests {
Err::<HttpResponse, _>(error::ErrorBadRequest("err")) Err::<HttpResponse, _>(error::ErrorBadRequest("err"))
})) }))
.route(web::post().to_async(|| { .route(web::post().to_async(|| {
sleep(Duration::from_millis(100)) async {
.then(|_| HttpResponse::Created()) delay_for(Duration::from_millis(100)).await;
Ok::<_, Error>(HttpResponse::Created())
}
})) }))
.route(web::delete().to_async(|| { .route(web::delete().to_async(|| {
sleep(Duration::from_millis(100)).then(|_| { async {
delay_for(Duration::from_millis(100)).await;
Err::<HttpResponse, _>(error::ErrorBadRequest("err")) Err::<HttpResponse, _>(error::ErrorBadRequest("err"))
}) }
})), })),
) )
.service(web::resource("/json").route(web::get().to_async(|| { .service(web::resource("/json").route(web::get().to_async(|| {
sleep(Duration::from_millis(25)).then(|_| { async {
Ok::<_, crate::Error>(web::Json(MyObject { delay_for(Duration::from_millis(25)).await;
Ok::<_, Error>(web::Json(MyObject {
name: "test".to_string(), name: "test".to_string(),
})) }))
}) }
}))), }))),
); )
.await;
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); let resp = call_service(&mut 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); let resp = call_service(&mut 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); let resp = call_service(&mut 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); let resp = call_service(&mut 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); let resp = call_service(&mut 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); let resp = call_service(&mut srv, req).await;
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
let body = read_body(resp); let body = read_body(resp).await;
assert_eq!(body, Bytes::from_static(b"{\"name\":\"test\"}")); assert_eq!(body, Bytes::from_static(b"{\"name\":\"test\"}"));
})
} }
} }

View file

@ -1,15 +1,16 @@
use std::cell::RefCell; use std::cell::RefCell;
use std::fmt; use std::fmt;
use std::pin::Pin;
use std::rc::Rc; use std::rc::Rc;
use std::task::{Context, Poll};
use actix_http::{Extensions, Response}; use actix_http::{Extensions, Response};
use actix_router::{ResourceDef, ResourceInfo, Router}; use actix_router::{ResourceDef, ResourceInfo, Router};
use actix_service::boxed::{self, BoxedNewService, BoxedService}; use actix_service::boxed::{self, BoxedNewService, BoxedService};
use actix_service::{ use actix_service::{
apply_transform, IntoNewService, IntoTransform, NewService, Service, Transform, apply, apply_fn_factory, IntoServiceFactory, Service, ServiceFactory, Transform,
}; };
use futures::future::{ok, Either, Future, FutureResult}; use futures::future::{ok, Either, Future, LocalBoxFuture, Ready};
use futures::{Async, IntoFuture, Poll};
use crate::config::ServiceConfig; use crate::config::ServiceConfig;
use crate::data::Data; use crate::data::Data;
@ -20,16 +21,13 @@ use crate::resource::Resource;
use crate::rmap::ResourceMap; use crate::rmap::ResourceMap;
use crate::route::Route; use crate::route::Route;
use crate::service::{ use crate::service::{
ServiceFactory, ServiceFactoryWrapper, ServiceRequest, ServiceResponse, AppServiceFactory, ServiceFactoryWrapper, ServiceRequest, ServiceResponse,
}; };
type Guards = Vec<Box<dyn Guard>>; type Guards = Vec<Box<dyn Guard>>;
type HttpService = BoxedService<ServiceRequest, ServiceResponse, Error>; type HttpService = BoxedService<ServiceRequest, ServiceResponse, Error>;
type HttpNewService = BoxedNewService<(), ServiceRequest, ServiceResponse, Error, ()>; type HttpNewService = BoxedNewService<(), ServiceRequest, ServiceResponse, Error, ()>;
type BoxedResponse = Either< type BoxedResponse = LocalBoxFuture<'static, Result<ServiceResponse, Error>>;
FutureResult<ServiceResponse, Error>,
Box<dyn Future<Item = ServiceResponse, Error = Error>>,
>;
/// Resources scope. /// Resources scope.
/// ///
@ -64,7 +62,7 @@ pub struct Scope<T = ScopeEndpoint> {
endpoint: T, endpoint: T,
rdef: String, rdef: String,
data: Option<Extensions>, data: Option<Extensions>,
services: Vec<Box<dyn ServiceFactory>>, services: Vec<Box<dyn AppServiceFactory>>,
guards: Vec<Box<dyn Guard>>, guards: Vec<Box<dyn Guard>>,
default: Rc<RefCell<Option<Rc<HttpNewService>>>>, default: Rc<RefCell<Option<Rc<HttpNewService>>>>,
external: Vec<ResourceDef>, external: Vec<ResourceDef>,
@ -90,7 +88,7 @@ impl Scope {
impl<T> Scope<T> impl<T> Scope<T>
where where
T: NewService< T: ServiceFactory<
Config = (), Config = (),
Request = ServiceRequest, Request = ServiceRequest,
Response = ServiceResponse, Response = ServiceResponse,
@ -285,8 +283,8 @@ where
/// If default resource is not registered, app's default resource is being used. /// If default resource is not registered, app's default resource is being used.
pub fn default_service<F, U>(mut self, f: F) -> Self pub fn default_service<F, U>(mut self, f: F) -> Self
where where
F: IntoNewService<U>, F: IntoServiceFactory<U>,
U: NewService< U: ServiceFactory<
Config = (), Config = (),
Request = ServiceRequest, Request = ServiceRequest,
Response = ServiceResponse, Response = ServiceResponse,
@ -295,8 +293,8 @@ where
U::InitError: fmt::Debug, U::InitError: fmt::Debug,
{ {
// create and configure default resource // create and configure default resource
self.default = Rc::new(RefCell::new(Some(Rc::new(boxed::new_service( self.default = Rc::new(RefCell::new(Some(Rc::new(boxed::factory(
f.into_new_service().map_init_err(|e| { f.into_factory().map_init_err(|e| {
log::error!("Can not construct default service: {:?}", e) log::error!("Can not construct default service: {:?}", e)
}), }),
))))); )))));
@ -313,11 +311,11 @@ where
/// ServiceResponse. /// ServiceResponse.
/// ///
/// Use middleware when you need to read or modify *every* request in some way. /// Use middleware when you need to read or modify *every* request in some way.
pub fn wrap<M, F>( pub fn wrap<M>(
self, self,
mw: F, mw: M,
) -> Scope< ) -> Scope<
impl NewService< impl ServiceFactory<
Config = (), Config = (),
Request = ServiceRequest, Request = ServiceRequest,
Response = ServiceResponse, Response = ServiceResponse,
@ -333,11 +331,9 @@ where
Error = Error, Error = Error,
InitError = (), InitError = (),
>, >,
F: IntoTransform<M, T::Service>,
{ {
let endpoint = apply_transform(mw, self.endpoint);
Scope { Scope {
endpoint, endpoint: apply(mw, self.endpoint),
rdef: self.rdef, rdef: self.rdef,
data: self.data, data: self.data,
guards: self.guards, guards: self.guards,
@ -368,13 +364,16 @@ where
/// fn main() { /// fn main() {
/// let app = App::new().service( /// let app = App::new().service(
/// web::scope("/app") /// web::scope("/app")
/// .wrap_fn(|req, srv| /// .wrap_fn(|req, srv| {
/// srv.call(req).map(|mut res| { /// let fut = srv.call(req);
/// async {
/// let mut res = fut.await?;
/// res.headers_mut().insert( /// res.headers_mut().insert(
/// CONTENT_TYPE, HeaderValue::from_static("text/plain"), /// CONTENT_TYPE, HeaderValue::from_static("text/plain"),
/// ); /// );
/// res /// Ok(res)
/// })) /// }
/// })
/// .route("/index.html", web::get().to(index))); /// .route("/index.html", web::get().to(index)));
/// } /// }
/// ``` /// ```
@ -382,7 +381,7 @@ where
self, self,
mw: F, mw: F,
) -> Scope< ) -> Scope<
impl NewService< impl ServiceFactory<
Config = (), Config = (),
Request = ServiceRequest, Request = ServiceRequest,
Response = ServiceResponse, Response = ServiceResponse,
@ -392,15 +391,24 @@ where
> >
where where
F: FnMut(ServiceRequest, &mut T::Service) -> R + Clone, F: FnMut(ServiceRequest, &mut T::Service) -> R + Clone,
R: IntoFuture<Item = ServiceResponse, Error = Error>, R: Future<Output = Result<ServiceResponse, Error>>,
{ {
self.wrap(mw) Scope {
endpoint: apply_fn_factory(self.endpoint, mw),
rdef: self.rdef,
data: self.data,
guards: self.guards,
services: self.services,
default: self.default,
external: self.external,
factory_ref: self.factory_ref,
}
} }
} }
impl<T> HttpServiceFactory for Scope<T> impl<T> HttpServiceFactory for Scope<T>
where where
T: NewService< T: ServiceFactory<
Config = (), Config = (),
Request = ServiceRequest, Request = ServiceRequest,
Response = ServiceResponse, Response = ServiceResponse,
@ -471,7 +479,7 @@ pub struct ScopeFactory {
default: Rc<RefCell<Option<Rc<HttpNewService>>>>, default: Rc<RefCell<Option<Rc<HttpNewService>>>>,
} }
impl NewService for ScopeFactory { impl ServiceFactory for ScopeFactory {
type Config = (); type Config = ();
type Request = ServiceRequest; type Request = ServiceRequest;
type Response = ServiceResponse; type Response = ServiceResponse;
@ -508,14 +516,15 @@ impl NewService for ScopeFactory {
/// Create scope service /// Create scope service
#[doc(hidden)] #[doc(hidden)]
#[pin_project::pin_project]
pub struct ScopeFactoryResponse { pub struct ScopeFactoryResponse {
fut: Vec<CreateScopeServiceItem>, fut: Vec<CreateScopeServiceItem>,
data: Option<Rc<Extensions>>, data: Option<Rc<Extensions>>,
default: Option<HttpService>, default: Option<HttpService>,
default_fut: Option<Box<dyn Future<Item = HttpService, Error = ()>>>, default_fut: Option<LocalBoxFuture<'static, Result<HttpService, ()>>>,
} }
type HttpServiceFut = Box<dyn Future<Item = HttpService, Error = ()>>; type HttpServiceFut = LocalBoxFuture<'static, Result<HttpService, ()>>;
enum CreateScopeServiceItem { enum CreateScopeServiceItem {
Future(Option<ResourceDef>, Option<Guards>, HttpServiceFut), Future(Option<ResourceDef>, Option<Guards>, HttpServiceFut),
@ -523,16 +532,15 @@ enum CreateScopeServiceItem {
} }
impl Future for ScopeFactoryResponse { impl Future for ScopeFactoryResponse {
type Item = ScopeService; type Output = Result<ScopeService, ()>;
type Error = ();
fn poll(&mut self) -> Poll<Self::Item, Self::Error> { fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
let mut done = true; let mut done = true;
if let Some(ref mut fut) = self.default_fut { if let Some(ref mut fut) = self.default_fut {
match fut.poll()? { match Pin::new(fut).poll(cx)? {
Async::Ready(default) => self.default = Some(default), Poll::Ready(default) => self.default = Some(default),
Async::NotReady => done = false, Poll::Pending => done = false,
} }
} }
@ -543,11 +551,11 @@ impl Future for ScopeFactoryResponse {
ref mut path, ref mut path,
ref mut guards, ref mut guards,
ref mut fut, ref mut fut,
) => match fut.poll()? { ) => match Pin::new(fut).poll(cx)? {
Async::Ready(service) => { Poll::Ready(service) => {
Some((path.take().unwrap(), guards.take(), service)) Some((path.take().unwrap(), guards.take(), service))
} }
Async::NotReady => { Poll::Pending => {
done = false; done = false;
None None
} }
@ -573,14 +581,14 @@ impl Future for ScopeFactoryResponse {
} }
router router
}); });
Ok(Async::Ready(ScopeService { Poll::Ready(Ok(ScopeService {
data: self.data.clone(), data: self.data.clone(),
router: router.finish(), router: router.finish(),
default: self.default.take(), default: self.default.take(),
_ready: None, _ready: None,
})) }))
} else { } else {
Ok(Async::NotReady) Poll::Pending
} }
} }
} }
@ -596,10 +604,10 @@ impl Service for ScopeService {
type Request = ServiceRequest; type Request = ServiceRequest;
type Response = ServiceResponse; type Response = ServiceResponse;
type Error = Error; type Error = Error;
type Future = Either<BoxedResponse, FutureResult<Self::Response, Self::Error>>; type Future = Either<BoxedResponse, Ready<Result<Self::Response, Self::Error>>>;
fn poll_ready(&mut self) -> Poll<(), Self::Error> { fn poll_ready(&mut self, _: &mut Context) -> Poll<Result<(), Self::Error>> {
Ok(Async::Ready(())) Poll::Ready(Ok(()))
} }
fn call(&mut self, mut req: ServiceRequest) -> Self::Future { fn call(&mut self, mut req: ServiceRequest) -> Self::Future {
@ -618,12 +626,12 @@ impl Service for ScopeService {
if let Some(ref data) = self.data { if let Some(ref data) = self.data {
req.set_data_container(data.clone()); req.set_data_container(data.clone());
} }
Either::A(srv.call(req)) Either::Left(srv.call(req))
} else if let Some(ref mut default) = self.default { } else if let Some(ref mut default) = self.default {
Either::A(default.call(req)) Either::Left(default.call(req))
} else { } else {
let req = req.into_parts().0; let req = req.into_parts().0;
Either::B(ok(ServiceResponse::new(req, Response::NotFound().finish()))) Either::Right(ok(ServiceResponse::new(req, Response::NotFound().finish())))
} }
} }
} }
@ -639,7 +647,7 @@ impl ScopeEndpoint {
} }
} }
impl NewService for ScopeEndpoint { impl ServiceFactory for ScopeEndpoint {
type Config = (); type Config = ();
type Request = ServiceRequest; type Request = ServiceRequest;
type Response = ServiceResponse; type Response = ServiceResponse;
@ -657,106 +665,127 @@ impl NewService for ScopeEndpoint {
mod tests { mod tests {
use actix_service::Service; use actix_service::Service;
use bytes::Bytes; use bytes::Bytes;
use futures::{Future, IntoFuture}; use futures::future::ok;
use futures::Future;
use crate::dev::{Body, ResponseBody}; use crate::dev::{Body, ResponseBody};
use crate::http::{header, HeaderValue, Method, StatusCode}; use crate::http::{header, HeaderValue, Method, StatusCode};
use crate::middleware::DefaultHeaders;
use crate::service::{ServiceRequest, ServiceResponse}; use crate::service::{ServiceRequest, ServiceResponse};
use crate::test::{block_on, call_service, init_service, read_body, TestRequest}; use crate::test::{block_on, call_service, init_service, read_body, TestRequest};
use crate::{guard, web, App, Error, HttpRequest, HttpResponse}; use crate::{guard, web, App, Error, HttpRequest, HttpResponse};
#[test] #[test]
fn test_scope() { fn test_scope() {
block_on(async {
let mut srv = init_service( let mut srv = init_service(
App::new().service( App::new().service(
web::scope("/app") web::scope("/app")
.service(web::resource("/path1").to(|| HttpResponse::Ok())), .service(web::resource("/path1").to(|| HttpResponse::Ok())),
), ),
); )
.await;
let req = TestRequest::with_uri("/app/path1").to_request(); let req = TestRequest::with_uri("/app/path1").to_request();
let resp = block_on(srv.call(req)).unwrap(); let resp = srv.call(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
})
} }
#[test] #[test]
fn test_scope_root() { fn test_scope_root() {
block_on(async {
let mut srv = init_service( let mut srv = init_service(
App::new().service( App::new().service(
web::scope("/app") web::scope("/app")
.service(web::resource("").to(|| HttpResponse::Ok())) .service(web::resource("").to(|| HttpResponse::Ok()))
.service(web::resource("/").to(|| HttpResponse::Created())), .service(web::resource("/").to(|| HttpResponse::Created())),
), ),
); )
.await;
let req = TestRequest::with_uri("/app").to_request(); let req = TestRequest::with_uri("/app").to_request();
let resp = block_on(srv.call(req)).unwrap(); let resp = srv.call(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
let req = TestRequest::with_uri("/app/").to_request(); let req = TestRequest::with_uri("/app/").to_request();
let resp = block_on(srv.call(req)).unwrap(); let resp = srv.call(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::CREATED); assert_eq!(resp.status(), StatusCode::CREATED);
})
} }
#[test] #[test]
fn test_scope_root2() { fn test_scope_root2() {
block_on(async {
let mut srv = init_service(App::new().service( let mut srv = init_service(App::new().service(
web::scope("/app/").service(web::resource("").to(|| HttpResponse::Ok())), web::scope("/app/").service(web::resource("").to(|| HttpResponse::Ok())),
)); ))
.await;
let req = TestRequest::with_uri("/app").to_request(); let req = TestRequest::with_uri("/app").to_request();
let resp = block_on(srv.call(req)).unwrap(); let resp = srv.call(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::NOT_FOUND); assert_eq!(resp.status(), StatusCode::NOT_FOUND);
let req = TestRequest::with_uri("/app/").to_request(); let req = TestRequest::with_uri("/app/").to_request();
let resp = block_on(srv.call(req)).unwrap(); let resp = srv.call(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
})
} }
#[test] #[test]
fn test_scope_root3() { fn test_scope_root3() {
let mut srv = init_service(App::new().service( block_on(async {
web::scope("/app/").service(web::resource("/").to(|| HttpResponse::Ok())), let mut srv = init_service(
)); App::new().service(
web::scope("/app/")
.service(web::resource("/").to(|| HttpResponse::Ok())),
),
)
.await;
let req = TestRequest::with_uri("/app").to_request(); let req = TestRequest::with_uri("/app").to_request();
let resp = block_on(srv.call(req)).unwrap(); let resp = srv.call(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::NOT_FOUND); assert_eq!(resp.status(), StatusCode::NOT_FOUND);
let req = TestRequest::with_uri("/app/").to_request(); let req = TestRequest::with_uri("/app/").to_request();
let resp = block_on(srv.call(req)).unwrap(); let resp = srv.call(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::NOT_FOUND); assert_eq!(resp.status(), StatusCode::NOT_FOUND);
})
} }
#[test] #[test]
fn test_scope_route() { fn test_scope_route() {
block_on(async {
let mut srv = init_service( let mut srv = init_service(
App::new().service( App::new().service(
web::scope("app") web::scope("app")
.route("/path1", web::get().to(|| HttpResponse::Ok())) .route("/path1", web::get().to(|| HttpResponse::Ok()))
.route("/path1", web::delete().to(|| HttpResponse::Ok())), .route("/path1", web::delete().to(|| HttpResponse::Ok())),
), ),
); )
.await;
let req = TestRequest::with_uri("/app/path1").to_request(); let req = TestRequest::with_uri("/app/path1").to_request();
let resp = block_on(srv.call(req)).unwrap(); let resp = srv.call(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
let req = TestRequest::with_uri("/app/path1") let req = TestRequest::with_uri("/app/path1")
.method(Method::DELETE) .method(Method::DELETE)
.to_request(); .to_request();
let resp = block_on(srv.call(req)).unwrap(); let resp = srv.call(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
let req = TestRequest::with_uri("/app/path1") let req = TestRequest::with_uri("/app/path1")
.method(Method::POST) .method(Method::POST)
.to_request(); .to_request();
let resp = block_on(srv.call(req)).unwrap(); let resp = srv.call(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::NOT_FOUND); assert_eq!(resp.status(), StatusCode::NOT_FOUND);
})
} }
#[test] #[test]
fn test_scope_route_without_leading_slash() { fn test_scope_route_without_leading_slash() {
block_on(async {
let mut srv = init_service( let mut srv = init_service(
App::new().service( App::new().service(
web::scope("app").service( web::scope("app").service(
@ -765,60 +794,67 @@ mod tests {
.route(web::delete().to(|| HttpResponse::Ok())), .route(web::delete().to(|| HttpResponse::Ok())),
), ),
), ),
); )
.await;
let req = TestRequest::with_uri("/app/path1").to_request(); let req = TestRequest::with_uri("/app/path1").to_request();
let resp = block_on(srv.call(req)).unwrap(); let resp = srv.call(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
let req = TestRequest::with_uri("/app/path1") let req = TestRequest::with_uri("/app/path1")
.method(Method::DELETE) .method(Method::DELETE)
.to_request(); .to_request();
let resp = block_on(srv.call(req)).unwrap(); let resp = srv.call(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
let req = TestRequest::with_uri("/app/path1") let req = TestRequest::with_uri("/app/path1")
.method(Method::POST) .method(Method::POST)
.to_request(); .to_request();
let resp = block_on(srv.call(req)).unwrap(); let resp = srv.call(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED); assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED);
})
} }
#[test] #[test]
fn test_scope_guard() { fn test_scope_guard() {
block_on(async {
let mut srv = init_service( let mut srv = init_service(
App::new().service( App::new().service(
web::scope("/app") web::scope("/app")
.guard(guard::Get()) .guard(guard::Get())
.service(web::resource("/path1").to(|| HttpResponse::Ok())), .service(web::resource("/path1").to(|| HttpResponse::Ok())),
), ),
); )
.await;
let req = TestRequest::with_uri("/app/path1") let req = TestRequest::with_uri("/app/path1")
.method(Method::POST) .method(Method::POST)
.to_request(); .to_request();
let resp = block_on(srv.call(req)).unwrap(); let resp = srv.call(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::NOT_FOUND); assert_eq!(resp.status(), StatusCode::NOT_FOUND);
let req = TestRequest::with_uri("/app/path1") let req = TestRequest::with_uri("/app/path1")
.method(Method::GET) .method(Method::GET)
.to_request(); .to_request();
let resp = block_on(srv.call(req)).unwrap(); let resp = srv.call(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
})
} }
#[test] #[test]
fn test_scope_variable_segment() { fn test_scope_variable_segment() {
block_on(async {
let mut srv = let mut 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"]))
}), }),
))); )))
.await;
let req = TestRequest::with_uri("/ab-project1/path1").to_request(); let req = TestRequest::with_uri("/ab-project1/path1").to_request();
let resp = block_on(srv.call(req)).unwrap(); let resp = srv.call(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
match resp.response().body() { match resp.response().body() {
@ -830,44 +866,48 @@ mod tests {
} }
let req = TestRequest::with_uri("/aa-project1/path1").to_request(); let req = TestRequest::with_uri("/aa-project1/path1").to_request();
let resp = block_on(srv.call(req)).unwrap(); let resp = srv.call(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::NOT_FOUND); assert_eq!(resp.status(), StatusCode::NOT_FOUND);
})
} }
#[test] #[test]
fn test_nested_scope() { fn test_nested_scope() {
let mut srv = init_service( block_on(async {
App::new().service( let mut srv =
web::scope("/app") init_service(App::new().service(
.service(web::scope("/t1").service( web::scope("/app").service(web::scope("/t1").service(
web::resource("/path1").to(|| HttpResponse::Created()), web::resource("/path1").to(|| HttpResponse::Created()),
)), )),
), ))
); .await;
let req = TestRequest::with_uri("/app/t1/path1").to_request(); let req = TestRequest::with_uri("/app/t1/path1").to_request();
let resp = block_on(srv.call(req)).unwrap(); let resp = srv.call(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::CREATED); assert_eq!(resp.status(), StatusCode::CREATED);
})
} }
#[test] #[test]
fn test_nested_scope_no_slash() { fn test_nested_scope_no_slash() {
let mut srv = init_service( block_on(async {
App::new().service( let mut srv =
web::scope("/app") init_service(App::new().service(
.service(web::scope("t1").service( web::scope("/app").service(web::scope("t1").service(
web::resource("/path1").to(|| HttpResponse::Created()), web::resource("/path1").to(|| HttpResponse::Created()),
)), )),
), ))
); .await;
let req = TestRequest::with_uri("/app/t1/path1").to_request(); let req = TestRequest::with_uri("/app/t1/path1").to_request();
let resp = block_on(srv.call(req)).unwrap(); let resp = srv.call(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::CREATED); assert_eq!(resp.status(), StatusCode::CREATED);
})
} }
#[test] #[test]
fn test_nested_scope_root() { fn test_nested_scope_root() {
block_on(async {
let mut srv = init_service( let mut srv = init_service(
App::new().service( App::new().service(
web::scope("/app").service( web::scope("/app").service(
@ -876,19 +916,22 @@ mod tests {
.service(web::resource("/").to(|| HttpResponse::Created())), .service(web::resource("/").to(|| HttpResponse::Created())),
), ),
), ),
); )
.await;
let req = TestRequest::with_uri("/app/t1").to_request(); let req = TestRequest::with_uri("/app/t1").to_request();
let resp = block_on(srv.call(req)).unwrap(); let resp = srv.call(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
let req = TestRequest::with_uri("/app/t1/").to_request(); let req = TestRequest::with_uri("/app/t1/").to_request();
let resp = block_on(srv.call(req)).unwrap(); let resp = srv.call(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::CREATED); assert_eq!(resp.status(), StatusCode::CREATED);
})
} }
#[test] #[test]
fn test_nested_scope_filter() { fn test_nested_scope_filter() {
block_on(async {
let mut srv = init_service( let mut srv = init_service(
App::new().service( App::new().service(
web::scope("/app").service( web::scope("/app").service(
@ -897,23 +940,26 @@ mod tests {
.service(web::resource("/path1").to(|| HttpResponse::Ok())), .service(web::resource("/path1").to(|| HttpResponse::Ok())),
), ),
), ),
); )
.await;
let req = TestRequest::with_uri("/app/t1/path1") let req = TestRequest::with_uri("/app/t1/path1")
.method(Method::POST) .method(Method::POST)
.to_request(); .to_request();
let resp = block_on(srv.call(req)).unwrap(); let resp = srv.call(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::NOT_FOUND); assert_eq!(resp.status(), StatusCode::NOT_FOUND);
let req = TestRequest::with_uri("/app/t1/path1") let req = TestRequest::with_uri("/app/t1/path1")
.method(Method::GET) .method(Method::GET)
.to_request(); .to_request();
let resp = block_on(srv.call(req)).unwrap(); let resp = srv.call(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
})
} }
#[test] #[test]
fn test_nested_scope_with_variable_segment() { fn test_nested_scope_with_variable_segment() {
block_on(async {
let mut srv = init_service(App::new().service(web::scope("/app").service( let mut 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| {
@ -921,10 +967,11 @@ mod tests {
.body(format!("project: {}", &r.match_info()["project_id"])) .body(format!("project: {}", &r.match_info()["project_id"]))
}, },
)), )),
))); )))
.await;
let req = TestRequest::with_uri("/app/project_1/path1").to_request(); let req = TestRequest::with_uri("/app/project_1/path1").to_request();
let resp = block_on(srv.call(req)).unwrap(); let resp = srv.call(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::CREATED); assert_eq!(resp.status(), StatusCode::CREATED);
match resp.response().body() { match resp.response().body() {
@ -934,10 +981,12 @@ mod tests {
} }
_ => panic!(), _ => panic!(),
} }
})
} }
#[test] #[test]
fn test_nested2_scope_with_variable_segment() { fn test_nested2_scope_with_variable_segment() {
block_on(async {
let mut srv = init_service(App::new().service(web::scope("/app").service( let mut 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| {
@ -948,10 +997,11 @@ mod tests {
)) ))
}), }),
)), )),
))); )))
.await;
let req = TestRequest::with_uri("/app/test/1/path1").to_request(); let req = TestRequest::with_uri("/app/test/1/path1").to_request();
let resp = block_on(srv.call(req)).unwrap(); let resp = srv.call(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::CREATED); assert_eq!(resp.status(), StatusCode::CREATED);
match resp.response().body() { match resp.response().body() {
@ -963,33 +1013,38 @@ mod tests {
} }
let req = TestRequest::with_uri("/app/test/1/path2").to_request(); let req = TestRequest::with_uri("/app/test/1/path2").to_request();
let resp = block_on(srv.call(req)).unwrap(); let resp = srv.call(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::NOT_FOUND); assert_eq!(resp.status(), StatusCode::NOT_FOUND);
})
} }
#[test] #[test]
fn test_default_resource() { fn test_default_resource() {
block_on(async {
let mut srv = init_service( let mut srv = init_service(
App::new().service( App::new().service(
web::scope("/app") web::scope("/app")
.service(web::resource("/path1").to(|| HttpResponse::Ok())) .service(web::resource("/path1").to(|| HttpResponse::Ok()))
.default_service(|r: ServiceRequest| { .default_service(|r: ServiceRequest| {
r.into_response(HttpResponse::BadRequest()) ok(r.into_response(HttpResponse::BadRequest()))
}), }),
), ),
); )
.await;
let req = TestRequest::with_uri("/app/path2").to_request(); let req = TestRequest::with_uri("/app/path2").to_request();
let resp = block_on(srv.call(req)).unwrap(); let resp = srv.call(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::BAD_REQUEST); assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
let req = TestRequest::with_uri("/path2").to_request(); let req = TestRequest::with_uri("/path2").to_request();
let resp = block_on(srv.call(req)).unwrap(); let resp = srv.call(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::NOT_FOUND); assert_eq!(resp.status(), StatusCode::NOT_FOUND);
})
} }
#[test] #[test]
fn test_default_resource_propagation() { fn test_default_resource_propagation() {
block_on(async {
let mut srv = init_service( let mut srv = init_service(
App::new() App::new()
.service(web::scope("/app1").default_service( .service(web::scope("/app1").default_service(
@ -997,27 +1052,29 @@ mod tests {
)) ))
.service(web::scope("/app2")) .service(web::scope("/app2"))
.default_service(|r: ServiceRequest| { .default_service(|r: ServiceRequest| {
r.into_response(HttpResponse::MethodNotAllowed()) ok(r.into_response(HttpResponse::MethodNotAllowed()))
}), }),
); )
.await;
let req = TestRequest::with_uri("/non-exist").to_request(); let req = TestRequest::with_uri("/non-exist").to_request();
let resp = block_on(srv.call(req)).unwrap(); let resp = srv.call(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED); assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED);
let req = TestRequest::with_uri("/app1/non-exist").to_request(); let req = TestRequest::with_uri("/app1/non-exist").to_request();
let resp = block_on(srv.call(req)).unwrap(); let resp = srv.call(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::BAD_REQUEST); assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
let req = TestRequest::with_uri("/app2/non-exist").to_request(); let req = TestRequest::with_uri("/app2/non-exist").to_request();
let resp = block_on(srv.call(req)).unwrap(); let resp = srv.call(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED); assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED);
})
} }
fn md<S, B>( fn md<S, B>(
req: ServiceRequest, req: ServiceRequest,
srv: &mut S, srv: &mut S,
) -> impl IntoFuture<Item = ServiceResponse<B>, Error = Error> ) -> impl Future<Output = Result<ServiceResponse<B>, Error>>
where where
S: Service< S: Service<
Request = ServiceRequest, Request = ServiceRequest,
@ -1025,56 +1082,78 @@ mod tests {
Error = Error, Error = Error,
>, >,
{ {
srv.call(req).map(|mut res| { let fut = srv.call(req);
async move {
let mut res = fut.await?;
res.headers_mut() res.headers_mut()
.insert(header::CONTENT_TYPE, HeaderValue::from_static("0001")); .insert(header::CONTENT_TYPE, HeaderValue::from_static("0001"));
res Ok(res)
}) }
} }
#[test] #[test]
fn test_middleware() { fn test_middleware() {
let mut srv = block_on(async {
init_service(App::new().service(web::scope("app").wrap(md).service( let mut srv = init_service(
web::resource("/test").route(web::get().to(|| HttpResponse::Ok())), App::new().service(
))); web::scope("app")
.wrap(DefaultHeaders::new().header(
header::CONTENT_TYPE,
HeaderValue::from_static("0001"),
))
.service(
web::resource("/test")
.route(web::get().to(|| HttpResponse::Ok())),
),
),
)
.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); let resp = call_service(&mut 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(),
HeaderValue::from_static("0001") HeaderValue::from_static("0001")
); );
})
} }
#[test] #[test]
fn test_middleware_fn() { fn test_middleware_fn() {
block_on(async {
let mut srv = init_service( let mut srv = init_service(
App::new().service( App::new().service(
web::scope("app") web::scope("app")
.wrap_fn(|req, srv| { .wrap_fn(|req, srv| {
srv.call(req).map(|mut res| { let fut = srv.call(req);
async move {
let mut res = fut.await?;
res.headers_mut().insert( res.headers_mut().insert(
header::CONTENT_TYPE, header::CONTENT_TYPE,
HeaderValue::from_static("0001"), HeaderValue::from_static("0001"),
); );
res Ok(res)
}) }
}) })
.route("/test", web::get().to(|| HttpResponse::Ok())), .route("/test", web::get().to(|| HttpResponse::Ok())),
), ),
); )
.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); let resp = call_service(&mut 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(),
HeaderValue::from_static("0001") HeaderValue::from_static("0001")
); );
})
} }
#[test] #[test]
fn test_override_data() { fn test_override_data() {
block_on(async {
let mut srv = init_service(App::new().data(1usize).service( let mut srv = init_service(App::new().data(1usize).service(
web::scope("app").data(10usize).route( web::scope("app").data(10usize).route(
"/t", "/t",
@ -1084,15 +1163,18 @@ mod tests {
HttpResponse::Ok() HttpResponse::Ok()
}), }),
), ),
)); ))
.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); let resp = call_service(&mut srv, req).await;
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
})
} }
#[test] #[test]
fn test_override_register_data() { fn test_override_register_data() {
block_on(async {
let mut srv = init_service( let mut srv = init_service(
App::new().register_data(web::Data::new(1usize)).service( App::new().register_data(web::Data::new(1usize)).service(
web::scope("app") web::scope("app")
@ -1106,41 +1188,50 @@ mod tests {
}), }),
), ),
), ),
); )
.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); let resp = call_service(&mut srv, req).await;
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
})
} }
#[test] #[test]
fn test_scope_config() { fn test_scope_config() {
block_on(async {
let mut srv = let mut srv =
init_service(App::new().service(web::scope("/app").configure(|s| { init_service(App::new().service(web::scope("/app").configure(|s| {
s.route("/path1", web::get().to(|| HttpResponse::Ok())); s.route("/path1", web::get().to(|| HttpResponse::Ok()));
}))); })))
.await;
let req = TestRequest::with_uri("/app/path1").to_request(); let req = TestRequest::with_uri("/app/path1").to_request();
let resp = block_on(srv.call(req)).unwrap(); let resp = srv.call(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
})
} }
#[test] #[test]
fn test_scope_config_2() { fn test_scope_config_2() {
block_on(async {
let mut srv = let mut srv =
init_service(App::new().service(web::scope("/app").configure(|s| { init_service(App::new().service(web::scope("/app").configure(|s| {
s.service(web::scope("/v1").configure(|s| { s.service(web::scope("/v1").configure(|s| {
s.route("/", web::get().to(|| HttpResponse::Ok())); s.route("/", web::get().to(|| HttpResponse::Ok()));
})); }));
}))); })))
.await;
let req = TestRequest::with_uri("/app/v1/").to_request(); let req = TestRequest::with_uri("/app/v1/").to_request();
let resp = block_on(srv.call(req)).unwrap(); let resp = srv.call(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
})
} }
#[test] #[test]
fn test_url_for_external() { fn test_url_for_external() {
block_on(async {
let mut srv = let mut srv =
init_service(App::new().service(web::scope("/app").configure(|s| { init_service(App::new().service(web::scope("/app").configure(|s| {
s.service(web::scope("/v1").configure(|s| { s.service(web::scope("/v1").configure(|s| {
@ -1153,22 +1244,27 @@ mod tests {
web::get().to(|req: HttpRequest| { web::get().to(|req: HttpRequest| {
HttpResponse::Ok().body(format!( HttpResponse::Ok().body(format!(
"{}", "{}",
req.url_for("youtube", &["xxxxxx"]).unwrap().as_str() req.url_for("youtube", &["xxxxxx"])
.unwrap()
.as_str()
)) ))
}), }),
); );
})); }));
}))); })))
.await;
let req = TestRequest::with_uri("/app/v1/").to_request(); let req = TestRequest::with_uri("/app/v1/").to_request();
let resp = block_on(srv.call(req)).unwrap(); let resp = srv.call(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
let body = read_body(resp); let body = read_body(resp).await;
assert_eq!(body, &b"https://youtube.com/watch/xxxxxx"[..]); assert_eq!(body, &b"https://youtube.com/watch/xxxxxx"[..]);
})
} }
#[test] #[test]
fn test_url_for_nested() { fn test_url_for_nested() {
block_on(async {
let mut srv = init_service(App::new().service(web::scope("/a").service( let mut 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| {
@ -1176,14 +1272,17 @@ mod tests {
.body(format!("{}", req.url_for("c", &["12345"]).unwrap())) .body(format!("{}", req.url_for("c", &["12345"]).unwrap()))
}), }),
)), )),
))); )))
.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); let resp = call_service(&mut srv, req).await;
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
let body = read_body(resp); let body = read_body(resp).await;
assert_eq!( assert_eq!(
body, body,
Bytes::from_static(b"http://localhost:8080/a/b/c/12345") Bytes::from_static(b"http://localhost:8080/a/b/c/12345")
); );
})
} }
} }

View file

@ -6,15 +6,15 @@ use actix_http::{body::MessageBody, Error, HttpService, KeepAlive, Request, Resp
use actix_rt::System; use actix_rt::System;
use actix_server::{Server, ServerBuilder}; use actix_server::{Server, ServerBuilder};
use actix_server_config::ServerConfig; use actix_server_config::ServerConfig;
use actix_service::{IntoNewService, NewService}; use actix_service::{IntoServiceFactory, Service, ServiceFactory};
use parking_lot::Mutex; use parking_lot::Mutex;
use net2::TcpBuilder; use net2::TcpBuilder;
#[cfg(feature = "ssl")] #[cfg(feature = "openssl")]
use openssl::ssl::{SslAcceptor, SslAcceptorBuilder}; use open_ssl::ssl::{SslAcceptor, SslAcceptorBuilder};
#[cfg(feature = "rust-tls")] #[cfg(feature = "rustls")]
use rustls::ServerConfig as RustlsServerConfig; use rust_tls::ServerConfig as RustlsServerConfig;
struct Socket { struct Socket {
scheme: &'static str, scheme: &'static str,
@ -51,12 +51,11 @@ struct Config {
pub struct HttpServer<F, I, S, B> pub struct HttpServer<F, I, S, B>
where where
F: Fn() -> I + Send + Clone + 'static, F: Fn() -> I + Send + Clone + 'static,
I: IntoNewService<S>, I: IntoServiceFactory<S>,
S: NewService<Config = ServerConfig, Request = Request>, S: ServiceFactory<Config = ServerConfig, Request = Request>,
S::Error: Into<Error>, S::Error: Into<Error>,
S::InitError: fmt::Debug, S::InitError: fmt::Debug,
S::Response: Into<Response<B>>, S::Response: Into<Response<B>>,
S::Service: 'static,
B: MessageBody, B: MessageBody,
{ {
pub(super) factory: F, pub(super) factory: F,
@ -71,12 +70,12 @@ where
impl<F, I, S, B> HttpServer<F, I, S, B> impl<F, I, S, B> HttpServer<F, I, S, B>
where where
F: Fn() -> I + Send + Clone + 'static, F: Fn() -> I + Send + Clone + 'static,
I: IntoNewService<S>, I: IntoServiceFactory<S>,
S: NewService<Config = ServerConfig, Request = Request>, S: ServiceFactory<Config = ServerConfig, Request = Request>,
S::Error: Into<Error>, S::Error: Into<Error> + 'static,
S::InitError: fmt::Debug, S::InitError: fmt::Debug,
S::Response: Into<Response<B>>, S::Response: Into<Response<B>> + 'static,
S::Service: 'static, <S::Service as Service>::Future: 'static,
B: MessageBody + 'static, B: MessageBody + 'static,
{ {
/// Create new http server with application factory /// Create new http server with application factory
@ -254,11 +253,11 @@ where
Ok(self) Ok(self)
} }
#[cfg(feature = "ssl")] #[cfg(feature = "openssl")]
/// Use listener for accepting incoming tls connection requests /// Use listener for accepting incoming tls connection requests
/// ///
/// This method sets alpn protocols to "h2" and "http/1.1" /// This method sets alpn protocols to "h2" and "http/1.1"
pub fn listen_ssl( pub fn listen_openssl(
self, self,
lst: net::TcpListener, lst: net::TcpListener,
builder: SslAcceptorBuilder, builder: SslAcceptorBuilder,
@ -266,13 +265,14 @@ where
self.listen_ssl_inner(lst, openssl_acceptor(builder)?) self.listen_ssl_inner(lst, openssl_acceptor(builder)?)
} }
#[cfg(feature = "ssl")] #[cfg(feature = "openssl")]
fn listen_ssl_inner( fn listen_ssl_inner(
mut self, mut self,
lst: net::TcpListener, lst: net::TcpListener,
acceptor: SslAcceptor, acceptor: SslAcceptor,
) -> io::Result<Self> { ) -> io::Result<Self> {
use actix_server::ssl::{OpensslAcceptor, SslError}; use actix_server::ssl::{OpensslAcceptor, SslError};
use actix_service::pipeline_factory;
let acceptor = OpensslAcceptor::new(acceptor); let acceptor = OpensslAcceptor::new(acceptor);
let factory = self.factory.clone(); let factory = self.factory.clone();
@ -288,7 +288,7 @@ where
lst, lst,
move || { move || {
let c = cfg.lock(); let c = cfg.lock();
acceptor.clone().map_err(SslError::Ssl).and_then( pipeline_factory(acceptor.clone().map_err(SslError::Ssl)).and_then(
HttpService::build() HttpService::build()
.keep_alive(c.keep_alive) .keep_alive(c.keep_alive)
.client_timeout(c.client_timeout) .client_timeout(c.client_timeout)
@ -302,7 +302,7 @@ where
Ok(self) Ok(self)
} }
#[cfg(feature = "rust-tls")] #[cfg(feature = "rustls")]
/// Use listener for accepting incoming tls connection requests /// Use listener for accepting incoming tls connection requests
/// ///
/// This method sets alpn protocols to "h2" and "http/1.1" /// This method sets alpn protocols to "h2" and "http/1.1"
@ -314,13 +314,14 @@ where
self.listen_rustls_inner(lst, config) self.listen_rustls_inner(lst, config)
} }
#[cfg(feature = "rust-tls")] #[cfg(feature = "rustls")]
fn listen_rustls_inner( fn listen_rustls_inner(
mut self, mut self,
lst: net::TcpListener, lst: net::TcpListener,
mut config: RustlsServerConfig, mut config: RustlsServerConfig,
) -> io::Result<Self> { ) -> io::Result<Self> {
use actix_server::ssl::{RustlsAcceptor, SslError}; use actix_server::ssl::{RustlsAcceptor, SslError};
use actix_service::pipeline_factory;
let protos = vec!["h2".to_string().into(), "http/1.1".to_string().into()]; let protos = vec!["h2".to_string().into(), "http/1.1".to_string().into()];
config.set_protocols(&protos); config.set_protocols(&protos);
@ -339,7 +340,7 @@ where
lst, lst,
move || { move || {
let c = cfg.lock(); let c = cfg.lock();
acceptor.clone().map_err(SslError::Ssl).and_then( pipeline_factory(acceptor.clone().map_err(SslError::Ssl)).and_then(
HttpService::build() HttpService::build()
.keep_alive(c.keep_alive) .keep_alive(c.keep_alive)
.client_timeout(c.client_timeout) .client_timeout(c.client_timeout)
@ -397,11 +398,11 @@ where
} }
} }
#[cfg(feature = "ssl")] #[cfg(feature = "openssl")]
/// Start listening for incoming tls connections. /// Start listening for incoming tls connections.
/// ///
/// This method sets alpn protocols to "h2" and "http/1.1" /// This method sets alpn protocols to "h2" and "http/1.1"
pub fn bind_ssl<A>( pub fn bind_openssl<A>(
mut self, mut self,
addr: A, addr: A,
builder: SslAcceptorBuilder, builder: SslAcceptorBuilder,
@ -419,7 +420,7 @@ where
Ok(self) Ok(self)
} }
#[cfg(feature = "rust-tls")] #[cfg(feature = "rustls")]
/// Start listening for incoming tls connections. /// Start listening for incoming tls connections.
/// ///
/// This method sets alpn protocols to "h2" and "http/1.1" /// This method sets alpn protocols to "h2" and "http/1.1"
@ -435,7 +436,7 @@ where
Ok(self) Ok(self)
} }
#[cfg(feature = "uds")] #[cfg(unix)]
/// Start listening for unix domain connections on existing listener. /// Start listening for unix domain connections on existing listener.
/// ///
/// This method is available with `uds` feature. /// This method is available with `uds` feature.
@ -466,7 +467,7 @@ where
Ok(self) Ok(self)
} }
#[cfg(feature = "uds")] #[cfg(unix)]
/// Start listening for incoming unix domain connections. /// Start listening for incoming unix domain connections.
/// ///
/// This method is available with `uds` feature. /// This method is available with `uds` feature.
@ -502,8 +503,8 @@ where
impl<F, I, S, B> HttpServer<F, I, S, B> impl<F, I, S, B> HttpServer<F, I, S, B>
where where
F: Fn() -> I + Send + Clone + 'static, F: Fn() -> I + Send + Clone + 'static,
I: IntoNewService<S>, I: IntoServiceFactory<S>,
S: NewService<Config = ServerConfig, Request = Request>, S: ServiceFactory<Config = ServerConfig, Request = Request>,
S::Error: Into<Error>, S::Error: Into<Error>,
S::InitError: fmt::Debug, S::InitError: fmt::Debug,
S::Response: Into<Response<B>>, S::Response: Into<Response<B>>,
@ -577,10 +578,10 @@ fn create_tcp_listener(
Ok(builder.listen(backlog)?) Ok(builder.listen(backlog)?)
} }
#[cfg(feature = "ssl")] #[cfg(feature = "openssl")]
/// Configure `SslAcceptorBuilder` with custom server flags. /// Configure `SslAcceptorBuilder` with custom server flags.
fn openssl_acceptor(mut builder: SslAcceptorBuilder) -> io::Result<SslAcceptor> { fn openssl_acceptor(mut builder: SslAcceptorBuilder) -> io::Result<SslAcceptor> {
use openssl::ssl::AlpnError; use open_ssl::ssl::AlpnError;
builder.set_alpn_select_callback(|_, protos| { builder.set_alpn_select_callback(|_, protos| {
const H2: &[u8] = b"\x02h2"; const H2: &[u8] = b"\x02h2";

View file

@ -9,8 +9,8 @@ use actix_http::{
ResponseHead, ResponseHead,
}; };
use actix_router::{Path, Resource, ResourceDef, Url}; use actix_router::{Path, Resource, ResourceDef, Url};
use actix_service::{IntoNewService, NewService}; use actix_service::{IntoServiceFactory, ServiceFactory};
use futures::future::{ok, FutureResult, IntoFuture}; use futures::future::{ok, Ready};
use crate::config::{AppConfig, AppService}; use crate::config::{AppConfig, AppService};
use crate::data::Data; use crate::data::Data;
@ -24,7 +24,7 @@ pub trait HttpServiceFactory {
fn register(self, config: &mut AppService); fn register(self, config: &mut AppService);
} }
pub(crate) trait ServiceFactory { pub(crate) trait AppServiceFactory {
fn register(&mut self, config: &mut AppService); fn register(&mut self, config: &mut AppService);
} }
@ -40,7 +40,7 @@ impl<T> ServiceFactoryWrapper<T> {
} }
} }
impl<T> ServiceFactory for ServiceFactoryWrapper<T> impl<T> AppServiceFactory for ServiceFactoryWrapper<T>
where where
T: HttpServiceFactory, T: HttpServiceFactory,
{ {
@ -404,16 +404,6 @@ impl<B> Into<Response<B>> for ServiceResponse<B> {
} }
} }
impl<B> IntoFuture for ServiceResponse<B> {
type Item = ServiceResponse<B>;
type Error = Error;
type Future = FutureResult<ServiceResponse<B>, Error>;
fn into_future(self) -> Self::Future {
ok(self)
}
}
impl<B: MessageBody> fmt::Debug for ServiceResponse<B> { impl<B: MessageBody> fmt::Debug for ServiceResponse<B> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let res = writeln!( let res = writeln!(
@ -459,10 +449,11 @@ impl WebService {
/// Add match guard to a web service. /// Add match guard to a web service.
/// ///
/// ```rust /// ```rust
/// use actix_web::{web, guard, dev, App, HttpResponse}; /// use futures::future::{ok, Ready};
/// use actix_web::{web, guard, dev, App, Error, HttpResponse};
/// ///
/// fn index(req: dev::ServiceRequest) -> dev::ServiceResponse { /// fn index(req: dev::ServiceRequest) -> Ready<Result<dev::ServiceResponse, Error>> {
/// req.into_response(HttpResponse::Ok().finish()) /// ok(req.into_response(HttpResponse::Ok().finish()))
/// } /// }
/// ///
/// fn main() { /// fn main() {
@ -482,8 +473,8 @@ impl WebService {
/// Set a service factory implementation and generate web service. /// Set a service factory implementation and generate web service.
pub fn finish<T, F>(self, service: F) -> impl HttpServiceFactory pub fn finish<T, F>(self, service: F) -> impl HttpServiceFactory
where where
F: IntoNewService<T>, F: IntoServiceFactory<T>,
T: NewService< T: ServiceFactory<
Config = (), Config = (),
Request = ServiceRequest, Request = ServiceRequest,
Response = ServiceResponse, Response = ServiceResponse,
@ -492,7 +483,7 @@ impl WebService {
> + 'static, > + 'static,
{ {
WebServiceImpl { WebServiceImpl {
srv: service.into_new_service(), srv: service.into_factory(),
rdef: self.rdef, rdef: self.rdef,
name: self.name, name: self.name,
guards: self.guards, guards: self.guards,
@ -509,7 +500,7 @@ struct WebServiceImpl<T> {
impl<T> HttpServiceFactory for WebServiceImpl<T> impl<T> HttpServiceFactory for WebServiceImpl<T>
where where
T: NewService< T: ServiceFactory<
Config = (), Config = (),
Request = ServiceRequest, Request = ServiceRequest,
Response = ServiceResponse, Response = ServiceResponse,
@ -539,8 +530,9 @@ where
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::test::{call_service, init_service, TestRequest}; use crate::test::{block_on, init_service, TestRequest};
use crate::{guard, http, web, App, HttpResponse}; use crate::{guard, http, web, App, HttpResponse};
use actix_service::Service;
#[test] #[test]
fn test_service_request() { fn test_service_request() {
@ -565,25 +557,33 @@ mod tests {
#[test] #[test]
fn test_service() { fn test_service() {
block_on(async {
let mut srv = init_service( let mut srv = init_service(
App::new().service(web::service("/test").name("test").finish( App::new().service(web::service("/test").name("test").finish(
|req: ServiceRequest| req.into_response(HttpResponse::Ok().finish()), |req: ServiceRequest| {
ok(req.into_response(HttpResponse::Ok().finish()))
},
)), )),
); )
.await;
let req = TestRequest::with_uri("/test").to_request(); let req = TestRequest::with_uri("/test").to_request();
let resp = call_service(&mut srv, req); 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 mut srv = init_service(App::new().service(
App::new().service(web::service("/test").guard(guard::Get()).finish( web::service("/test").guard(guard::Get()).finish(
|req: ServiceRequest| req.into_response(HttpResponse::Ok().finish()), |req: ServiceRequest| {
)), ok(req.into_response(HttpResponse::Ok().finish()))
); },
),
))
.await;
let req = TestRequest::with_uri("/test") let req = TestRequest::with_uri("/test")
.method(http::Method::PUT) .method(http::Method::PUT)
.to_request(); .to_request();
let resp = call_service(&mut srv, req); let resp = srv.call(req).await.unwrap();
assert_eq!(resp.status(), http::StatusCode::NOT_FOUND); assert_eq!(resp.status(), http::StatusCode::NOT_FOUND);
})
} }
#[test] #[test]

View file

@ -7,10 +7,10 @@ use actix_http::test::TestRequest as HttpTestRequest;
use actix_http::{cookie::Cookie, Extensions, Request}; use actix_http::{cookie::Cookie, Extensions, Request};
use actix_router::{Path, ResourceDef, Url}; use actix_router::{Path, ResourceDef, Url};
use actix_server_config::ServerConfig; use actix_server_config::ServerConfig;
use actix_service::{IntoNewService, IntoService, NewService, Service}; use actix_service::{IntoService, IntoServiceFactory, Service, ServiceFactory};
use bytes::{Bytes, BytesMut}; use bytes::{Bytes, BytesMut};
use futures::future::{ok, Future}; use futures::future::{ok, Future, FutureExt};
use futures::Stream; use futures::stream::{Stream, StreamExt};
use serde::de::DeserializeOwned; use serde::de::DeserializeOwned;
use serde::Serialize; use serde::Serialize;
use serde_json; use serde_json;
@ -39,7 +39,7 @@ pub fn default_service(
) -> impl Service<Request = ServiceRequest, Response = ServiceResponse<Body>, Error = Error> ) -> impl Service<Request = ServiceRequest, Response = ServiceResponse<Body>, Error = Error>
{ {
(move |req: ServiceRequest| { (move |req: ServiceRequest| {
req.into_response(HttpResponse::build(status_code).finish()) ok(req.into_response(HttpResponse::build(status_code).finish()))
}) })
.into_service() .into_service()
} }
@ -66,12 +66,12 @@ pub fn default_service(
/// assert_eq!(resp.status(), StatusCode::OK); /// assert_eq!(resp.status(), StatusCode::OK);
/// } /// }
/// ``` /// ```
pub fn init_service<R, S, B, E>( pub async fn init_service<R, S, B, E>(
app: R, app: R,
) -> impl Service<Request = Request, Response = ServiceResponse<B>, Error = E> ) -> impl Service<Request = Request, Response = ServiceResponse<B>, Error = E>
where where
R: IntoNewService<S>, R: IntoServiceFactory<S>,
S: NewService< S: ServiceFactory<
Config = ServerConfig, Config = ServerConfig,
Request = Request, Request = Request,
Response = ServiceResponse<B>, Response = ServiceResponse<B>,
@ -80,9 +80,8 @@ where
S::InitError: std::fmt::Debug, S::InitError: std::fmt::Debug,
{ {
let cfg = ServerConfig::new("127.0.0.1:8080".parse().unwrap()); let cfg = ServerConfig::new("127.0.0.1:8080".parse().unwrap());
let srv = app.into_new_service(); let srv = app.into_factory();
let fut = run_on(move || srv.new_service(&cfg)); srv.new_service(&cfg).await.unwrap()
block_on(fut).unwrap()
} }
/// Calls service and waits for response future completion. /// Calls service and waits for response future completion.
@ -106,12 +105,12 @@ where
/// assert_eq!(resp.status(), StatusCode::OK); /// assert_eq!(resp.status(), StatusCode::OK);
/// } /// }
/// ``` /// ```
pub fn call_service<S, R, B, E>(app: &mut S, req: R) -> S::Response pub async fn call_service<S, R, B, E>(app: &mut S, req: R) -> S::Response
where where
S: Service<Request = R, Response = ServiceResponse<B>, Error = E>, S: Service<Request = R, Response = ServiceResponse<B>, Error = E>,
E: std::fmt::Debug, E: std::fmt::Debug,
{ {
block_on(run_on(move || app.call(req))).unwrap() app.call(req).await.unwrap()
} }
/// Helper function that returns a response body of a TestRequest /// Helper function that returns a response body of a TestRequest
@ -138,22 +137,22 @@ where
/// assert_eq!(result, Bytes::from_static(b"welcome!")); /// assert_eq!(result, Bytes::from_static(b"welcome!"));
/// } /// }
/// ``` /// ```
pub fn read_response<S, B>(app: &mut S, req: Request) -> Bytes pub async fn read_response<S, B>(app: &mut S, req: Request) -> Bytes
where where
S: Service<Request = Request, Response = ServiceResponse<B>, Error = Error>, S: Service<Request = Request, Response = ServiceResponse<B>, Error = Error>,
B: MessageBody, B: MessageBody,
{ {
block_on(run_on(move || { let mut resp = app
app.call(req).and_then(|mut resp: ServiceResponse<B>| { .call(req)
resp.take_body() .await
.fold(BytesMut::new(), move |mut body, chunk| { .unwrap_or_else(|_| panic!("read_response failed at block_on unwrap"));
body.extend_from_slice(&chunk);
Ok::<_, Error>(body) let mut body = resp.take_body();
}) let mut bytes = BytesMut::new();
.map(|body: BytesMut| body.freeze()) while let Some(item) = body.next().await {
}) bytes.extend_from_slice(&item.unwrap());
})) }
.unwrap_or_else(|_| panic!("read_response failed at block_on unwrap")) bytes.freeze()
} }
/// Helper function that returns a response body of a ServiceResponse. /// Helper function that returns a response body of a ServiceResponse.
@ -181,19 +180,27 @@ where
/// assert_eq!(result, Bytes::from_static(b"welcome!")); /// assert_eq!(result, Bytes::from_static(b"welcome!"));
/// } /// }
/// ``` /// ```
pub fn read_body<B>(mut res: ServiceResponse<B>) -> Bytes pub async fn read_body<B>(mut res: ServiceResponse<B>) -> Bytes
where where
B: MessageBody, B: MessageBody,
{ {
block_on(run_on(move || { let mut body = res.take_body();
res.take_body() let mut bytes = BytesMut::new();
.fold(BytesMut::new(), move |mut body, chunk| { while let Some(item) = body.next().await {
body.extend_from_slice(&chunk); bytes.extend_from_slice(&item.unwrap());
Ok::<_, Error>(body) }
}) bytes.freeze()
.map(|body: BytesMut| body.freeze()) }
}))
.unwrap_or_else(|_| panic!("read_response failed at block_on unwrap")) pub async fn load_stream<S>(mut stream: S) -> Result<Bytes, Error>
where
S: Stream<Item = Result<Bytes, Error>> + Unpin,
{
let mut data = BytesMut::new();
while let Some(item) = stream.next().await {
data.extend_from_slice(&item?);
}
Ok(data.freeze())
} }
/// Helper function that returns a deserialized response body of a TestRequest /// Helper function that returns a deserialized response body of a TestRequest
@ -230,27 +237,16 @@ where
/// let result: Person = test::read_response_json(&mut app, req); /// let result: Person = test::read_response_json(&mut app, req);
/// } /// }
/// ``` /// ```
pub fn read_response_json<S, B, T>(app: &mut S, req: Request) -> T pub async fn read_response_json<S, B, T>(app: &mut S, req: Request) -> T
where where
S: Service<Request = Request, Response = ServiceResponse<B>, Error = Error>, S: Service<Request = Request, Response = ServiceResponse<B>, Error = Error>,
B: MessageBody, B: MessageBody,
T: DeserializeOwned, T: DeserializeOwned,
{ {
block_on(run_on(move || { let body = read_response(app, req).await;
app.call(req).and_then(|mut resp: ServiceResponse<B>| {
resp.take_body() serde_json::from_slice(&body)
.fold(BytesMut::new(), move |mut body, chunk| { .unwrap_or_else(|_| panic!("read_response_json failed during deserialization"))
body.extend_from_slice(&chunk);
Ok::<_, Error>(body)
})
.and_then(|body: BytesMut| {
ok(serde_json::from_slice(&body).unwrap_or_else(|_| {
panic!("read_response_json failed during deserialization")
}))
})
})
}))
.unwrap_or_else(|_| panic!("read_response_json failed at block_on unwrap"))
} }
/// Test `Request` builder. /// Test `Request` builder.
@ -511,6 +507,7 @@ mod tests {
#[test] #[test]
fn test_basics() { fn test_basics() {
block_on(async {
let req = TestRequest::with_hdr(header::ContentType::json()) let req = TestRequest::with_hdr(header::ContentType::json())
.version(Version::HTTP_2) .version(Version::HTTP_2)
.set(header::Date(SystemTime::now().into())) .set(header::Date(SystemTime::now().into()))
@ -529,10 +526,12 @@ mod tests {
assert!(req.app_data::<u64>().is_none()); assert!(req.app_data::<u64>().is_none());
let data = req.app_data::<u32>().unwrap(); let data = req.app_data::<u32>().unwrap();
assert_eq!(*data, 10); assert_eq!(*data, 10);
})
} }
#[test] #[test]
fn test_request_methods() { fn test_request_methods() {
block_on(async {
let mut app = init_service( let mut app = init_service(
App::new().service( App::new().service(
web::resource("/index.html") web::resource("/index.html")
@ -540,14 +539,15 @@ mod tests {
.route(web::patch().to(|| HttpResponse::Ok().body("patch!"))) .route(web::patch().to(|| HttpResponse::Ok().body("patch!")))
.route(web::delete().to(|| HttpResponse::Ok().body("delete!"))), .route(web::delete().to(|| HttpResponse::Ok().body("delete!"))),
), ),
); )
.await;
let put_req = TestRequest::put() let put_req = TestRequest::put()
.uri("/index.html") .uri("/index.html")
.header(header::CONTENT_TYPE, "application/json") .header(header::CONTENT_TYPE, "application/json")
.to_request(); .to_request();
let result = read_response(&mut app, put_req); let result = read_response(&mut 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()
@ -555,30 +555,34 @@ mod tests {
.header(header::CONTENT_TYPE, "application/json") .header(header::CONTENT_TYPE, "application/json")
.to_request(); .to_request();
let result = read_response(&mut app, patch_req); let result = read_response(&mut 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); let result = read_response(&mut app, delete_req).await;
assert_eq!(result, Bytes::from_static(b"delete!")); assert_eq!(result, Bytes::from_static(b"delete!"));
})
} }
#[test] #[test]
fn test_response() { fn test_response() {
block_on(async {
let mut app = init_service( let mut 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!"))),
), ),
); )
.await;
let req = TestRequest::post() let req = TestRequest::post()
.uri("/index.html") .uri("/index.html")
.header(header::CONTENT_TYPE, "application/json") .header(header::CONTENT_TYPE, "application/json")
.to_request(); .to_request();
let result = read_response(&mut app, req); let result = read_response(&mut app, req).await;
assert_eq!(result, Bytes::from_static(b"welcome!")); assert_eq!(result, Bytes::from_static(b"welcome!"));
})
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
@ -589,11 +593,14 @@ mod tests {
#[test] #[test]
fn test_response_json() { fn test_response_json() {
let mut app = init_service(App::new().service(web::resource("/people").route( block_on(async {
let mut app =
init_service(App::new().service(web::resource("/people").route(
web::post().to(|person: web::Json<Person>| { web::post().to(|person: web::Json<Person>| {
HttpResponse::Ok().json(person.into_inner()) HttpResponse::Ok().json(person.into_inner())
}), }),
))); )))
.await;
let payload = r#"{"id":"12345","name":"User name"}"#.as_bytes(); let payload = r#"{"id":"12345","name":"User name"}"#.as_bytes();
@ -603,17 +610,21 @@ mod tests {
.set_payload(payload) .set_payload(payload)
.to_request(); .to_request();
let result: Person = read_response_json(&mut app, req); let result: Person = read_response_json(&mut app, req).await;
assert_eq!(&result.id, "12345"); assert_eq!(&result.id, "12345");
})
} }
#[test] #[test]
fn test_request_response_form() { fn test_request_response_form() {
let mut app = init_service(App::new().service(web::resource("/people").route( block_on(async {
let mut app =
init_service(App::new().service(web::resource("/people").route(
web::post().to(|person: web::Form<Person>| { web::post().to(|person: web::Form<Person>| {
HttpResponse::Ok().json(person.into_inner()) HttpResponse::Ok().json(person.into_inner())
}), }),
))); )))
.await;
let payload = Person { let payload = Person {
id: "12345".to_string(), id: "12345".to_string(),
@ -627,18 +638,22 @@ 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); let result: Person = read_response_json(&mut 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");
})
} }
#[test] #[test]
fn test_request_response_json() { fn test_request_response_json() {
let mut app = init_service(App::new().service(web::resource("/people").route( block_on(async {
let mut app =
init_service(App::new().service(web::resource("/people").route(
web::post().to(|person: web::Json<Person>| { web::post().to(|person: web::Json<Person>| {
HttpResponse::Ok().json(person.into_inner()) HttpResponse::Ok().json(person.into_inner())
}), }),
))); )))
.await;
let payload = Person { let payload = Person {
id: "12345".to_string(), id: "12345".to_string(),
@ -652,66 +667,73 @@ 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); let result: Person = read_response_json(&mut 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");
})
} }
#[test] #[test]
fn test_async_with_block() { fn test_async_with_block() {
fn async_with_block() -> impl Future<Item = HttpResponse, Error = Error> { block_on(async {
web::block(move || Some(4).ok_or("wrong")).then(|res| match res { async fn async_with_block() -> Result<HttpResponse, Error> {
Ok(value) => HttpResponse::Ok() let res = web::block(move || Some(4usize).ok_or("wrong")).await;
match res? {
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 mut app = init_service(
App::new().service(web::resource("/index.html").to_async(async_with_block)), App::new()
); .service(web::resource("/index.html").to_async(async_with_block)),
)
.await;
let req = TestRequest::post().uri("/index.html").to_request(); let req = TestRequest::post().uri("/index.html").to_request();
let res = block_fn(|| app.call(req)).unwrap(); let res = app.call(req).await.unwrap();
assert!(res.status().is_success()); assert!(res.status().is_success());
}
#[test]
fn test_actor() {
use actix::Actor;
struct MyActor;
struct Num(usize);
impl actix::Message for Num {
type Result = usize;
}
impl actix::Actor for MyActor {
type Context = actix::Context<Self>;
}
impl actix::Handler<Num> for MyActor {
type Result = usize;
fn handle(&mut self, msg: Num, _: &mut Self::Context) -> Self::Result {
msg.0
}
}
let addr = run_on(|| MyActor.start());
let mut app = init_service(App::new().service(
web::resource("/index.html").to_async(move || {
addr.send(Num(1)).from_err().and_then(|res| {
if res == 1 {
HttpResponse::Ok()
} else {
HttpResponse::BadRequest()
}
}) })
}),
));
let req = TestRequest::post().uri("/index.html").to_request();
let res = block_fn(|| app.call(req)).unwrap();
assert!(res.status().is_success());
} }
// #[test]
// fn test_actor() {
// use actix::Actor;
// struct MyActor;
// struct Num(usize);
// impl actix::Message for Num {
// type Result = usize;
// }
// impl actix::Actor for MyActor {
// type Context = actix::Context<Self>;
// }
// impl actix::Handler<Num> for MyActor {
// type Result = usize;
// fn handle(&mut self, msg: Num, _: &mut Self::Context) -> Self::Result {
// msg.0
// }
// }
// let addr = run_on(|| MyActor.start());
// let mut app = init_service(App::new().service(
// web::resource("/index.html").to_async(move || {
// addr.send(Num(1)).from_err().and_then(|res| {
// if res == 1 {
// HttpResponse::Ok()
// } else {
// HttpResponse::BadRequest()
// }
// })
// }),
// ));
// let req = TestRequest::post().uri("/index.html").to_request();
// let res = block_fn(|| app.call(req)).unwrap();
// assert!(res.status().is_success());
// }
} }

View file

@ -1,12 +1,16 @@
//! Form extractor //! Form extractor
use std::future::Future;
use std::pin::Pin;
use std::rc::Rc; use std::rc::Rc;
use std::task::{Context, Poll};
use std::{fmt, ops}; use std::{fmt, ops};
use actix_http::{Error, HttpMessage, Payload, Response}; use actix_http::{Error, HttpMessage, Payload, Response};
use bytes::BytesMut; use bytes::BytesMut;
use encoding_rs::{Encoding, UTF_8}; use encoding_rs::{Encoding, UTF_8};
use futures::{Future, Poll, Stream}; use futures::future::{err, ok, FutureExt, LocalBoxFuture, Ready};
use futures::{Stream, StreamExt};
use serde::de::DeserializeOwned; use serde::de::DeserializeOwned;
use serde::Serialize; use serde::Serialize;
@ -110,7 +114,7 @@ where
{ {
type Config = FormConfig; type Config = FormConfig;
type Error = Error; type Error = Error;
type Future = Box<dyn Future<Item = Self, Error = Error>>; type Future = LocalBoxFuture<'static, Result<Self, Error>>;
#[inline] #[inline]
fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future { fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {
@ -120,18 +124,19 @@ where
.map(|c| (c.limit, c.ehandler.clone())) .map(|c| (c.limit, c.ehandler.clone()))
.unwrap_or((16384, None)); .unwrap_or((16384, None));
Box::new(
UrlEncoded::new(req, payload) UrlEncoded::new(req, payload)
.limit(limit) .limit(limit)
.map_err(move |e| { .map(move |res| match res {
Err(e) => {
if let Some(err) = err { if let Some(err) = err {
(*err)(e, &req2) Err((*err)(e, &req2))
} else { } else {
e.into() Err(e.into())
} }
}
Ok(item) => Ok(Form(item)),
}) })
.map(Form), .boxed_local()
)
} }
} }
@ -149,15 +154,15 @@ impl<T: fmt::Display> fmt::Display for Form<T> {
impl<T: Serialize> Responder for Form<T> { impl<T: Serialize> Responder for Form<T> {
type Error = Error; type Error = Error;
type Future = Result<Response, Error>; type Future = Ready<Result<Response, Error>>;
fn respond_to(self, _: &HttpRequest) -> Self::Future { fn respond_to(self, _: &HttpRequest) -> Self::Future {
let body = match serde_urlencoded::to_string(&self.0) { let body = match serde_urlencoded::to_string(&self.0) {
Ok(body) => body, Ok(body) => body,
Err(e) => return Err(e.into()), Err(e) => return err(e.into()),
}; };
Ok(Response::build(StatusCode::OK) ok(Response::build(StatusCode::OK)
.set(ContentType::form_url_encoded()) .set(ContentType::form_url_encoded())
.body(body)) .body(body))
} }
@ -240,7 +245,7 @@ pub struct UrlEncoded<U> {
length: Option<usize>, length: Option<usize>,
encoding: &'static Encoding, encoding: &'static Encoding,
err: Option<UrlencodedError>, err: Option<UrlencodedError>,
fut: Option<Box<dyn Future<Item = U, Error = UrlencodedError>>>, fut: Option<LocalBoxFuture<'static, Result<U, UrlencodedError>>>,
} }
impl<U> UrlEncoded<U> { impl<U> UrlEncoded<U> {
@ -301,45 +306,45 @@ impl<U> Future for UrlEncoded<U>
where where
U: DeserializeOwned + 'static, U: DeserializeOwned + 'static,
{ {
type Item = U; type Output = Result<U, UrlencodedError>;
type Error = UrlencodedError;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> { fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
if let Some(ref mut fut) = self.fut { if let Some(ref mut fut) = self.fut {
return fut.poll(); return Pin::new(fut).poll(cx);
} }
if let Some(err) = self.err.take() { if let Some(err) = self.err.take() {
return Err(err); return Poll::Ready(Err(err));
} }
// payload size // payload size
let limit = self.limit; let limit = self.limit;
if let Some(len) = self.length.take() { if let Some(len) = self.length.take() {
if len > limit { if len > limit {
return Err(UrlencodedError::Overflow { size: len, limit }); return Poll::Ready(Err(UrlencodedError::Overflow { size: len, limit }));
} }
} }
// future // future
let encoding = self.encoding; let encoding = self.encoding;
let fut = self let mut stream = self.stream.take().unwrap();
.stream
.take() self.fut = Some(
.unwrap() async move {
.from_err() let mut body = BytesMut::with_capacity(8192);
.fold(BytesMut::with_capacity(8192), move |mut body, chunk| {
while let Some(item) = stream.next().await {
let chunk = item?;
if (body.len() + chunk.len()) > limit { if (body.len() + chunk.len()) > limit {
Err(UrlencodedError::Overflow { return Err(UrlencodedError::Overflow {
size: body.len() + chunk.len(), size: body.len() + chunk.len(),
limit, limit,
}) });
} else { } else {
body.extend_from_slice(&chunk); body.extend_from_slice(&chunk);
Ok(body)
} }
}) }
.and_then(move |body| {
if encoding == UTF_8 { if encoding == UTF_8 {
serde_urlencoded::from_bytes::<U>(&body) serde_urlencoded::from_bytes::<U>(&body)
.map_err(|_| UrlencodedError::Parse) .map_err(|_| UrlencodedError::Parse)
@ -351,9 +356,10 @@ where
serde_urlencoded::from_str::<U>(&body) serde_urlencoded::from_str::<U>(&body)
.map_err(|_| UrlencodedError::Parse) .map_err(|_| UrlencodedError::Parse)
} }
}); }
self.fut = Some(Box::new(fut)); .boxed_local(),
self.poll() );
self.poll(cx)
} }
} }
@ -374,13 +380,16 @@ mod tests {
#[test] #[test]
fn test_form() { fn test_form() {
let (req, mut pl) = block_on(async {
TestRequest::with_header(CONTENT_TYPE, "application/x-www-form-urlencoded") let (req, mut pl) = TestRequest::with_header(
CONTENT_TYPE,
"application/x-www-form-urlencoded",
)
.header(CONTENT_LENGTH, "11") .header(CONTENT_LENGTH, "11")
.set_payload(Bytes::from_static(b"hello=world&counter=123")) .set_payload(Bytes::from_static(b"hello=world&counter=123"))
.to_http_parts(); .to_http_parts();
let Form(s) = block_on(Form::<Info>::from_request(&req, &mut pl)).unwrap(); let Form(s) = Form::<Info>::from_request(&req, &mut pl).await.unwrap();
assert_eq!( assert_eq!(
s, s,
Info { Info {
@ -388,6 +397,7 @@ mod tests {
counter: 123 counter: 123
} }
); );
})
} }
fn eq(err: UrlencodedError, other: UrlencodedError) -> bool { fn eq(err: UrlencodedError, other: UrlencodedError) -> bool {
@ -410,18 +420,23 @@ mod tests {
#[test] #[test]
fn test_urlencoded_error() { fn test_urlencoded_error() {
let (req, mut pl) = block_on(async {
TestRequest::with_header(CONTENT_TYPE, "application/x-www-form-urlencoded") let (req, mut pl) = TestRequest::with_header(
CONTENT_TYPE,
"application/x-www-form-urlencoded",
)
.header(CONTENT_LENGTH, "xxxx") .header(CONTENT_LENGTH, "xxxx")
.to_http_parts(); .to_http_parts();
let info = block_on(UrlEncoded::<Info>::new(&req, &mut pl)); let info = UrlEncoded::<Info>::new(&req, &mut pl).await;
assert!(eq(info.err().unwrap(), UrlencodedError::UnknownLength)); assert!(eq(info.err().unwrap(), UrlencodedError::UnknownLength));
let (req, mut pl) = let (req, mut pl) = TestRequest::with_header(
TestRequest::with_header(CONTENT_TYPE, "application/x-www-form-urlencoded") CONTENT_TYPE,
"application/x-www-form-urlencoded",
)
.header(CONTENT_LENGTH, "1000000") .header(CONTENT_LENGTH, "1000000")
.to_http_parts(); .to_http_parts();
let info = block_on(UrlEncoded::<Info>::new(&req, &mut pl)); let info = UrlEncoded::<Info>::new(&req, &mut pl).await;
assert!(eq( assert!(eq(
info.err().unwrap(), info.err().unwrap(),
UrlencodedError::Overflow { size: 0, limit: 0 } UrlencodedError::Overflow { size: 0, limit: 0 }
@ -430,19 +445,23 @@ mod tests {
let (req, mut pl) = TestRequest::with_header(CONTENT_TYPE, "text/plain") let (req, mut pl) = TestRequest::with_header(CONTENT_TYPE, "text/plain")
.header(CONTENT_LENGTH, "10") .header(CONTENT_LENGTH, "10")
.to_http_parts(); .to_http_parts();
let info = block_on(UrlEncoded::<Info>::new(&req, &mut pl)); let info = UrlEncoded::<Info>::new(&req, &mut pl).await;
assert!(eq(info.err().unwrap(), UrlencodedError::ContentType)); assert!(eq(info.err().unwrap(), UrlencodedError::ContentType));
})
} }
#[test] #[test]
fn test_urlencoded() { fn test_urlencoded() {
let (req, mut pl) = block_on(async {
TestRequest::with_header(CONTENT_TYPE, "application/x-www-form-urlencoded") let (req, mut pl) = TestRequest::with_header(
CONTENT_TYPE,
"application/x-www-form-urlencoded",
)
.header(CONTENT_LENGTH, "11") .header(CONTENT_LENGTH, "11")
.set_payload(Bytes::from_static(b"hello=world&counter=123")) .set_payload(Bytes::from_static(b"hello=world&counter=123"))
.to_http_parts(); .to_http_parts();
let info = block_on(UrlEncoded::<Info>::new(&req, &mut pl)).unwrap(); let info = UrlEncoded::<Info>::new(&req, &mut pl).await.unwrap();
assert_eq!( assert_eq!(
info, info,
Info { Info {
@ -459,7 +478,7 @@ mod tests {
.set_payload(Bytes::from_static(b"hello=world&counter=123")) .set_payload(Bytes::from_static(b"hello=world&counter=123"))
.to_http_parts(); .to_http_parts();
let info = block_on(UrlEncoded::<Info>::new(&req, &mut pl)).unwrap(); let info = UrlEncoded::<Info>::new(&req, &mut pl).await.unwrap();
assert_eq!( assert_eq!(
info, info,
Info { Info {
@ -467,17 +486,19 @@ mod tests {
counter: 123 counter: 123
} }
); );
})
} }
#[test] #[test]
fn test_responder() { fn test_responder() {
block_on(async {
let req = TestRequest::default().to_http_request(); let req = TestRequest::default().to_http_request();
let form = Form(Info { let form = Form(Info {
hello: "world".to_string(), hello: "world".to_string(),
counter: 123, counter: 123,
}); });
let resp = form.respond_to(&req).unwrap(); let resp = form.respond_to(&req).await.unwrap();
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
assert_eq!( assert_eq!(
resp.headers().get(CONTENT_TYPE).unwrap(), resp.headers().get(CONTENT_TYPE).unwrap(),
@ -486,5 +507,6 @@ mod tests {
use crate::responder::tests::BodyTest; use crate::responder::tests::BodyTest;
assert_eq!(resp.body().bin_ref(), b"hello=world&counter=123"); assert_eq!(resp.body().bin_ref(), b"hello=world&counter=123");
})
} }
} }

View file

@ -1,10 +1,14 @@
//! Json extractor/responder //! Json extractor/responder
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc; use std::sync::Arc;
use std::task::{Context, Poll};
use std::{fmt, ops}; use std::{fmt, ops};
use bytes::BytesMut; use bytes::BytesMut;
use futures::{Future, Poll, Stream}; use futures::future::{err, ok, FutureExt, LocalBoxFuture, Ready};
use futures::{Stream, StreamExt};
use serde::de::DeserializeOwned; use serde::de::DeserializeOwned;
use serde::Serialize; use serde::Serialize;
use serde_json; use serde_json;
@ -118,15 +122,15 @@ where
impl<T: Serialize> Responder for Json<T> { impl<T: Serialize> Responder for Json<T> {
type Error = Error; type Error = Error;
type Future = Result<Response, Error>; type Future = Ready<Result<Response, Error>>;
fn respond_to(self, _: &HttpRequest) -> Self::Future { fn respond_to(self, _: &HttpRequest) -> Self::Future {
let body = match serde_json::to_string(&self.0) { let body = match serde_json::to_string(&self.0) {
Ok(body) => body, Ok(body) => body,
Err(e) => return Err(e.into()), Err(e) => return err(e.into()),
}; };
Ok(Response::build(StatusCode::OK) ok(Response::build(StatusCode::OK)
.content_type("application/json") .content_type("application/json")
.body(body)) .body(body))
} }
@ -169,7 +173,7 @@ where
T: DeserializeOwned + 'static, T: DeserializeOwned + 'static,
{ {
type Error = Error; type Error = Error;
type Future = Box<dyn Future<Item = Self, Error = Error>>; type Future = LocalBoxFuture<'static, Result<Self, Error>>;
type Config = JsonConfig; type Config = JsonConfig;
#[inline] #[inline]
@ -180,23 +184,24 @@ where
.map(|c| (c.limit, c.ehandler.clone(), c.content_type.clone())) .map(|c| (c.limit, c.ehandler.clone(), c.content_type.clone()))
.unwrap_or((32768, None, None)); .unwrap_or((32768, None, None));
Box::new(
JsonBody::new(req, payload, ctype) JsonBody::new(req, payload, ctype)
.limit(limit) .limit(limit)
.map_err(move |e| { .map(move |res| match res {
Err(e) => {
log::debug!( log::debug!(
"Failed to deserialize Json from payload. \ "Failed to deserialize Json from payload. \
Request path: {}", Request path: {}",
req2.path() req2.path()
); );
if let Some(err) = err { if let Some(err) = err {
(*err)(e, &req2) Err((*err)(e, &req2))
} else { } else {
e.into() Err(e.into())
} }
}
Ok(data) => Ok(Json(data)),
}) })
.map(Json), .boxed_local()
)
} }
} }
@ -290,7 +295,7 @@ pub struct JsonBody<U> {
length: Option<usize>, length: Option<usize>,
stream: Option<Decompress<Payload>>, stream: Option<Decompress<Payload>>,
err: Option<JsonPayloadError>, err: Option<JsonPayloadError>,
fut: Option<Box<dyn Future<Item = U, Error = JsonPayloadError>>>, fut: Option<LocalBoxFuture<'static, Result<U, JsonPayloadError>>>,
} }
impl<U> JsonBody<U> impl<U> JsonBody<U>
@ -349,41 +354,43 @@ impl<U> Future for JsonBody<U>
where where
U: DeserializeOwned + 'static, U: DeserializeOwned + 'static,
{ {
type Item = U; type Output = Result<U, JsonPayloadError>;
type Error = JsonPayloadError;
fn poll(&mut self) -> Poll<U, JsonPayloadError> { fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
if let Some(ref mut fut) = self.fut { if let Some(ref mut fut) = self.fut {
return fut.poll(); return Pin::new(fut).poll(cx);
} }
if let Some(err) = self.err.take() { if let Some(err) = self.err.take() {
return Err(err); return Poll::Ready(Err(err));
} }
let limit = self.limit; let limit = self.limit;
if let Some(len) = self.length.take() { if let Some(len) = self.length.take() {
if len > limit { if len > limit {
return Err(JsonPayloadError::Overflow); return Poll::Ready(Err(JsonPayloadError::Overflow));
} }
} }
let mut stream = self.stream.take().unwrap();
let fut = self self.fut = Some(
.stream async move {
.take() let mut body = BytesMut::with_capacity(8192);
.unwrap()
.from_err() while let Some(item) = stream.next().await {
.fold(BytesMut::with_capacity(8192), move |mut body, chunk| { let chunk = item?;
if (body.len() + chunk.len()) > limit { if (body.len() + chunk.len()) > limit {
Err(JsonPayloadError::Overflow) return Err(JsonPayloadError::Overflow);
} else { } else {
body.extend_from_slice(&chunk); body.extend_from_slice(&chunk);
Ok(body)
} }
}) }
.and_then(|body| Ok(serde_json::from_slice::<U>(&body)?)); Ok(serde_json::from_slice::<U>(&body)?)
self.fut = Some(Box::new(fut)); }
self.poll() .boxed_local(),
);
self.poll(cx)
} }
} }
@ -395,7 +402,7 @@ mod tests {
use super::*; use super::*;
use crate::error::InternalError; use crate::error::InternalError;
use crate::http::header; use crate::http::header;
use crate::test::{block_on, TestRequest}; use crate::test::{block_on, load_stream, TestRequest};
use crate::HttpResponse; use crate::HttpResponse;
#[derive(Serialize, Deserialize, PartialEq, Debug)] #[derive(Serialize, Deserialize, PartialEq, Debug)]
@ -419,12 +426,13 @@ mod tests {
#[test] #[test]
fn test_responder() { fn test_responder() {
block_on(async {
let req = TestRequest::default().to_http_request(); let req = TestRequest::default().to_http_request();
let j = Json(MyObject { let j = Json(MyObject {
name: "test".to_string(), name: "test".to_string(),
}); });
let resp = j.respond_to(&req).unwrap(); let resp = j.respond_to(&req).await.unwrap();
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(),
@ -433,10 +441,12 @@ mod tests {
use crate::responder::tests::BodyTest; use crate::responder::tests::BodyTest;
assert_eq!(resp.body().bin_ref(), b"{\"name\":\"test\"}"); assert_eq!(resp.body().bin_ref(), b"{\"name\":\"test\"}");
})
} }
#[test] #[test]
fn test_custom_error_responder() { fn test_custom_error_responder() {
block_on(async {
let (req, mut pl) = TestRequest::default() let (req, mut pl) = TestRequest::default()
.header( .header(
header::CONTENT_TYPE, header::CONTENT_TYPE,
@ -457,17 +467,19 @@ mod tests {
})) }))
.to_http_parts(); .to_http_parts();
let s = block_on(Json::<MyObject>::from_request(&req, &mut pl)); let s = Json::<MyObject>::from_request(&req, &mut pl).await;
let mut resp = Response::from_error(s.err().unwrap().into()); let mut resp = Response::from_error(s.err().unwrap().into());
assert_eq!(resp.status(), StatusCode::BAD_REQUEST); assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
let body = block_on(resp.take_body().concat2()).unwrap(); let body = load_stream(resp.take_body()).await.unwrap();
let msg: MyObject = serde_json::from_slice(&body).unwrap(); let msg: MyObject = serde_json::from_slice(&body).unwrap();
assert_eq!(msg.name, "invalid request"); assert_eq!(msg.name, "invalid request");
})
} }
#[test] #[test]
fn test_extract() { fn test_extract() {
block_on(async {
let (req, mut pl) = TestRequest::default() let (req, mut pl) = TestRequest::default()
.header( .header(
header::CONTENT_TYPE, header::CONTENT_TYPE,
@ -480,7 +492,7 @@ mod tests {
.set_payload(Bytes::from_static(b"{\"name\": \"test\"}")) .set_payload(Bytes::from_static(b"{\"name\": \"test\"}"))
.to_http_parts(); .to_http_parts();
let s = block_on(Json::<MyObject>::from_request(&req, &mut pl)).unwrap(); let s = Json::<MyObject>::from_request(&req, &mut pl).await.unwrap();
assert_eq!(s.name, "test"); assert_eq!(s.name, "test");
assert_eq!( assert_eq!(
s.into_inner(), s.into_inner(),
@ -502,7 +514,7 @@ mod tests {
.data(JsonConfig::default().limit(10)) .data(JsonConfig::default().limit(10))
.to_http_parts(); .to_http_parts();
let s = block_on(Json::<MyObject>::from_request(&req, &mut pl)); let s = Json::<MyObject>::from_request(&req, &mut pl).await;
assert!(format!("{}", s.err().unwrap()) assert!(format!("{}", s.err().unwrap())
.contains("Json payload size is bigger than allowed")); .contains("Json payload size is bigger than allowed"));
@ -522,14 +534,16 @@ mod tests {
.error_handler(|_, _| JsonPayloadError::ContentType.into()), .error_handler(|_, _| JsonPayloadError::ContentType.into()),
) )
.to_http_parts(); .to_http_parts();
let s = block_on(Json::<MyObject>::from_request(&req, &mut pl)); let s = Json::<MyObject>::from_request(&req, &mut pl).await;
assert!(format!("{}", s.err().unwrap()).contains("Content type error")); assert!(format!("{}", s.err().unwrap()).contains("Content type error"));
})
} }
#[test] #[test]
fn test_json_body() { fn test_json_body() {
block_on(async {
let (req, mut pl) = TestRequest::default().to_http_parts(); let (req, mut pl) = TestRequest::default().to_http_parts();
let json = block_on(JsonBody::<MyObject>::new(&req, &mut pl, None)); let json = JsonBody::<MyObject>::new(&req, &mut pl, None).await;
assert!(json_eq(json.err().unwrap(), JsonPayloadError::ContentType)); assert!(json_eq(json.err().unwrap(), JsonPayloadError::ContentType));
let (req, mut pl) = TestRequest::default() let (req, mut pl) = TestRequest::default()
@ -538,7 +552,7 @@ mod tests {
header::HeaderValue::from_static("application/text"), header::HeaderValue::from_static("application/text"),
) )
.to_http_parts(); .to_http_parts();
let json = block_on(JsonBody::<MyObject>::new(&req, &mut pl, None)); let json = JsonBody::<MyObject>::new(&req, &mut pl, None).await;
assert!(json_eq(json.err().unwrap(), JsonPayloadError::ContentType)); assert!(json_eq(json.err().unwrap(), JsonPayloadError::ContentType));
let (req, mut pl) = TestRequest::default() let (req, mut pl) = TestRequest::default()
@ -552,7 +566,9 @@ mod tests {
) )
.to_http_parts(); .to_http_parts();
let json = block_on(JsonBody::<MyObject>::new(&req, &mut pl, None).limit(100)); let json = JsonBody::<MyObject>::new(&req, &mut pl, None)
.limit(100)
.await;
assert!(json_eq(json.err().unwrap(), JsonPayloadError::Overflow)); assert!(json_eq(json.err().unwrap(), JsonPayloadError::Overflow));
let (req, mut pl) = TestRequest::default() let (req, mut pl) = TestRequest::default()
@ -567,17 +583,19 @@ mod tests {
.set_payload(Bytes::from_static(b"{\"name\": \"test\"}")) .set_payload(Bytes::from_static(b"{\"name\": \"test\"}"))
.to_http_parts(); .to_http_parts();
let json = block_on(JsonBody::<MyObject>::new(&req, &mut pl, None)); let json = JsonBody::<MyObject>::new(&req, &mut pl, None).await;
assert_eq!( assert_eq!(
json.ok().unwrap(), json.ok().unwrap(),
MyObject { MyObject {
name: "test".to_owned() name: "test".to_owned()
} }
); );
})
} }
#[test] #[test]
fn test_with_json_and_bad_content_type() { fn test_with_json_and_bad_content_type() {
block_on(async {
let (req, mut pl) = TestRequest::with_header( let (req, mut pl) = TestRequest::with_header(
header::CONTENT_TYPE, header::CONTENT_TYPE,
header::HeaderValue::from_static("text/plain"), header::HeaderValue::from_static("text/plain"),
@ -590,12 +608,14 @@ mod tests {
.data(JsonConfig::default().limit(4096)) .data(JsonConfig::default().limit(4096))
.to_http_parts(); .to_http_parts();
let s = block_on(Json::<MyObject>::from_request(&req, &mut pl)); let s = Json::<MyObject>::from_request(&req, &mut pl).await;
assert!(s.is_err()) assert!(s.is_err())
})
} }
#[test] #[test]
fn test_with_json_and_good_custom_content_type() { fn test_with_json_and_good_custom_content_type() {
block_on(async {
let (req, mut pl) = TestRequest::with_header( let (req, mut pl) = TestRequest::with_header(
header::CONTENT_TYPE, header::CONTENT_TYPE,
header::HeaderValue::from_static("text/plain"), header::HeaderValue::from_static("text/plain"),
@ -610,12 +630,14 @@ mod tests {
})) }))
.to_http_parts(); .to_http_parts();
let s = block_on(Json::<MyObject>::from_request(&req, &mut pl)); let s = Json::<MyObject>::from_request(&req, &mut pl).await;
assert!(s.is_ok()) assert!(s.is_ok())
})
} }
#[test] #[test]
fn test_with_json_and_bad_custom_content_type() { fn test_with_json_and_bad_custom_content_type() {
block_on(async {
let (req, mut pl) = TestRequest::with_header( let (req, mut pl) = TestRequest::with_header(
header::CONTENT_TYPE, header::CONTENT_TYPE,
header::HeaderValue::from_static("text/html"), header::HeaderValue::from_static("text/html"),
@ -630,7 +652,8 @@ mod tests {
})) }))
.to_http_parts(); .to_http_parts();
let s = block_on(Json::<MyObject>::from_request(&req, &mut pl)); let s = Json::<MyObject>::from_request(&req, &mut pl).await;
assert!(s.is_err()) assert!(s.is_err())
})
} }
} }

View file

@ -5,6 +5,7 @@ use std::{fmt, ops};
use actix_http::error::{Error, ErrorNotFound}; use actix_http::error::{Error, ErrorNotFound};
use actix_router::PathDeserializer; use actix_router::PathDeserializer;
use futures::future::{ready, Ready};
use serde::de; use serde::de;
use crate::dev::Payload; use crate::dev::Payload;
@ -159,7 +160,7 @@ where
T: de::DeserializeOwned, T: de::DeserializeOwned,
{ {
type Error = Error; type Error = Error;
type Future = Result<Self, Error>; type Future = Ready<Result<Self, Error>>;
type Config = PathConfig; type Config = PathConfig;
#[inline] #[inline]
@ -169,6 +170,7 @@ where
.map(|c| c.ehandler.clone()) .map(|c| c.ehandler.clone())
.unwrap_or(None); .unwrap_or(None);
ready(
de::Deserialize::deserialize(PathDeserializer::new(req.match_info())) de::Deserialize::deserialize(PathDeserializer::new(req.match_info()))
.map(|inner| Path { inner }) .map(|inner| Path { inner })
.map_err(move |e| { .map_err(move |e| {
@ -183,7 +185,8 @@ where
} else { } else {
ErrorNotFound(e) ErrorNotFound(e)
} }
}) }),
)
} }
} }
@ -268,52 +271,57 @@ mod tests {
#[test] #[test]
fn test_extract_path_single() { fn test_extract_path_single() {
block_on(async {
let resource = ResourceDef::new("/{value}/"); let resource = ResourceDef::new("/{value}/");
let mut req = TestRequest::with_uri("/32/").to_srv_request(); let mut req = TestRequest::with_uri("/32/").to_srv_request();
resource.match_path(req.match_info_mut()); resource.match_path(req.match_info_mut());
let (req, mut pl) = req.into_parts(); let (req, mut pl) = req.into_parts();
assert_eq!(*Path::<i8>::from_request(&req, &mut pl).unwrap(), 32); assert_eq!(*Path::<i8>::from_request(&req, &mut pl).await.unwrap(), 32);
assert!(Path::<MyStruct>::from_request(&req, &mut pl).is_err()); assert!(Path::<MyStruct>::from_request(&req, &mut pl).await.is_err());
})
} }
#[test] #[test]
fn test_tuple_extract() { fn test_tuple_extract() {
block_on(async {
let resource = ResourceDef::new("/{key}/{value}/"); let resource = ResourceDef::new("/{key}/{value}/");
let mut req = TestRequest::with_uri("/name/user1/?id=test").to_srv_request(); let mut req = TestRequest::with_uri("/name/user1/?id=test").to_srv_request();
resource.match_path(req.match_info_mut()); resource.match_path(req.match_info_mut());
let (req, mut pl) = req.into_parts(); let (req, mut pl) = req.into_parts();
let res = let res = <(Path<(String, String)>,)>::from_request(&req, &mut pl)
block_on(<(Path<(String, String)>,)>::from_request(&req, &mut pl)).unwrap(); .await
.unwrap();
assert_eq!((res.0).0, "name"); assert_eq!((res.0).0, "name");
assert_eq!((res.0).1, "user1"); assert_eq!((res.0).1, "user1");
let res = block_on( let res = <(Path<(String, String)>, Path<(String, String)>)>::from_request(
<(Path<(String, String)>, Path<(String, String)>)>::from_request(
&req, &mut pl, &req, &mut pl,
),
) )
.await
.unwrap(); .unwrap();
assert_eq!((res.0).0, "name"); assert_eq!((res.0).0, "name");
assert_eq!((res.0).1, "user1"); assert_eq!((res.0).1, "user1");
assert_eq!((res.1).0, "name"); assert_eq!((res.1).0, "name");
assert_eq!((res.1).1, "user1"); assert_eq!((res.1).1, "user1");
let () = <()>::from_request(&req, &mut pl).unwrap(); let () = <()>::from_request(&req, &mut pl).await.unwrap();
})
} }
#[test] #[test]
fn test_request_extract() { fn test_request_extract() {
block_on(async {
let mut req = TestRequest::with_uri("/name/user1/?id=test").to_srv_request(); let mut req = TestRequest::with_uri("/name/user1/?id=test").to_srv_request();
let resource = ResourceDef::new("/{key}/{value}/"); let resource = ResourceDef::new("/{key}/{value}/");
resource.match_path(req.match_info_mut()); resource.match_path(req.match_info_mut());
let (req, mut pl) = req.into_parts(); let (req, mut pl) = req.into_parts();
let mut s = Path::<MyStruct>::from_request(&req, &mut pl).unwrap(); let mut s = Path::<MyStruct>::from_request(&req, &mut pl).await.unwrap();
assert_eq!(s.key, "name"); assert_eq!(s.key, "name");
assert_eq!(s.value, "user1"); assert_eq!(s.value, "user1");
s.value = "user2".to_string(); s.value = "user2".to_string();
@ -325,7 +333,9 @@ mod tests {
let s = s.into_inner(); let s = s.into_inner();
assert_eq!(s.value, "user2"); assert_eq!(s.value, "user2");
let s = Path::<(String, String)>::from_request(&req, &mut pl).unwrap(); let s = Path::<(String, String)>::from_request(&req, &mut pl)
.await
.unwrap();
assert_eq!(s.0, "name"); assert_eq!(s.0, "name");
assert_eq!(s.1, "user1"); assert_eq!(s.1, "user1");
@ -334,21 +344,27 @@ mod tests {
resource.match_path(req.match_info_mut()); resource.match_path(req.match_info_mut());
let (req, mut pl) = req.into_parts(); let (req, mut pl) = req.into_parts();
let s = Path::<Test2>::from_request(&req, &mut pl).unwrap(); let s = Path::<Test2>::from_request(&req, &mut pl).await.unwrap();
assert_eq!(s.as_ref().key, "name"); assert_eq!(s.as_ref().key, "name");
assert_eq!(s.value, 32); assert_eq!(s.value, 32);
let s = Path::<(String, u8)>::from_request(&req, &mut pl).unwrap(); let s = Path::<(String, u8)>::from_request(&req, &mut pl)
.await
.unwrap();
assert_eq!(s.0, "name"); assert_eq!(s.0, "name");
assert_eq!(s.1, 32); assert_eq!(s.1, 32);
let res = Path::<Vec<String>>::from_request(&req, &mut pl).unwrap(); let res = Path::<Vec<String>>::from_request(&req, &mut pl)
.await
.unwrap();
assert_eq!(res[0], "name".to_owned()); assert_eq!(res[0], "name".to_owned());
assert_eq!(res[1], "32".to_owned()); assert_eq!(res[1], "32".to_owned());
})
} }
#[test] #[test]
fn test_custom_err_handler() { fn test_custom_err_handler() {
block_on(async {
let (req, mut pl) = TestRequest::with_uri("/name/user1/") let (req, mut pl) = TestRequest::with_uri("/name/user1/")
.data(PathConfig::default().error_handler(|err, _| { .data(PathConfig::default().error_handler(|err, _| {
error::InternalError::from_response( error::InternalError::from_response(
@ -359,9 +375,12 @@ mod tests {
})) }))
.to_http_parts(); .to_http_parts();
let s = block_on(Path::<(usize,)>::from_request(&req, &mut pl)).unwrap_err(); let s = Path::<(usize,)>::from_request(&req, &mut pl)
.await
.unwrap_err();
let res: HttpResponse = s.into(); let res: HttpResponse = s.into();
assert_eq!(res.status(), http::StatusCode::CONFLICT); assert_eq!(res.status(), http::StatusCode::CONFLICT);
})
} }
} }

View file

@ -1,12 +1,15 @@
//! Payload/Bytes/String extractors //! Payload/Bytes/String extractors
use std::future::Future;
use std::pin::Pin;
use std::str; use std::str;
use std::task::{Context, Poll};
use actix_http::error::{Error, ErrorBadRequest, PayloadError}; use actix_http::error::{Error, ErrorBadRequest, PayloadError};
use actix_http::HttpMessage; use actix_http::HttpMessage;
use bytes::{Bytes, BytesMut}; use bytes::{Bytes, BytesMut};
use encoding_rs::UTF_8; use encoding_rs::UTF_8;
use futures::future::{err, Either, FutureResult}; use futures::future::{err, ok, Either, FutureExt, LocalBoxFuture, Ready};
use futures::{Future, Poll, Stream}; use futures::{Stream, StreamExt};
use mime::Mime; use mime::Mime;
use crate::dev; use crate::dev;
@ -19,21 +22,19 @@ use crate::request::HttpRequest;
/// ## Example /// ## Example
/// ///
/// ```rust /// ```rust
/// use futures::{Future, Stream}; /// use futures::{Future, Stream, StreamExt};
/// use actix_web::{web, error, App, Error, HttpResponse}; /// use actix_web::{web, error, App, Error, HttpResponse};
/// ///
/// /// extract binary data from request /// /// extract binary data from request
/// fn index(body: web::Payload) -> impl Future<Item = HttpResponse, Error = Error> /// async fn index(mut body: web::Payload) -> Result<HttpResponse, Error>
/// { /// {
/// body.map_err(Error::from) /// let mut bytes = web::BytesMut::new();
/// .fold(web::BytesMut::new(), move |mut body, chunk| { /// while let Some(item) = body.next().await {
/// body.extend_from_slice(&chunk); /// bytes.extend_from_slice(&item?);
/// Ok::<_, Error>(body) /// }
/// }) ///
/// .and_then(|body| { /// format!("Body {:?}!", bytes);
/// format!("Body {:?}!", body);
/// Ok(HttpResponse::Ok().finish()) /// Ok(HttpResponse::Ok().finish())
/// })
/// } /// }
/// ///
/// fn main() { /// fn main() {
@ -53,12 +54,14 @@ impl Payload {
} }
impl Stream for Payload { impl Stream for Payload {
type Item = Bytes; type Item = Result<Bytes, PayloadError>;
type Error = PayloadError;
#[inline] #[inline]
fn poll(&mut self) -> Poll<Option<Self::Item>, PayloadError> { fn poll_next(
self.0.poll() mut self: Pin<&mut Self>,
cx: &mut Context,
) -> Poll<Option<Self::Item>> {
Pin::new(&mut self.0).poll_next(cx)
} }
} }
@ -67,21 +70,19 @@ impl Stream for Payload {
/// ## Example /// ## Example
/// ///
/// ```rust /// ```rust
/// use futures::{Future, Stream}; /// use futures::{Future, Stream, StreamExt};
/// use actix_web::{web, error, App, Error, HttpResponse}; /// use actix_web::{web, error, App, Error, HttpResponse};
/// ///
/// /// extract binary data from request /// /// extract binary data from request
/// fn index(body: web::Payload) -> impl Future<Item = HttpResponse, Error = Error> /// async fn index(mut body: web::Payload) -> Result<HttpResponse, Error>
/// { /// {
/// body.map_err(Error::from) /// let mut bytes = web::BytesMut::new();
/// .fold(web::BytesMut::new(), move |mut body, chunk| { /// while let Some(item) = body.next().await {
/// body.extend_from_slice(&chunk); /// bytes.extend_from_slice(&item?);
/// Ok::<_, Error>(body) /// }
/// }) ///
/// .and_then(|body| { /// format!("Body {:?}!", bytes);
/// format!("Body {:?}!", body);
/// Ok(HttpResponse::Ok().finish()) /// Ok(HttpResponse::Ok().finish())
/// })
/// } /// }
/// ///
/// fn main() { /// fn main() {
@ -94,11 +95,11 @@ impl Stream for Payload {
impl FromRequest for Payload { impl FromRequest for Payload {
type Config = PayloadConfig; type Config = PayloadConfig;
type Error = Error; type Error = Error;
type Future = Result<Payload, Error>; type Future = Ready<Result<Payload, Error>>;
#[inline] #[inline]
fn from_request(_: &HttpRequest, payload: &mut dev::Payload) -> Self::Future { fn from_request(_: &HttpRequest, payload: &mut dev::Payload) -> Self::Future {
Ok(Payload(payload.take())) ok(Payload(payload.take()))
} }
} }
@ -130,8 +131,10 @@ impl FromRequest for Payload {
impl FromRequest for Bytes { impl FromRequest for Bytes {
type Config = PayloadConfig; type Config = PayloadConfig;
type Error = Error; type Error = Error;
type Future = type Future = Either<
Either<Box<dyn Future<Item = Bytes, Error = Error>>, FutureResult<Bytes, Error>>; LocalBoxFuture<'static, Result<Bytes, Error>>,
Ready<Result<Bytes, Error>>,
>;
#[inline] #[inline]
fn from_request(req: &HttpRequest, payload: &mut dev::Payload) -> Self::Future { fn from_request(req: &HttpRequest, payload: &mut dev::Payload) -> Self::Future {
@ -144,13 +147,12 @@ impl FromRequest for Bytes {
}; };
if let Err(e) = cfg.check_mimetype(req) { if let Err(e) = cfg.check_mimetype(req) {
return Either::B(err(e)); return Either::Right(err(e));
} }
let limit = cfg.limit; let limit = cfg.limit;
Either::A(Box::new( let fut = HttpMessageBody::new(req, payload).limit(limit);
HttpMessageBody::new(req, payload).limit(limit).from_err(), Either::Left(async move { Ok(fut.await?) }.boxed_local())
))
} }
} }
@ -185,8 +187,8 @@ impl FromRequest for String {
type Config = PayloadConfig; type Config = PayloadConfig;
type Error = Error; type Error = Error;
type Future = Either< type Future = Either<
Box<dyn Future<Item = String, Error = Error>>, LocalBoxFuture<'static, Result<String, Error>>,
FutureResult<String, Error>, Ready<Result<String, Error>>,
>; >;
#[inline] #[inline]
@ -201,21 +203,21 @@ impl FromRequest for String {
// check content-type // check content-type
if let Err(e) = cfg.check_mimetype(req) { if let Err(e) = cfg.check_mimetype(req) {
return Either::B(err(e)); return Either::Right(err(e));
} }
// check charset // check charset
let encoding = match req.encoding() { let encoding = match req.encoding() {
Ok(enc) => enc, Ok(enc) => enc,
Err(e) => return Either::B(err(e.into())), Err(e) => return Either::Right(err(e.into())),
}; };
let limit = cfg.limit; let limit = cfg.limit;
let fut = HttpMessageBody::new(req, payload).limit(limit);
Either::Left(
async move {
let body = fut.await?;
Either::A(Box::new(
HttpMessageBody::new(req, payload)
.limit(limit)
.from_err()
.and_then(move |body| {
if encoding == UTF_8 { if encoding == UTF_8 {
Ok(str::from_utf8(body.as_ref()) Ok(str::from_utf8(body.as_ref())
.map_err(|_| ErrorBadRequest("Can not decode body"))? .map_err(|_| ErrorBadRequest("Can not decode body"))?
@ -226,8 +228,9 @@ impl FromRequest for String {
.map(|s| s.into_owned()) .map(|s| s.into_owned())
.ok_or_else(|| ErrorBadRequest("Can not decode body"))?) .ok_or_else(|| ErrorBadRequest("Can not decode body"))?)
} }
}), }
)) .boxed_local(),
)
} }
} }
/// Payload configuration for request's payload. /// Payload configuration for request's payload.
@ -300,7 +303,7 @@ pub struct HttpMessageBody {
length: Option<usize>, length: Option<usize>,
stream: Option<dev::Decompress<dev::Payload>>, stream: Option<dev::Decompress<dev::Payload>>,
err: Option<PayloadError>, err: Option<PayloadError>,
fut: Option<Box<dyn Future<Item = Bytes, Error = PayloadError>>>, fut: Option<LocalBoxFuture<'static, Result<Bytes, PayloadError>>>,
} }
impl HttpMessageBody { impl HttpMessageBody {
@ -346,42 +349,43 @@ impl HttpMessageBody {
} }
impl Future for HttpMessageBody { impl Future for HttpMessageBody {
type Item = Bytes; type Output = Result<Bytes, PayloadError>;
type Error = PayloadError;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> { fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
if let Some(ref mut fut) = self.fut { if let Some(ref mut fut) = self.fut {
return fut.poll(); return Pin::new(fut).poll(cx);
} }
if let Some(err) = self.err.take() { if let Some(err) = self.err.take() {
return Err(err); return Poll::Ready(Err(err));
} }
if let Some(len) = self.length.take() { if let Some(len) = self.length.take() {
if len > self.limit { if len > self.limit {
return Err(PayloadError::Overflow); return Poll::Ready(Err(PayloadError::Overflow));
} }
} }
// future // future
let limit = self.limit; let limit = self.limit;
self.fut = Some(Box::new( let mut stream = self.stream.take().unwrap();
self.stream self.fut = Some(
.take() async move {
.unwrap() let mut body = BytesMut::with_capacity(8192);
.from_err()
.fold(BytesMut::with_capacity(8192), move |mut body, chunk| { while let Some(item) = stream.next().await {
if (body.len() + chunk.len()) > limit { let chunk = item?;
Err(PayloadError::Overflow) if body.len() + chunk.len() > limit {
return Err(PayloadError::Overflow);
} else { } else {
body.extend_from_slice(&chunk); body.extend_from_slice(&chunk);
Ok(body)
} }
}) }
.map(|body| body.freeze()), Ok(body.freeze())
)); }
self.poll() .boxed_local(),
);
self.poll(cx)
} }
} }

View file

@ -4,6 +4,7 @@ use std::sync::Arc;
use std::{fmt, ops}; use std::{fmt, ops};
use actix_http::error::Error; use actix_http::error::Error;
use futures::future::{err, ok, Ready};
use serde::de; use serde::de;
use serde_urlencoded; use serde_urlencoded;
@ -132,7 +133,7 @@ where
T: de::DeserializeOwned, T: de::DeserializeOwned,
{ {
type Error = Error; type Error = Error;
type Future = Result<Self, Error>; type Future = Ready<Result<Self, Error>>;
type Config = QueryConfig; type Config = QueryConfig;
#[inline] #[inline]
@ -143,7 +144,7 @@ where
.unwrap_or(None); .unwrap_or(None);
serde_urlencoded::from_str::<T>(req.query_string()) serde_urlencoded::from_str::<T>(req.query_string())
.map(|val| Ok(Query(val))) .map(|val| ok(Query(val)))
.unwrap_or_else(move |e| { .unwrap_or_else(move |e| {
let e = QueryPayloadError::Deserialize(e); let e = QueryPayloadError::Deserialize(e);
@ -159,7 +160,7 @@ where
e.into() e.into()
}; };
Err(e) err(e)
}) })
} }
} }
@ -227,7 +228,7 @@ mod tests {
use super::*; use super::*;
use crate::error::InternalError; use crate::error::InternalError;
use crate::test::TestRequest; use crate::test::{block_on, TestRequest};
use crate::HttpResponse; use crate::HttpResponse;
#[derive(Deserialize, Debug, Display)] #[derive(Deserialize, Debug, Display)]
@ -253,24 +254,27 @@ mod tests {
#[test] #[test]
fn test_request_extract() { fn test_request_extract() {
block_on(async {
let req = TestRequest::with_uri("/name/user1/").to_srv_request(); let req = TestRequest::with_uri("/name/user1/").to_srv_request();
let (req, mut pl) = req.into_parts(); let (req, mut pl) = req.into_parts();
assert!(Query::<Id>::from_request(&req, &mut pl).is_err()); assert!(Query::<Id>::from_request(&req, &mut pl).await.is_err());
let req = TestRequest::with_uri("/name/user1/?id=test").to_srv_request(); let req = TestRequest::with_uri("/name/user1/?id=test").to_srv_request();
let (req, mut pl) = req.into_parts(); let (req, mut pl) = req.into_parts();
let mut s = Query::<Id>::from_request(&req, &mut pl).unwrap(); let mut s = Query::<Id>::from_request(&req, &mut pl).await.unwrap();
assert_eq!(s.id, "test"); assert_eq!(s.id, "test");
assert_eq!(format!("{}, {:?}", s, s), "test, Id { id: \"test\" }"); assert_eq!(format!("{}, {:?}", s, s), "test, Id { id: \"test\" }");
s.id = "test1".to_string(); s.id = "test1".to_string();
let s = s.into_inner(); let s = s.into_inner();
assert_eq!(s.id, "test1"); assert_eq!(s.id, "test1");
})
} }
#[test] #[test]
fn test_custom_error_responder() { fn test_custom_error_responder() {
block_on(async {
let req = TestRequest::with_uri("/name/user1/") let req = TestRequest::with_uri("/name/user1/")
.data(QueryConfig::default().error_handler(|e, _| { .data(QueryConfig::default().error_handler(|e, _| {
let resp = HttpResponse::UnprocessableEntity().finish(); let resp = HttpResponse::UnprocessableEntity().finish();
@ -279,7 +283,7 @@ mod tests {
.to_srv_request(); .to_srv_request();
let (req, mut pl) = req.into_parts(); let (req, mut pl) = req.into_parts();
let query = Query::<Id>::from_request(&req, &mut pl); let query = Query::<Id>::from_request(&req, &mut pl).await;
assert!(query.is_err()); assert!(query.is_err());
assert_eq!( assert_eq!(
@ -290,5 +294,6 @@ mod tests {
.status(), .status(),
StatusCode::UNPROCESSABLE_ENTITY StatusCode::UNPROCESSABLE_ENTITY
); );
})
} }
} }

View file

@ -1,9 +1,13 @@
use std::borrow::Cow; use std::borrow::Cow;
use std::future::Future;
use std::pin::Pin;
use std::str; use std::str;
use std::task::{Context, Poll};
use bytes::{Bytes, BytesMut}; use bytes::{Bytes, BytesMut};
use encoding_rs::{Encoding, UTF_8}; use encoding_rs::{Encoding, UTF_8};
use futures::{Async, Poll, Stream}; use futures::Stream;
use pin_project::pin_project;
use crate::dev::Payload; use crate::dev::Payload;
use crate::error::{PayloadError, ReadlinesError}; use crate::error::{PayloadError, ReadlinesError};
@ -22,7 +26,7 @@ pub struct Readlines<T: HttpMessage> {
impl<T> Readlines<T> impl<T> Readlines<T>
where where
T: HttpMessage, T: HttpMessage,
T::Stream: Stream<Item = Bytes, Error = PayloadError>, T::Stream: Stream<Item = Result<Bytes, PayloadError>> + Unpin,
{ {
/// Create a new stream to read request line by line. /// Create a new stream to read request line by line.
pub fn new(req: &mut T) -> Self { pub fn new(req: &mut T) -> Self {
@ -62,20 +66,21 @@ where
impl<T> Stream for Readlines<T> impl<T> Stream for Readlines<T>
where where
T: HttpMessage, T: HttpMessage,
T::Stream: Stream<Item = Bytes, Error = PayloadError>, T::Stream: Stream<Item = Result<Bytes, PayloadError>> + Unpin,
{ {
type Item = String; type Item = Result<String, ReadlinesError>;
type Error = ReadlinesError;
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> { fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Option<Self::Item>> {
if let Some(err) = self.err.take() { let this = self.get_mut();
return Err(err);
if let Some(err) = this.err.take() {
return Poll::Ready(Some(Err(err)));
} }
// check if there is a newline in the buffer // check if there is a newline in the buffer
if !self.checked_buff { if !this.checked_buff {
let mut found: Option<usize> = None; let mut found: Option<usize> = None;
for (ind, b) in self.buff.iter().enumerate() { for (ind, b) in this.buff.iter().enumerate() {
if *b == b'\n' { if *b == b'\n' {
found = Some(ind); found = Some(ind);
break; break;
@ -83,28 +88,28 @@ where
} }
if let Some(ind) = found { if let Some(ind) = found {
// check if line is longer than limit // check if line is longer than limit
if ind + 1 > self.limit { if ind + 1 > this.limit {
return Err(ReadlinesError::LimitOverflow); return Poll::Ready(Some(Err(ReadlinesError::LimitOverflow)));
} }
let line = if self.encoding == UTF_8 { let line = if this.encoding == UTF_8 {
str::from_utf8(&self.buff.split_to(ind + 1)) str::from_utf8(&this.buff.split_to(ind + 1))
.map_err(|_| ReadlinesError::EncodingError)? .map_err(|_| ReadlinesError::EncodingError)?
.to_owned() .to_owned()
} else { } else {
self.encoding this.encoding
.decode_without_bom_handling_and_without_replacement( .decode_without_bom_handling_and_without_replacement(
&self.buff.split_to(ind + 1), &this.buff.split_to(ind + 1),
) )
.map(Cow::into_owned) .map(Cow::into_owned)
.ok_or(ReadlinesError::EncodingError)? .ok_or(ReadlinesError::EncodingError)?
}; };
return Ok(Async::Ready(Some(line))); return Poll::Ready(Some(Ok(line)));
} }
self.checked_buff = true; this.checked_buff = true;
} }
// poll req for more bytes // poll req for more bytes
match self.stream.poll() { match Pin::new(&mut this.stream).poll_next(cx) {
Ok(Async::Ready(Some(mut bytes))) => { Poll::Ready(Some(Ok(mut bytes))) => {
// check if there is a newline in bytes // check if there is a newline in bytes
let mut found: Option<usize> = None; let mut found: Option<usize> = None;
for (ind, b) in bytes.iter().enumerate() { for (ind, b) in bytes.iter().enumerate() {
@ -115,15 +120,15 @@ where
} }
if let Some(ind) = found { if let Some(ind) = found {
// check if line is longer than limit // check if line is longer than limit
if ind + 1 > self.limit { if ind + 1 > this.limit {
return Err(ReadlinesError::LimitOverflow); return Poll::Ready(Some(Err(ReadlinesError::LimitOverflow)));
} }
let line = if self.encoding == UTF_8 { let line = if this.encoding == UTF_8 {
str::from_utf8(&bytes.split_to(ind + 1)) str::from_utf8(&bytes.split_to(ind + 1))
.map_err(|_| ReadlinesError::EncodingError)? .map_err(|_| ReadlinesError::EncodingError)?
.to_owned() .to_owned()
} else { } else {
self.encoding this.encoding
.decode_without_bom_handling_and_without_replacement( .decode_without_bom_handling_and_without_replacement(
&bytes.split_to(ind + 1), &bytes.split_to(ind + 1),
) )
@ -131,46 +136,49 @@ where
.ok_or(ReadlinesError::EncodingError)? .ok_or(ReadlinesError::EncodingError)?
}; };
// extend buffer with rest of the bytes; // extend buffer with rest of the bytes;
self.buff.extend_from_slice(&bytes); this.buff.extend_from_slice(&bytes);
self.checked_buff = false; this.checked_buff = false;
return Ok(Async::Ready(Some(line))); return Poll::Ready(Some(Ok(line)));
} }
self.buff.extend_from_slice(&bytes); this.buff.extend_from_slice(&bytes);
Ok(Async::NotReady) Poll::Pending
} }
Ok(Async::NotReady) => Ok(Async::NotReady), Poll::Pending => Poll::Pending,
Ok(Async::Ready(None)) => { Poll::Ready(None) => {
if self.buff.is_empty() { if this.buff.is_empty() {
return Ok(Async::Ready(None)); return Poll::Ready(None);
} }
if self.buff.len() > self.limit { if this.buff.len() > this.limit {
return Err(ReadlinesError::LimitOverflow); return Poll::Ready(Some(Err(ReadlinesError::LimitOverflow)));
} }
let line = if self.encoding == UTF_8 { let line = if this.encoding == UTF_8 {
str::from_utf8(&self.buff) str::from_utf8(&this.buff)
.map_err(|_| ReadlinesError::EncodingError)? .map_err(|_| ReadlinesError::EncodingError)?
.to_owned() .to_owned()
} else { } else {
self.encoding this.encoding
.decode_without_bom_handling_and_without_replacement(&self.buff) .decode_without_bom_handling_and_without_replacement(&this.buff)
.map(Cow::into_owned) .map(Cow::into_owned)
.ok_or(ReadlinesError::EncodingError)? .ok_or(ReadlinesError::EncodingError)?
}; };
self.buff.clear(); this.buff.clear();
Ok(Async::Ready(Some(line))) Poll::Ready(Some(Ok(line)))
} }
Err(e) => Err(ReadlinesError::from(e)), Poll::Ready(Some(Err(e))) => Poll::Ready(Some(Err(ReadlinesError::from(e)))),
} }
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use futures::stream::StreamExt;
use super::*; use super::*;
use crate::test::{block_on, TestRequest}; use crate::test::{block_on, TestRequest};
#[test] #[test]
fn test_readlines() { fn test_readlines() {
block_on(async {
let mut req = TestRequest::default() let mut req = TestRequest::default()
.set_payload(Bytes::from_static( .set_payload(Bytes::from_static(
b"Lorem Ipsum is simply dummy text of the printing and typesetting\n\ b"Lorem Ipsum is simply dummy text of the printing and typesetting\n\
@ -178,36 +186,22 @@ mod tests {
Contrary to popular belief, Lorem Ipsum is not simply random text.", Contrary to popular belief, Lorem Ipsum is not simply random text.",
)) ))
.to_request(); .to_request();
let stream = match block_on(Readlines::new(&mut req).into_future()) {
Ok((Some(s), stream)) => { let mut stream = Readlines::new(&mut req);
assert_eq!( assert_eq!(
s, stream.next().await.unwrap().unwrap(),
"Lorem Ipsum is simply dummy text of the printing and typesetting\n" "Lorem Ipsum is simply dummy text of the printing and typesetting\n"
); );
stream
}
_ => unreachable!("error"),
};
let stream = match block_on(stream.into_future()) {
Ok((Some(s), stream)) => {
assert_eq!( assert_eq!(
s, stream.next().await.unwrap().unwrap(),
"industry. Lorem Ipsum has been the industry's standard dummy\n" "industry. Lorem Ipsum has been the industry's standard dummy\n"
); );
stream
}
_ => unreachable!("error"),
};
match block_on(stream.into_future()) {
Ok((Some(s), _)) => {
assert_eq!( assert_eq!(
s, stream.next().await.unwrap().unwrap(),
"Contrary to popular belief, Lorem Ipsum is not simply random text." "Contrary to popular belief, Lorem Ipsum is not simply random text."
); );
} })
_ => unreachable!("error"),
}
} }
} }

View file

@ -1,11 +1,12 @@
//! Essentials helper functions and types for application registration. //! Essentials helper functions and types for application registration.
use actix_http::http::Method; use actix_http::http::Method;
use futures::{Future, IntoFuture}; use futures::Future;
pub use actix_http::Response as HttpResponse; pub use actix_http::Response as HttpResponse;
pub use bytes::{Bytes, BytesMut}; pub use bytes::{Bytes, BytesMut};
pub use futures::channel::oneshot::Canceled;
use crate::error::{BlockingError, Error}; use crate::error::Error;
use crate::extract::FromRequest; use crate::extract::FromRequest;
use crate::handler::{AsyncFactory, Factory}; use crate::handler::{AsyncFactory, Factory};
use crate::resource::Resource; use crate::resource::Resource;
@ -256,21 +257,21 @@ where
/// # use futures::future::{ok, Future}; /// # use futures::future::{ok, Future};
/// use actix_web::{web, App, HttpResponse, Error}; /// use actix_web::{web, App, HttpResponse, Error};
/// ///
/// fn index() -> impl Future<Item=HttpResponse, Error=Error> { /// async fn index() -> Result<HttpResponse, Error> {
/// ok(HttpResponse::Ok().finish()) /// Ok(HttpResponse::Ok().finish())
/// } /// }
/// ///
/// App::new().service(web::resource("/").route( /// App::new().service(web::resource("/").route(
/// web::to_async(index)) /// web::to_async(index))
/// ); /// );
/// ``` /// ```
pub fn to_async<F, I, R>(handler: F) -> Route pub fn to_async<F, I, R, O, E>(handler: F) -> Route
where where
F: AsyncFactory<I, R>, F: AsyncFactory<I, R, O, E>,
I: FromRequest + 'static, I: FromRequest + 'static,
R: IntoFuture + 'static, R: Future<Output = Result<O, E>> + 'static,
R::Item: Responder, O: Responder + 'static,
R::Error: Into<Error>, E: Into<Error> + 'static,
{ {
Route::new().to_async(handler) Route::new().to_async(handler)
} }
@ -279,10 +280,11 @@ where
/// ///
/// ```rust /// ```rust
/// # extern crate actix_web; /// # extern crate actix_web;
/// use actix_web::{dev, web, guard, App, HttpResponse}; /// use futures::future::{ok, Ready};
/// use actix_web::{dev, web, guard, App, Error, HttpResponse};
/// ///
/// fn my_service(req: dev::ServiceRequest) -> dev::ServiceResponse { /// fn my_service(req: dev::ServiceRequest) -> Ready<Result<dev::ServiceResponse, Error>> {
/// req.into_response(HttpResponse::Ok().finish()) /// ok(req.into_response(HttpResponse::Ok().finish()))
/// } /// }
/// ///
/// fn main() { /// fn main() {
@ -299,11 +301,10 @@ pub fn service(path: &str) -> 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 fn block<F, I, E>(f: F) -> impl Future<Item = I, Error = BlockingError<E>> pub fn block<F, R>(f: F) -> impl Future<Output = Result<R, Canceled>>
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,
{ {
actix_threadpool::run(f).from_err() actix_threadpool::run(f)
} }

View file

@ -59,19 +59,5 @@ tokio-timer = "0.3.0-alpha.6"
open-ssl = { version="0.10", package="openssl", optional = true } open-ssl = { version="0.10", package="openssl", optional = true }
[dev-dependencies] [dev-dependencies]
#actix-web = "1.0.7" actix-web = "2.0.0-alpha.1"
actix-http = "0.3.0-alpha.1" actix-http = "0.3.0-alpha.1"
[patch.crates-io]
actix-http = { path = "../actix-http" }
awc = { path = "../awc" }
actix-codec = { path = "../../actix-net/actix-codec" }
actix-connect = { path = "../../actix-net/actix-connect" }
actix-rt = { path = "../../actix-net/actix-rt" }
actix-server = { path = "../../actix-net/actix-server" }
actix-server-config = { path = "../../actix-net/actix-server-config" }
actix-service = { path = "../../actix-net/actix-service" }
actix-testing = { path = "../../actix-net/actix-testing" }
actix-threadpool = { path = "../../actix-net/actix-threadpool" }
actix-utils = { path = "../../actix-net/actix-utils" }

View file

@ -3,7 +3,7 @@ use std::sync::mpsc;
use std::{net, thread, time}; use std::{net, thread, time};
use actix_codec::{AsyncRead, AsyncWrite, Framed}; use actix_codec::{AsyncRead, AsyncWrite, Framed};
use actix_rt::{System}; use actix_rt::System;
use actix_server::{Server, ServiceFactory}; use actix_server::{Server, ServiceFactory};
use awc::{error::PayloadError, ws, Client, ClientRequest, ClientResponse, Connector}; use awc::{error::PayloadError, ws, Client, ClientRequest, ClientResponse, Connector};
use bytes::Bytes; use bytes::Bytes;

View file

@ -2,8 +2,8 @@ use net2::TcpBuilder;
use std::sync::mpsc; use std::sync::mpsc;
use std::{net, thread, time::Duration}; use std::{net, thread, time::Duration};
#[cfg(feature = "ssl")] #[cfg(feature = "openssl")]
use openssl::ssl::SslAcceptorBuilder; use open_ssl::ssl::SslAcceptorBuilder;
use actix_http::Response; use actix_http::Response;
use actix_web::{test, web, App, HttpServer}; use actix_web::{test, web, App, HttpServer};
@ -55,22 +55,19 @@ fn test_start() {
use actix_http::client; use actix_http::client;
use actix_web::test; use actix_web::test;
let client = test::run_on(|| { test::block_on(async {
Ok::<_, ()>( let client = awc::Client::build()
awc::Client::build()
.connector( .connector(
client::Connector::new() client::Connector::new()
.timeout(Duration::from_millis(100)) .timeout(Duration::from_millis(100))
.finish(), .finish(),
) )
.finish(), .finish();
)
})
.unwrap();
let host = format!("http://{}", addr);
let response = test::block_on(client.get(host.clone()).send()).unwrap(); let host = format!("http://{}", addr);
let response = client.get(host.clone()).send().await.unwrap();
assert!(response.status().is_success()); assert!(response.status().is_success());
});
} }
// stop // stop
@ -80,9 +77,9 @@ fn test_start() {
let _ = sys.stop(); let _ = sys.stop();
} }
#[cfg(feature = "ssl")] #[cfg(feature = "openssl")]
fn ssl_acceptor() -> std::io::Result<SslAcceptorBuilder> { fn ssl_acceptor() -> std::io::Result<SslAcceptorBuilder> {
use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod}; use open_ssl::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
@ -95,7 +92,7 @@ fn ssl_acceptor() -> std::io::Result<SslAcceptorBuilder> {
} }
#[test] #[test]
#[cfg(feature = "ssl")] #[cfg(feature = "openssl")]
fn test_start_ssl() { fn test_start_ssl() {
let addr = unused_addr(); let addr = unused_addr();
let (tx, rx) = mpsc::channel(); let (tx, rx) = mpsc::channel();
@ -113,7 +110,7 @@ fn test_start_ssl() {
.shutdown_timeout(1) .shutdown_timeout(1)
.system_exit() .system_exit()
.disable_signals() .disable_signals()
.bind_ssl(format!("{}", addr), builder) .bind_openssl(format!("{}", addr), builder)
.unwrap() .unwrap()
.start(); .start();
@ -122,30 +119,27 @@ fn test_start_ssl() {
}); });
let (srv, sys) = rx.recv().unwrap(); let (srv, sys) = rx.recv().unwrap();
let client = test::run_on(|| { test::block_on(async move {
use openssl::ssl::{SslConnector, SslMethod, SslVerifyMode}; use open_ssl::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
.set_alpn_protos(b"\x02h2\x08http/1.1") .set_alpn_protos(b"\x02h2\x08http/1.1")
.map_err(|e| log::error!("Can not set alpn protocol: {:?}", e)); .map_err(|e| log::error!("Can not set alpn protocol: {:?}", e));
Ok::<_, ()>( let client = awc::Client::build()
awc::Client::build()
.connector( .connector(
awc::Connector::new() awc::Connector::new()
.ssl(builder.build()) .ssl(builder.build())
.timeout(Duration::from_millis(100)) .timeout(Duration::from_millis(100))
.finish(), .finish(),
) )
.finish(), .finish();
)
})
.unwrap();
let host = format!("https://{}", addr);
let response = test::block_on(client.get(host.clone()).send()).unwrap(); let host = format!("https://{}", addr);
let response = client.get(host.clone()).send().await.unwrap();
assert!(response.status().is_success()); assert!(response.status().is_success());
});
// stop // stop
let _ = srv.stop(false); let _ = srv.stop(false);

View file

@ -1,18 +1,17 @@
use std::io::{Read, Write}; use std::io::{Read, Write};
use std::sync::mpsc;
use std::thread;
use actix_http::http::header::{ use actix_http::http::header::{
ContentEncoding, ACCEPT_ENCODING, CONTENT_ENCODING, CONTENT_LENGTH, ContentEncoding, ACCEPT_ENCODING, CONTENT_ENCODING, CONTENT_LENGTH,
TRANSFER_ENCODING, TRANSFER_ENCODING,
}; };
use actix_http::{h1, Error, HttpService, Response}; use actix_http::{h1, Error, HttpService, Response};
use actix_http_test::TestServer; use actix_http_test::{block_on, TestServer};
use brotli2::write::{BrotliDecoder, BrotliEncoder}; use brotli2::write::{BrotliDecoder, BrotliEncoder};
use bytes::Bytes; use bytes::Bytes;
use flate2::read::GzDecoder; use flate2::read::GzDecoder;
use flate2::write::{GzEncoder, ZlibDecoder, ZlibEncoder}; use flate2::write::{GzEncoder, ZlibDecoder, ZlibEncoder};
use flate2::Compression; use flate2::Compression;
use futures::future::ok;
use futures::stream::once; use futures::stream::once;
use rand::{distributions::Alphanumeric, Rng}; use rand::{distributions::Alphanumeric, Rng};
@ -44,56 +43,62 @@ const STR: &str = "Hello World Hello World Hello World Hello World Hello World \
#[test] #[test]
fn test_body() { fn test_body() {
let mut srv = TestServer::new(|| { block_on(async {
h1::H1Service::new( let srv =
App::new() TestServer::start(|| {
.service(web::resource("/").route(web::to(|| Response::Ok().body(STR)))), h1::H1Service::new(App::new().service(
) web::resource("/").route(web::to(|| Response::Ok().body(STR))),
))
}); });
let mut response = srv.block_on(srv.get("/").send()).unwrap(); let mut response = srv.get("/").send().await.unwrap();
assert!(response.status().is_success()); assert!(response.status().is_success());
// read response // read response
let bytes = srv.block_on(response.body()).unwrap(); let bytes = response.body().await.unwrap();
assert_eq!(bytes, Bytes::from_static(STR.as_ref())); assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
})
} }
#[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))]
#[test] #[test]
fn test_body_gzip() { fn test_body_gzip() {
let mut srv = TestServer::new(|| { block_on(async {
let srv = TestServer::start(|| {
h1::H1Service::new( h1::H1Service::new(
App::new() App::new()
.wrap(Compress::new(ContentEncoding::Gzip)) .wrap(Compress::new(ContentEncoding::Gzip))
.service(web::resource("/").route(web::to(|| Response::Ok().body(STR)))), .service(
web::resource("/").route(web::to(|| Response::Ok().body(STR))),
),
) )
}); });
let mut response = srv let mut response = srv
.block_on( .get("/")
srv.get("/")
.no_decompress() .no_decompress()
.header(ACCEPT_ENCODING, "gzip") .header(ACCEPT_ENCODING, "gzip")
.send(), .send()
) .await
.unwrap(); .unwrap();
assert!(response.status().is_success()); assert!(response.status().is_success());
// read response // read response
let bytes = srv.block_on(response.body()).unwrap(); let bytes = response.body().await.unwrap();
// decode // decode
let mut e = GzDecoder::new(&bytes[..]); let mut e = GzDecoder::new(&bytes[..]);
let mut dec = Vec::new(); let mut dec = Vec::new();
e.read_to_end(&mut dec).unwrap(); e.read_to_end(&mut dec).unwrap();
assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref())); assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref()));
})
} }
#[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))]
#[test] #[test]
fn test_body_gzip2() { fn test_body_gzip2() {
let mut srv = TestServer::new(|| { block_on(async {
let srv = TestServer::start(|| {
h1::H1Service::new( h1::H1Service::new(
App::new() App::new()
.wrap(Compress::new(ContentEncoding::Gzip)) .wrap(Compress::new(ContentEncoding::Gzip))
@ -104,29 +109,30 @@ fn test_body_gzip2() {
}); });
let mut response = srv let mut response = srv
.block_on( .get("/")
srv.get("/")
.no_decompress() .no_decompress()
.header(ACCEPT_ENCODING, "gzip") .header(ACCEPT_ENCODING, "gzip")
.send(), .send()
) .await
.unwrap(); .unwrap();
assert!(response.status().is_success()); assert!(response.status().is_success());
// read response // read response
let bytes = srv.block_on(response.body()).unwrap(); let bytes = response.body().await.unwrap();
// decode // decode
let mut e = GzDecoder::new(&bytes[..]); let mut e = GzDecoder::new(&bytes[..]);
let mut dec = Vec::new(); let mut dec = Vec::new();
e.read_to_end(&mut dec).unwrap(); e.read_to_end(&mut dec).unwrap();
assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref())); assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref()));
})
} }
#[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))]
#[test] #[test]
fn test_body_encoding_override() { fn test_body_encoding_override() {
let mut srv = TestServer::new(|| { block_on(async {
let srv = TestServer::start(|| {
h1::H1Service::new( h1::H1Service::new(
App::new() App::new()
.wrap(Compress::new(ContentEncoding::Gzip)) .wrap(Compress::new(ContentEncoding::Gzip))
@ -147,17 +153,16 @@ fn test_body_encoding_override() {
// Builder // Builder
let mut response = srv let mut response = srv
.block_on( .get("/")
srv.get("/")
.no_decompress() .no_decompress()
.header(ACCEPT_ENCODING, "deflate") .header(ACCEPT_ENCODING, "deflate")
.send(), .send()
) .await
.unwrap(); .unwrap();
assert!(response.status().is_success()); assert!(response.status().is_success());
// read response // read response
let bytes = srv.block_on(response.body()).unwrap(); let bytes = response.body().await.unwrap();
// decode // decode
let mut e = ZlibDecoder::new(Vec::new()); let mut e = ZlibDecoder::new(Vec::new());
@ -167,32 +172,33 @@ fn test_body_encoding_override() {
// Raw Response // Raw Response
let mut response = srv let mut response = srv
.block_on( .request(actix_web::http::Method::GET, srv.url("/raw"))
srv.request(actix_web::http::Method::GET, srv.url("/raw"))
.no_decompress() .no_decompress()
.header(ACCEPT_ENCODING, "deflate") .header(ACCEPT_ENCODING, "deflate")
.send(), .send()
) .await
.unwrap(); .unwrap();
assert!(response.status().is_success()); assert!(response.status().is_success());
// read response // read response
let bytes = srv.block_on(response.body()).unwrap(); let bytes = response.body().await.unwrap();
// decode // decode
let mut e = ZlibDecoder::new(Vec::new()); let mut e = ZlibDecoder::new(Vec::new());
e.write_all(bytes.as_ref()).unwrap(); e.write_all(bytes.as_ref()).unwrap();
let dec = e.finish().unwrap(); let dec = e.finish().unwrap();
assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref())); assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref()));
})
} }
#[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))]
#[test] #[test]
fn test_body_gzip_large() { fn test_body_gzip_large() {
block_on(async {
let data = STR.repeat(10); let data = STR.repeat(10);
let srv_data = data.clone(); let srv_data = data.clone();
let mut srv = TestServer::new(move || { let srv = TestServer::start(move || {
let data = srv_data.clone(); let data = srv_data.clone();
h1::H1Service::new( h1::H1Service::new(
App::new() App::new()
@ -205,35 +211,36 @@ fn test_body_gzip_large() {
}); });
let mut response = srv let mut response = srv
.block_on( .get("/")
srv.get("/")
.no_decompress() .no_decompress()
.header(ACCEPT_ENCODING, "gzip") .header(ACCEPT_ENCODING, "gzip")
.send(), .send()
) .await
.unwrap(); .unwrap();
assert!(response.status().is_success()); assert!(response.status().is_success());
// read response // read response
let bytes = srv.block_on(response.body()).unwrap(); let bytes = response.body().await.unwrap();
// decode // decode
let mut e = GzDecoder::new(&bytes[..]); let mut e = GzDecoder::new(&bytes[..]);
let mut dec = Vec::new(); let mut dec = Vec::new();
e.read_to_end(&mut dec).unwrap(); e.read_to_end(&mut dec).unwrap();
assert_eq!(Bytes::from(dec), Bytes::from(data)); assert_eq!(Bytes::from(dec), Bytes::from(data));
})
} }
#[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))]
#[test] #[test]
fn test_body_gzip_large_random() { fn test_body_gzip_large_random() {
block_on(async {
let data = rand::thread_rng() let data = rand::thread_rng()
.sample_iter(&Alphanumeric) .sample_iter(&Alphanumeric)
.take(70_000) .take(70_000)
.collect::<String>(); .collect::<String>();
let srv_data = data.clone(); let srv_data = data.clone();
let mut srv = TestServer::new(move || { let srv = TestServer::start(move || {
let data = srv_data.clone(); let data = srv_data.clone();
h1::H1Service::new( h1::H1Service::new(
App::new() App::new()
@ -246,17 +253,16 @@ fn test_body_gzip_large_random() {
}); });
let mut response = srv let mut response = srv
.block_on( .get("/")
srv.get("/")
.no_decompress() .no_decompress()
.header(ACCEPT_ENCODING, "gzip") .header(ACCEPT_ENCODING, "gzip")
.send(), .send()
) .await
.unwrap(); .unwrap();
assert!(response.status().is_success()); assert!(response.status().is_success());
// read response // read response
let bytes = srv.block_on(response.body()).unwrap(); let bytes = response.body().await.unwrap();
// decode // decode
let mut e = GzDecoder::new(&bytes[..]); let mut e = GzDecoder::new(&bytes[..]);
@ -264,30 +270,31 @@ fn test_body_gzip_large_random() {
e.read_to_end(&mut dec).unwrap(); e.read_to_end(&mut dec).unwrap();
assert_eq!(dec.len(), data.len()); assert_eq!(dec.len(), data.len());
assert_eq!(Bytes::from(dec), Bytes::from(data)); assert_eq!(Bytes::from(dec), Bytes::from(data));
})
} }
#[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))]
#[test] #[test]
fn test_body_chunked_implicit() { fn test_body_chunked_implicit() {
let mut srv = TestServer::new(move || { block_on(async {
let srv = TestServer::start(move || {
h1::H1Service::new( h1::H1Service::new(
App::new() App::new()
.wrap(Compress::new(ContentEncoding::Gzip)) .wrap(Compress::new(ContentEncoding::Gzip))
.service(web::resource("/").route(web::get().to(move || { .service(web::resource("/").route(web::get().to(move || {
Response::Ok().streaming(once(Ok::<_, Error>(Bytes::from_static( Response::Ok().streaming(once(ok::<_, Error>(
STR.as_ref(), Bytes::from_static(STR.as_ref()),
)))) )))
}))), }))),
) )
}); });
let mut response = srv let mut response = srv
.block_on( .get("/")
srv.get("/")
.no_decompress() .no_decompress()
.header(ACCEPT_ENCODING, "gzip") .header(ACCEPT_ENCODING, "gzip")
.send(), .send()
) .await
.unwrap(); .unwrap();
assert!(response.status().is_success()); assert!(response.status().is_success());
assert_eq!( assert_eq!(
@ -296,56 +303,62 @@ fn test_body_chunked_implicit() {
); );
// read response // read response
let bytes = srv.block_on(response.body()).unwrap(); let bytes = response.body().await.unwrap();
// decode // decode
let mut e = GzDecoder::new(&bytes[..]); let mut e = GzDecoder::new(&bytes[..]);
let mut dec = Vec::new(); let mut dec = Vec::new();
e.read_to_end(&mut dec).unwrap(); e.read_to_end(&mut dec).unwrap();
assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref())); assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref()));
})
} }
#[test] #[test]
#[cfg(feature = "brotli")] #[cfg(feature = "brotli")]
fn test_body_br_streaming() { fn test_body_br_streaming() {
let mut srv = TestServer::new(move || { block_on(async {
h1::H1Service::new(App::new().wrap(Compress::new(ContentEncoding::Br)).service( let srv = TestServer::start(move || {
h1::H1Service::new(
App::new().wrap(Compress::new(ContentEncoding::Br)).service(
web::resource("/").route(web::to(move || { web::resource("/").route(web::to(move || {
Response::Ok() Response::Ok().streaming(once(ok::<_, Error>(
.streaming(once(Ok::<_, Error>(Bytes::from_static(STR.as_ref())))) Bytes::from_static(STR.as_ref()),
)))
})), })),
)) ),
)
}); });
let mut response = srv let mut response = srv
.block_on( .get("/")
srv.get("/")
.header(ACCEPT_ENCODING, "br") .header(ACCEPT_ENCODING, "br")
.no_decompress() .no_decompress()
.send(), .send()
) .await
.unwrap(); .unwrap();
assert!(response.status().is_success()); assert!(response.status().is_success());
// read response // read response
let bytes = srv.block_on(response.body()).unwrap(); let bytes = response.body().await.unwrap();
// decode br // decode br
let mut e = BrotliDecoder::new(Vec::with_capacity(2048)); let mut e = BrotliDecoder::new(Vec::with_capacity(2048));
e.write_all(bytes.as_ref()).unwrap(); e.write_all(bytes.as_ref()).unwrap();
let dec = e.finish().unwrap(); let dec = e.finish().unwrap();
assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref())); assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref()));
})
} }
#[test] #[test]
fn test_head_binary() { fn test_head_binary() {
let mut srv = TestServer::new(move || { block_on(async {
let srv = TestServer::start(move || {
h1::H1Service::new(App::new().service(web::resource("/").route( h1::H1Service::new(App::new().service(web::resource("/").route(
web::head().to(move || Response::Ok().content_length(100).body(STR)), web::head().to(move || Response::Ok().content_length(100).body(STR)),
))) )))
}); });
let mut response = srv.block_on(srv.head("/").send()).unwrap(); let mut response = srv.head("/").send().await.unwrap();
assert!(response.status().is_success()); assert!(response.status().is_success());
{ {
@ -354,40 +367,79 @@ fn test_head_binary() {
} }
// read response // read response
let bytes = srv.block_on(response.body()).unwrap(); let bytes = response.body().await.unwrap();
assert!(bytes.is_empty()); assert!(bytes.is_empty());
})
} }
#[test] #[test]
fn test_no_chunking() { fn test_no_chunking() {
let mut srv = TestServer::new(move || { block_on(async {
let srv = TestServer::start(move || {
h1::H1Service::new(App::new().service(web::resource("/").route(web::to( h1::H1Service::new(App::new().service(web::resource("/").route(web::to(
move || { move || {
Response::Ok() Response::Ok()
.no_chunking() .no_chunking()
.content_length(STR.len() as u64) .content_length(STR.len() as u64)
.streaming(once(Ok::<_, Error>(Bytes::from_static(STR.as_ref())))) .streaming(once(ok::<_, Error>(Bytes::from_static(
STR.as_ref(),
))))
}, },
)))) ))))
}); });
let mut response = srv.block_on(srv.get("/").send()).unwrap(); let mut response = srv.get("/").send().await.unwrap();
assert!(response.status().is_success()); assert!(response.status().is_success());
assert!(!response.headers().contains_key(TRANSFER_ENCODING)); assert!(!response.headers().contains_key(TRANSFER_ENCODING));
// read response // read response
let bytes = srv.block_on(response.body()).unwrap(); let bytes = response.body().await.unwrap();
assert_eq!(bytes, Bytes::from_static(STR.as_ref())); assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
})
} }
#[test] #[test]
#[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))]
fn test_body_deflate() { fn test_body_deflate() {
let mut srv = TestServer::new(move || { block_on(async {
let srv = TestServer::start(move || {
h1::H1Service::new( h1::H1Service::new(
App::new() App::new()
.wrap(Compress::new(ContentEncoding::Deflate)) .wrap(Compress::new(ContentEncoding::Deflate))
.service( .service(
web::resource("/")
.route(web::to(move || Response::Ok().body(STR))),
),
)
});
// client request
let mut response = srv
.get("/")
.header(ACCEPT_ENCODING, "deflate")
.no_decompress()
.send()
.await
.unwrap();
assert!(response.status().is_success());
// read response
let bytes = response.body().await.unwrap();
let mut e = ZlibDecoder::new(Vec::new());
e.write_all(bytes.as_ref()).unwrap();
let dec = e.finish().unwrap();
assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref()));
})
}
#[test]
#[cfg(any(feature = "brotli"))]
fn test_body_brotli() {
block_on(async {
let srv = TestServer::start(move || {
h1::H1Service::new(
App::new().wrap(Compress::new(ContentEncoding::Br)).service(
web::resource("/").route(web::to(move || Response::Ok().body(STR))), web::resource("/").route(web::to(move || Response::Ok().body(STR))),
), ),
) )
@ -395,58 +447,30 @@ fn test_body_deflate() {
// client request // client request
let mut response = srv let mut response = srv
.block_on( .get("/")
srv.get("/")
.header(ACCEPT_ENCODING, "deflate")
.no_decompress()
.send(),
)
.unwrap();
assert!(response.status().is_success());
// read response
let bytes = srv.block_on(response.body()).unwrap();
let mut e = ZlibDecoder::new(Vec::new());
e.write_all(bytes.as_ref()).unwrap();
let dec = e.finish().unwrap();
assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref()));
}
#[test]
#[cfg(any(feature = "brotli"))]
fn test_body_brotli() {
let mut srv = TestServer::new(move || {
h1::H1Service::new(App::new().wrap(Compress::new(ContentEncoding::Br)).service(
web::resource("/").route(web::to(move || Response::Ok().body(STR))),
))
});
// client request
let mut response = srv
.block_on(
srv.get("/")
.header(ACCEPT_ENCODING, "br") .header(ACCEPT_ENCODING, "br")
.no_decompress() .no_decompress()
.send(), .send()
) .await
.unwrap(); .unwrap();
assert!(response.status().is_success()); assert!(response.status().is_success());
// read response // read response
let bytes = srv.block_on(response.body()).unwrap(); let bytes = response.body().await.unwrap();
// decode brotli // decode brotli
let mut e = BrotliDecoder::new(Vec::with_capacity(2048)); let mut e = BrotliDecoder::new(Vec::with_capacity(2048));
e.write_all(bytes.as_ref()).unwrap(); e.write_all(bytes.as_ref()).unwrap();
let dec = e.finish().unwrap(); let dec = e.finish().unwrap();
assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref())); assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref()));
})
} }
#[test] #[test]
#[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))]
fn test_encoding() { fn test_encoding() {
let mut srv = TestServer::new(move || { block_on(async {
let srv = TestServer::start(move || {
HttpService::new( HttpService::new(
App::new().wrap(Compress::default()).service( App::new().wrap(Compress::default()).service(
web::resource("/") web::resource("/")
@ -464,18 +488,20 @@ fn test_encoding() {
.post("/") .post("/")
.header(CONTENT_ENCODING, "gzip") .header(CONTENT_ENCODING, "gzip")
.send_body(enc.clone()); .send_body(enc.clone());
let mut response = srv.block_on(request).unwrap(); let mut response = request.await.unwrap();
assert!(response.status().is_success()); assert!(response.status().is_success());
// read response // read response
let bytes = srv.block_on(response.body()).unwrap(); let bytes = response.body().await.unwrap();
assert_eq!(bytes, Bytes::from_static(STR.as_ref())); assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
})
} }
#[test] #[test]
#[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))]
fn test_gzip_encoding() { fn test_gzip_encoding() {
let mut srv = TestServer::new(move || { block_on(async {
let srv = TestServer::start(move || {
HttpService::new( HttpService::new(
App::new().service( App::new().service(
web::resource("/") web::resource("/")
@ -493,19 +519,21 @@ fn test_gzip_encoding() {
.post("/") .post("/")
.header(CONTENT_ENCODING, "gzip") .header(CONTENT_ENCODING, "gzip")
.send_body(enc.clone()); .send_body(enc.clone());
let mut response = srv.block_on(request).unwrap(); let mut response = request.await.unwrap();
assert!(response.status().is_success()); assert!(response.status().is_success());
// read response // read response
let bytes = srv.block_on(response.body()).unwrap(); let bytes = response.body().await.unwrap();
assert_eq!(bytes, Bytes::from_static(STR.as_ref())); assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
})
} }
#[test] #[test]
#[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))]
fn test_gzip_encoding_large() { fn test_gzip_encoding_large() {
block_on(async {
let data = STR.repeat(10); let data = STR.repeat(10);
let mut srv = TestServer::new(move || { let srv = TestServer::start(move || {
h1::H1Service::new( h1::H1Service::new(
App::new().service( App::new().service(
web::resource("/") web::resource("/")
@ -523,23 +551,25 @@ fn test_gzip_encoding_large() {
.post("/") .post("/")
.header(CONTENT_ENCODING, "gzip") .header(CONTENT_ENCODING, "gzip")
.send_body(enc.clone()); .send_body(enc.clone());
let mut response = srv.block_on(request).unwrap(); let mut response = request.await.unwrap();
assert!(response.status().is_success()); assert!(response.status().is_success());
// read response // read response
let bytes = srv.block_on(response.body()).unwrap(); let bytes = response.body().await.unwrap();
assert_eq!(bytes, Bytes::from(data)); assert_eq!(bytes, Bytes::from(data));
})
} }
#[test] #[test]
#[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))]
fn test_reading_gzip_encoding_large_random() { fn test_reading_gzip_encoding_large_random() {
block_on(async {
let data = rand::thread_rng() let data = rand::thread_rng()
.sample_iter(&Alphanumeric) .sample_iter(&Alphanumeric)
.take(60_000) .take(60_000)
.collect::<String>(); .collect::<String>();
let mut srv = TestServer::new(move || { let srv = TestServer::start(move || {
HttpService::new( HttpService::new(
App::new().service( App::new().service(
web::resource("/") web::resource("/")
@ -557,19 +587,21 @@ fn test_reading_gzip_encoding_large_random() {
.post("/") .post("/")
.header(CONTENT_ENCODING, "gzip") .header(CONTENT_ENCODING, "gzip")
.send_body(enc.clone()); .send_body(enc.clone());
let mut response = srv.block_on(request).unwrap(); let mut response = request.await.unwrap();
assert!(response.status().is_success()); assert!(response.status().is_success());
// read response // read response
let bytes = srv.block_on(response.body()).unwrap(); let bytes = response.body().await.unwrap();
assert_eq!(bytes.len(), data.len()); assert_eq!(bytes.len(), data.len());
assert_eq!(bytes, Bytes::from(data)); assert_eq!(bytes, Bytes::from(data));
})
} }
#[test] #[test]
#[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))]
fn test_reading_deflate_encoding() { fn test_reading_deflate_encoding() {
let mut srv = TestServer::new(move || { block_on(async {
let srv = TestServer::start(move || {
h1::H1Service::new( h1::H1Service::new(
App::new().service( App::new().service(
web::resource("/") web::resource("/")
@ -587,19 +619,21 @@ fn test_reading_deflate_encoding() {
.post("/") .post("/")
.header(CONTENT_ENCODING, "deflate") .header(CONTENT_ENCODING, "deflate")
.send_body(enc.clone()); .send_body(enc.clone());
let mut response = srv.block_on(request).unwrap(); let mut response = request.await.unwrap();
assert!(response.status().is_success()); assert!(response.status().is_success());
// read response // read response
let bytes = srv.block_on(response.body()).unwrap(); let bytes = response.body().await.unwrap();
assert_eq!(bytes, Bytes::from_static(STR.as_ref())); assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
})
} }
#[test] #[test]
#[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))]
fn test_reading_deflate_encoding_large() { fn test_reading_deflate_encoding_large() {
block_on(async {
let data = STR.repeat(10); let data = STR.repeat(10);
let mut srv = TestServer::new(move || { let srv = TestServer::start(move || {
h1::H1Service::new( h1::H1Service::new(
App::new().service( App::new().service(
web::resource("/") web::resource("/")
@ -617,23 +651,25 @@ fn test_reading_deflate_encoding_large() {
.post("/") .post("/")
.header(CONTENT_ENCODING, "deflate") .header(CONTENT_ENCODING, "deflate")
.send_body(enc.clone()); .send_body(enc.clone());
let mut response = srv.block_on(request).unwrap(); let mut response = request.await.unwrap();
assert!(response.status().is_success()); assert!(response.status().is_success());
// read response // read response
let bytes = srv.block_on(response.body()).unwrap(); let bytes = response.body().await.unwrap();
assert_eq!(bytes, Bytes::from(data)); assert_eq!(bytes, Bytes::from(data));
})
} }
#[test] #[test]
#[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))]
fn test_reading_deflate_encoding_large_random() { fn test_reading_deflate_encoding_large_random() {
block_on(async {
let data = rand::thread_rng() let data = rand::thread_rng()
.sample_iter(&Alphanumeric) .sample_iter(&Alphanumeric)
.take(160_000) .take(160_000)
.collect::<String>(); .collect::<String>();
let mut srv = TestServer::new(move || { let srv = TestServer::start(move || {
h1::H1Service::new( h1::H1Service::new(
App::new().service( App::new().service(
web::resource("/") web::resource("/")
@ -651,19 +687,21 @@ fn test_reading_deflate_encoding_large_random() {
.post("/") .post("/")
.header(CONTENT_ENCODING, "deflate") .header(CONTENT_ENCODING, "deflate")
.send_body(enc.clone()); .send_body(enc.clone());
let mut response = srv.block_on(request).unwrap(); let mut response = request.await.unwrap();
assert!(response.status().is_success()); assert!(response.status().is_success());
// read response // read response
let bytes = srv.block_on(response.body()).unwrap(); let bytes = response.body().await.unwrap();
assert_eq!(bytes.len(), data.len()); assert_eq!(bytes.len(), data.len());
assert_eq!(bytes, Bytes::from(data)); assert_eq!(bytes, Bytes::from(data));
})
} }
#[test] #[test]
#[cfg(feature = "brotli")] #[cfg(feature = "brotli")]
fn test_brotli_encoding() { fn test_brotli_encoding() {
let mut srv = TestServer::new(move || { block_on(async {
let srv = TestServer::start(move || {
h1::H1Service::new( h1::H1Service::new(
App::new().service( App::new().service(
web::resource("/") web::resource("/")
@ -681,19 +719,21 @@ fn test_brotli_encoding() {
.post("/") .post("/")
.header(CONTENT_ENCODING, "br") .header(CONTENT_ENCODING, "br")
.send_body(enc.clone()); .send_body(enc.clone());
let mut response = srv.block_on(request).unwrap(); let mut response = request.await.unwrap();
assert!(response.status().is_success()); assert!(response.status().is_success());
// read response // read response
let bytes = srv.block_on(response.body()).unwrap(); let bytes = response.body().await.unwrap();
assert_eq!(bytes, Bytes::from_static(STR.as_ref())); assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
})
} }
#[cfg(feature = "brotli")] #[cfg(feature = "brotli")]
#[test] #[test]
fn test_brotli_encoding_large() { fn test_brotli_encoding_large() {
block_on(async {
let data = STR.repeat(10); let data = STR.repeat(10);
let mut srv = TestServer::new(move || { let srv = TestServer::start(move || {
h1::H1Service::new( h1::H1Service::new(
App::new().service( App::new().service(
web::resource("/") web::resource("/")
@ -711,12 +751,13 @@ fn test_brotli_encoding_large() {
.post("/") .post("/")
.header(CONTENT_ENCODING, "br") .header(CONTENT_ENCODING, "br")
.send_body(enc.clone()); .send_body(enc.clone());
let mut response = srv.block_on(request).unwrap(); let mut response = request.await.unwrap();
assert!(response.status().is_success()); assert!(response.status().is_success());
// read response // read response
let bytes = srv.block_on(response.body()).unwrap(); let bytes = response.body().await.unwrap();
assert_eq!(bytes, Bytes::from(data)); assert_eq!(bytes, Bytes::from(data));
})
} }
// #[cfg(all(feature = "brotli", feature = "ssl"))] // #[cfg(all(feature = "brotli", feature = "ssl"))]
@ -782,6 +823,7 @@ fn test_brotli_encoding_large() {
))] ))]
#[test] #[test]
fn test_reading_deflate_encoding_large_random_ssl() { fn test_reading_deflate_encoding_large_random_ssl() {
block_on(async {
use openssl::ssl::{SslConnector, SslMethod, SslVerifyMode}; use openssl::ssl::{SslConnector, SslMethod, SslVerifyMode};
use rustls::internal::pemfile::{certs, pkcs8_private_keys}; use rustls::internal::pemfile::{certs, pkcs8_private_keys};
use rustls::{NoClientAuth, ServerConfig}; use rustls::{NoClientAuth, ServerConfig};
@ -861,6 +903,7 @@ fn test_reading_deflate_encoding_large_random_ssl() {
// stop // stop
let _ = srv.stop(false); let _ = srv.stop(false);
})
} }
// #[cfg(all(feature = "tls", feature = "ssl"))] // #[cfg(all(feature = "tls", feature = "ssl"))]
@ -954,7 +997,7 @@ fn test_reading_deflate_encoding_large_random_ssl() {
// fn test_server_cookies() { // fn test_server_cookies() {
// use actix_web::http; // use actix_web::http;
// let mut srv = test::TestServer::with_factory(|| { // let srv = test::TestServer::with_factory(|| {
// App::new().resource("/", |r| { // App::new().resource("/", |r| {
// r.f(|_| { // r.f(|_| {
// HttpResponse::Ok() // HttpResponse::Ok()