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,9 +303,8 @@ 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);
self self

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" }

File diff suppressed because it is too large Load diff

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
.header("User-Agent", "Actix-web")
.send() // <- Send http request
.from_err()
.and_then(|mut response| {
// <- server http response
println!("Response: {:?}", response);
// read response body // Create request builder, configure request and send
response let mut response = client
.body() .get("https://www.rust-lang.org/")
.from_err() .header("User-Agent", "Actix-web")
.map(|body| println!("Downloaded: {:?} bytes", body.len())) .send()
}) .await?;
}))
// server http response
println!("Response: {:?}", response);
// read response body
let body = response.body().await?;
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 {
log::error!("Can not construct data instance: {:?}", e); Err(e) => {
}) log::error!("Can not construct data instance: {:?}", e);
.map(|data| { Err(())
let data: Box<dyn DataFactory> = Box::new(Data::new(data)); }
data Ok(data) => {
}), let data: Box<dyn DataFactory> = Box::new(Data::new(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,82 +486,89 @@ 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() {
let mut srv = init_service( block_on(async {
App::new().service(web::resource("/test").to(|| HttpResponse::Ok())), let mut srv = init_service(
); App::new().service(web::resource("/test").to(|| HttpResponse::Ok())),
let req = TestRequest::with_uri("/test").to_request(); )
let resp = block_fn(|| srv.call(req)).unwrap(); .await;
assert_eq!(resp.status(), StatusCode::OK); let req = TestRequest::with_uri("/test").to_request();
let resp = srv.call(req).await.unwrap();
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(
App::new() App::new()
.service(web::resource("/test").to(|| HttpResponse::Ok())) .service(web::resource("/test").to(|| HttpResponse::Ok()))
.service( .service(
web::resource("/test2") web::resource("/test2")
.default_service(|r: ServiceRequest| { .default_service(|r: ServiceRequest| {
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() {
let mut srv = block_on(async {
init_service(App::new().data_factory(|| Ok::<_, ()>(10usize)).service( let mut srv =
web::resource("/").to(|_: web::Data<usize>| HttpResponse::Ok()), init_service(App::new().data_factory(|| ok::<_, ()>(10usize)).service(
)); web::resource("/").to(|_: web::Data<usize>| HttpResponse::Ok()),
let req = TestRequest::default().to_request(); ))
let resp = block_on(srv.call(req)).unwrap(); .await;
assert_eq!(resp.status(), StatusCode::OK); let req = TestRequest::default().to_request();
let resp = srv.call(req).await.unwrap();
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()),
)); ))
let req = TestRequest::default().to_request(); .await;
let resp = block_on(srv.call(req)).unwrap(); let req = TestRequest::default().to_request();
assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR); let resp = srv.call(req).await.unwrap();
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,112 +576,141 @@ 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 {
App::new() let mut srv =
.wrap(md) init_service(
.route("/test", web::get().to(|| HttpResponse::Ok())), App::new()
); .wrap(DefaultHeaders::new().header(
let req = TestRequest::with_uri("/test").to_request(); header::CONTENT_TYPE,
let resp = call_service(&mut srv, req); HeaderValue::from_static("0001"),
assert_eq!(resp.status(), StatusCode::OK); ))
assert_eq!( .route("/test", web::get().to(|| HttpResponse::Ok())),
resp.headers().get(header::CONTENT_TYPE).unwrap(), )
HeaderValue::from_static("0001") .await;
); let req = TestRequest::with_uri("/test").to_request();
let resp = call_service(&mut srv, req).await;
assert_eq!(resp.status(), StatusCode::OK);
assert_eq!(
resp.headers().get(header::CONTENT_TYPE).unwrap(),
HeaderValue::from_static("0001")
);
})
} }
#[test] #[test]
fn test_router_wrap() { fn test_router_wrap() {
let mut srv = init_service( block_on(async {
App::new() let mut srv =
.route("/test", web::get().to(|| HttpResponse::Ok())) init_service(
.wrap(md), App::new()
); .route("/test", web::get().to(|| HttpResponse::Ok()))
let req = TestRequest::with_uri("/test").to_request(); .wrap(DefaultHeaders::new().header(
let resp = call_service(&mut srv, req); header::CONTENT_TYPE,
assert_eq!(resp.status(), StatusCode::OK); HeaderValue::from_static("0001"),
assert_eq!( )),
resp.headers().get(header::CONTENT_TYPE).unwrap(), )
HeaderValue::from_static("0001") .await;
); let req = TestRequest::with_uri("/test").to_request();
let resp = call_service(&mut srv, req).await;
assert_eq!(resp.status(), StatusCode::OK);
assert_eq!(
resp.headers().get(header::CONTENT_TYPE).unwrap(),
HeaderValue::from_static("0001")
);
})
} }
#[test] #[test]
fn test_wrap_fn() { fn test_wrap_fn() {
let mut srv = init_service( block_on(async {
App::new() let mut srv = init_service(
.wrap_fn(|req, srv| { App::new()
srv.call(req).map(|mut res| { .wrap_fn(|req, srv| {
res.headers_mut().insert( let fut = srv.call(req);
header::CONTENT_TYPE, async move {
HeaderValue::from_static("0001"), let mut res = fut.await?;
); res.headers_mut().insert(
res header::CONTENT_TYPE,
HeaderValue::from_static("0001"),
);
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() {
let mut srv = init_service( block_on(async {
App::new() let mut srv = init_service(
.route("/test", web::get().to(|| HttpResponse::Ok())) App::new()
.wrap_fn(|req, srv| { .route("/test", web::get().to(|| HttpResponse::Ok()))
srv.call(req).map(|mut res| { .wrap_fn(|req, srv| {
res.headers_mut().insert( let fut = srv.call(req);
header::CONTENT_TYPE, async {
HeaderValue::from_static("0001"), let mut res = fut.await?;
); res.headers_mut().insert(
res header::CONTENT_TYPE,
}) HeaderValue::from_static("0001"),
}), );
); Ok(res)
let req = TestRequest::with_uri("/test").to_request(); }
let resp = call_service(&mut srv, req); }),
assert_eq!(resp.status(), StatusCode::OK); )
assert_eq!( .await;
resp.headers().get(header::CONTENT_TYPE).unwrap(), let req = TestRequest::with_uri("/test").to_request();
HeaderValue::from_static("0001") let resp = call_service(&mut srv, req).await;
); assert_eq!(resp.status(), StatusCode::OK);
assert_eq!(
resp.headers().get(header::CONTENT_TYPE).unwrap(),
HeaderValue::from_static("0001")
);
})
} }
#[test] #[test]
fn test_external_resource() { fn test_external_resource() {
let mut srv = init_service( block_on(async {
App::new() let mut srv = init_service(
.external_resource("youtube", "https://youtube.com/watch/{video_id}") App::new()
.route( .external_resource("youtube", "https://youtube.com/watch/{video_id}")
"/test", .route(
web::get().to(|req: HttpRequest| { "/test",
HttpResponse::Ok().body(format!( web::get().to(|req: HttpRequest| {
"{}", HttpResponse::Ok().body(format!(
req.url_for("youtube", &["12345"]).unwrap() "{}",
)) req.url_for("youtube", &["12345"]).unwrap()
}), ))
), }),
); ),
let req = TestRequest::with_uri("/test").to_request(); )
let resp = call_service(&mut srv, req); .await;
assert_eq!(resp.status(), StatusCode::OK); let req = TestRequest::with_uri("/test").to_request();
let body = read_body(resp); let resp = call_service(&mut srv, req).await;
assert_eq!(body, Bytes::from_static(b"https://youtube.com/watch/12345")); assert_eq!(resp.status(), StatusCode::OK);
let body = read_body(resp).await;
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() {
let cfg = |cfg: &mut ServiceConfig| { block_on(async {
cfg.data(10usize); let cfg = |cfg: &mut ServiceConfig| {
}; 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()),
)); ))
let req = TestRequest::default().to_request(); .await;
let resp = block_on(srv.call(req)).unwrap(); let req = TestRequest::default().to_request();
assert_eq!(resp.status(), StatusCode::OK); let resp = srv.call(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::OK);
})
} }
// #[test] // #[test]
@ -298,50 +300,57 @@ mod tests {
#[test] #[test]
fn test_external_resource() { fn test_external_resource() {
let mut srv = init_service( block_on(async {
App::new() let mut srv = init_service(
.configure(|cfg| { App::new()
cfg.external_resource( .configure(|cfg| {
"youtube", cfg.external_resource(
"https://youtube.com/watch/{video_id}", "youtube",
); "https://youtube.com/watch/{video_id}",
}) );
.route( })
"/test", .route(
web::get().to(|req: HttpRequest| { "/test",
HttpResponse::Ok().body(format!( web::get().to(|req: HttpRequest| {
"{}", HttpResponse::Ok().body(format!(
req.url_for("youtube", &["12345"]).unwrap() "{}",
)) req.url_for("youtube", &["12345"]).unwrap()
}), ))
), }),
); ),
let req = TestRequest::with_uri("/test").to_request(); )
let resp = call_service(&mut srv, req); .await;
assert_eq!(resp.status(), StatusCode::OK); let req = TestRequest::with_uri("/test").to_request();
let body = read_body(resp); let resp = call_service(&mut srv, req).await;
assert_eq!(body, Bytes::from_static(b"https://youtube.com/watch/12345")); assert_eq!(resp.status(), StatusCode::OK);
let body = read_body(resp).await;
assert_eq!(body, Bytes::from_static(b"https://youtube.com/watch/12345"));
})
} }
#[test] #[test]
fn test_service() { fn test_service() {
let mut srv = init_service(App::new().configure(|cfg| { block_on(async {
cfg.service( let mut srv = init_service(App::new().configure(|cfg| {
web::resource("/test").route(web::get().to(|| HttpResponse::Created())), cfg.service(
) web::resource("/test")
.route("/index.html", web::get().to(|| HttpResponse::Ok())); .route(web::get().to(|| HttpResponse::Created())),
})); )
.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,85 +143,99 @@ 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()),
)); ))
let req = TestRequest::default().to_request(); .await;
let resp = block_on(srv.call(req)).unwrap(); let req = TestRequest::default().to_request();
assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR); let resp = srv.call(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
})
} }
#[test] #[test]
fn test_register_data_extractor() { fn test_register_data_extractor() {
let mut srv = block_on(async {
init_service(App::new().register_data(Data::new(10usize)).service( let mut srv =
web::resource("/").to(|_: web::Data<usize>| HttpResponse::Ok()), init_service(App::new().register_data(Data::new(10usize)).service(
)); 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()),
)); ))
let req = TestRequest::default().to_request(); .await;
let resp = block_on(srv.call(req)).unwrap(); let req = TestRequest::default().to_request();
assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR); let resp = srv.call(req).await.unwrap();
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(
let _ = data.clone(); |data: web::Data<usize>| {
HttpResponse::Ok() let _ = data.clone();
}), 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
let mut srv = init_service( let mut srv = init_service(
App::new().service( App::new().service(
web::resource("/") web::resource("/")
.data(10u32) .data(10u32)
.route(web::get().to(|_: web::Data<usize>| HttpResponse::Ok())), .route(web::get().to(|_: web::Data<usize>| HttpResponse::Ok())),
), ),
); )
let req = TestRequest::default().to_request(); .await;
let resp = block_on(srv.call(req)).unwrap(); let req = TestRequest::default().to_request();
assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR); let resp = srv.call(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
})
} }
#[test] #[test]
fn test_override_data() { fn test_override_data() {
let mut srv = init_service(App::new().data(1usize).service( block_on(async {
web::resource("/").data(10usize).route(web::get().to( let mut srv = init_service(App::new().data(1usize).service(
|data: web::Data<usize>| { web::resource("/").data(10usize).route(web::get().to(
assert_eq!(*data, 10); |data: web::Data<usize>| {
let _ = data.clone(); assert_eq!(*data, 10);
HttpResponse::Ok() let _ = data.clone();
}, 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) .then(|r| match r {
.into_future() Ok(v) => ok(Some(v)),
.then(|r| match r { Err(e) => {
Ok(v) => future::ok(Some(v)), log::debug!("Error for Option<T> extractor: {}", e.into());
Err(e) => { ok(None)
log::debug!("Error for Option<T> extractor: {}", e.into()); }
future::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) .then(|res| match res {
.into_future() Ok(v) => ok(Ok(v)),
.then(|res| match res { Err(e) => ok(Err(e)),
Ok(v) => ok(Ok(v)), })
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();
let enc = if let Some(enc) = resp.response().extensions().get::<Enc>() { match futures::ready!(this.fut.poll(cx)) {
enc.0 Ok(resp) => {
} else { let enc = if let Some(enc) = resp.response().extensions().get::<Enc>() {
self.encoding enc.0
}; } else {
*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( ))
DefaultHeaders::new() };
let mut mw = 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,66 +468,73 @@ mod tests {
#[test] #[test]
fn test_app_data() { fn test_app_data() {
let mut srv = init_service(App::new().data(10usize).service( block_on(async {
web::resource("/").to(|req: HttpRequest| { let mut srv = init_service(App::new().data(10usize).service(
if req.app_data::<usize>().is_some() { web::resource("/").to(|req: HttpRequest| {
HttpResponse::Ok() if req.app_data::<usize>().is_some() {
} else { HttpResponse::Ok()
HttpResponse::BadRequest() } else {
} 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(
web::resource("/").to(|req: HttpRequest| { web::resource("/").to(|req: HttpRequest| {
if req.app_data::<usize>().is_some() { if req.app_data::<usize>().is_some() {
HttpResponse::Ok() HttpResponse::Ok()
} else { } else {
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() {
struct Tracker { block_on(async {
pub dropped: bool, struct Tracker {
} pub dropped: bool,
struct Foo { }
tracker: Rc<RefCell<Tracker>>, struct Foo {
} tracker: Rc<RefCell<Tracker>>,
impl Drop for Foo { }
fn drop(&mut self) { impl Drop for Foo {
self.tracker.borrow_mut().dropped = true; fn drop(&mut self) {
self.tracker.borrow_mut().dropped = true;
}
} }
}
let tracker = Rc::new(RefCell::new(Tracker { dropped: false })); let tracker = Rc::new(RefCell::new(Tracker { dropped: false }));
{ {
let tracker2 = Rc::clone(&tracker); let tracker2 = Rc::clone(&tracker);
let mut srv = init_service(App::new().data(10u32).service( let mut srv = init_service(App::new().data(10u32).service(
web::resource("/").to(move |req: HttpRequest| { web::resource("/").to(move |req: HttpRequest| {
req.extensions_mut().insert(Foo { req.extensions_mut().insert(Foo {
tracker: Rc::clone(&tracker2), tracker: Rc::clone(&tracker2),
}); });
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,178 +642,210 @@ 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 = init_service( block_on(async {
App::new().service( let mut srv = init_service(
web::resource("/test") App::new().service(
.name("test") web::resource("/test")
.wrap(md) .name("test")
.route(web::get().to(|| HttpResponse::Ok())), .wrap(DefaultHeaders::new().header(
), header::CONTENT_TYPE,
); HeaderValue::from_static("0001"),
let req = TestRequest::with_uri("/test").to_request(); ))
let resp = call_service(&mut srv, req); .route(web::get().to(|| HttpResponse::Ok())),
assert_eq!(resp.status(), StatusCode::OK); ),
assert_eq!( )
resp.headers().get(header::CONTENT_TYPE).unwrap(), .await;
HeaderValue::from_static("0001") let req = TestRequest::with_uri("/test").to_request();
); let resp = call_service(&mut srv, req).await;
assert_eq!(resp.status(), StatusCode::OK);
assert_eq!(
resp.headers().get(header::CONTENT_TYPE).unwrap(),
HeaderValue::from_static("0001")
);
})
} }
#[test] #[test]
fn test_middleware_fn() { fn test_middleware_fn() {
let mut srv = init_service( block_on(async {
App::new().service( let mut srv = init_service(
web::resource("/test") App::new().service(
.wrap_fn(|req, srv| { web::resource("/test")
srv.call(req).map(|mut res| { .wrap_fn(|req, srv| {
res.headers_mut().insert( let fut = srv.call(req);
header::CONTENT_TYPE, async {
HeaderValue::from_static("0001"), fut.await.map(|mut res| {
); res.headers_mut().insert(
res header::CONTENT_TYPE,
HeaderValue::from_static("0001"),
);
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 {
let req = TestRequest::with_uri("/test").to_request(); delay_for(Duration::from_millis(100)).await;
let resp = call_service(&mut srv, req); Ok::<_, Error>(HttpResponse::Ok())
assert_eq!(resp.status(), StatusCode::OK); }
}),
))
.await;
let req = TestRequest::with_uri("/test").to_request();
let resp = call_service(&mut srv, req).await;
assert_eq!(resp.status(), StatusCode::OK);
})
} }
#[test] #[test]
fn test_default_resource() { fn test_default_resource() {
let mut srv = init_service( block_on(async {
App::new() let mut srv = init_service(
.service( App::new()
web::resource("/test").route(web::get().to(|| HttpResponse::Ok())), .service(
) web::resource("/test")
.default_service(|r: ServiceRequest| { .route(web::get().to(|| HttpResponse::Ok())),
r.into_response(HttpResponse::BadRequest()) )
}),
);
let req = TestRequest::with_uri("/test").to_request();
let resp = call_service(&mut srv, req);
assert_eq!(resp.status(), StatusCode::OK);
let req = TestRequest::with_uri("/test")
.method(Method::POST)
.to_request();
let resp = call_service(&mut srv, req);
assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED);
let mut srv = init_service(
App::new().service(
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 resp = call_service(&mut srv, req).await;
assert_eq!(resp.status(), StatusCode::OK);
let req = TestRequest::with_uri("/test").to_request(); let req = TestRequest::with_uri("/test")
let resp = call_service(&mut srv, req); .method(Method::POST)
assert_eq!(resp.status(), StatusCode::OK); .to_request();
let resp = call_service(&mut srv, req).await;
assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED);
let req = TestRequest::with_uri("/test") let mut srv = init_service(
.method(Method::POST) App::new().service(
.to_request(); web::resource("/test")
let resp = call_service(&mut srv, req); .route(web::get().to(|| HttpResponse::Ok()))
assert_eq!(resp.status(), StatusCode::BAD_REQUEST); .default_service(|r: ServiceRequest| {
ok(r.into_response(HttpResponse::BadRequest()))
}),
),
)
.await;
let req = TestRequest::with_uri("/test").to_request();
let resp = call_service(&mut srv, req).await;
assert_eq!(resp.status(), StatusCode::OK);
let req = TestRequest::with_uri("/test")
.method(Method::POST)
.to_request();
let resp = call_service(&mut srv, req).await;
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
})
} }
#[test] #[test]
fn test_resource_guards() { fn test_resource_guards() {
let mut srv = init_service( block_on(async {
App::new() let mut srv = init_service(
.service( App::new()
web::resource("/test/{p}") .service(
.guard(guard::Get()) web::resource("/test/{p}")
.to(|| HttpResponse::Ok()), .guard(guard::Get())
) .to(|| HttpResponse::Ok()),
.service( )
web::resource("/test/{p}") .service(
.guard(guard::Put()) web::resource("/test/{p}")
.to(|| HttpResponse::Created()), .guard(guard::Put())
) .to(|| HttpResponse::Created()),
.service( )
web::resource("/test/{p}") .service(
.guard(guard::Delete()) web::resource("/test/{p}")
.to(|| HttpResponse::NoContent()), .guard(guard::Delete())
), .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() {
let mut srv = init_service( block_on(async {
App::new() let mut srv = init_service(
.data(1.0f64) App::new()
.data(1usize) .data(1.0f64)
.register_data(web::Data::new('-')) .data(1usize)
.service( .register_data(web::Data::new('-'))
web::resource("/test") .service(
.data(10usize) web::resource("/test")
.register_data(web::Data::new('*')) .data(10usize)
.guard(guard::Get()) .register_data(web::Data::new('*'))
.to( .guard(guard::Get())
|data1: web::Data<usize>, .to(
data2: web::Data<char>, |data1: web::Data<usize>,
data3: web::Data<f64>| { data2: web::Data<char>,
assert_eq!(*data1, 10); data3: web::Data<f64>| {
assert_eq!(*data2, '*'); assert_eq!(*data1, 10);
assert_eq!(*data3, 1.0); assert_eq!(*data2, '*');
HttpResponse::Ok() assert_eq!(*data3, 1.0);
}, HttpResponse::Ok()
), },
), ),
); ),
)
.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,97 +377,85 @@ 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())),
)
}
}
impl<T> Responder for InternalError<T> impl<T> Responder for InternalError<T>
where 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,26 +472,31 @@ pub(crate) mod tests {
#[test] #[test]
fn test_option_responder() { fn test_option_responder() {
let mut srv = init_service( block_on(async {
App::new() let mut srv = init_service(
.service(web::resource("/none").to(|| -> Option<&'static str> { None })) App::new()
.service(web::resource("/some").to(|| Some("some"))), .service(
); web::resource("/none").to(|| -> Option<&'static str> { None }),
)
.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)) => {
let bytes: Bytes = b.clone().into(); let bytes: Bytes = b.clone().into();
assert_eq!(bytes, Bytes::from_static(b"some")); assert_eq!(bytes, Bytes::from_static(b"some"));
}
_ => panic!(),
} }
_ => panic!(), })
}
} }
pub(crate) trait BodyTest { pub(crate) trait BodyTest {
@ -526,142 +527,155 @@ pub(crate) mod tests {
#[test] #[test]
fn test_responder() { fn test_responder() {
let req = TestRequest::default().to_http_request(); block_on(async {
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!(
resp.headers().get(CONTENT_TYPE).unwrap(), resp.headers().get(CONTENT_TYPE).unwrap(),
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!(
resp.headers().get(CONTENT_TYPE).unwrap(), resp.headers().get(CONTENT_TYPE).unwrap(),
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!(
resp.headers().get(CONTENT_TYPE).unwrap(), resp.headers().get(CONTENT_TYPE).unwrap(),
HeaderValue::from_static("text/plain; charset=utf-8") HeaderValue::from_static("text/plain; charset=utf-8")
); );
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!(
resp.headers().get(CONTENT_TYPE).unwrap(), resp.headers().get(CONTENT_TYPE).unwrap(),
HeaderValue::from_static("text/plain; charset=utf-8") HeaderValue::from_static("text/plain; charset=utf-8")
); );
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!(
resp.headers().get(CONTENT_TYPE).unwrap(), resp.headers().get(CONTENT_TYPE).unwrap(),
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();
assert_eq!(resp.status(), StatusCode::OK);
assert_eq!(resp.body().bin_ref(), b"test");
assert_eq!(
resp.headers().get(CONTENT_TYPE).unwrap(),
HeaderValue::from_static("application/octet-stream")
);
// InternalError
let resp: HttpResponse =
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::OK);
assert_eq!(resp.body().bin_ref(), b"test");
assert_eq!(
resp.headers().get(CONTENT_TYPE).unwrap(),
HeaderValue::from_static("application/octet-stream")
);
// InternalError
let resp: HttpResponse =
error::InternalError::new("err", StatusCode::BAD_REQUEST)
.respond_to(&req)
.await
.unwrap();
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
})
} }
#[test] #[test]
fn test_result_responder() { fn test_result_responder() {
let req = TestRequest::default().to_http_request(); block_on(async {
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)
assert_eq!(resp.status(), StatusCode::OK); .await
assert_eq!(resp.body().bin_ref(), b"test"); .unwrap();
assert_eq!( assert_eq!(resp.status(), StatusCode::OK);
resp.headers().get(CONTENT_TYPE).unwrap(), assert_eq!(resp.body().bin_ref(), b"test");
HeaderValue::from_static("text/plain; charset=utf-8") assert_eq!(
); resp.headers().get(CONTENT_TYPE).unwrap(),
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,
); ))
assert!(res.is_err()); .respond_to(&req)
.await;
assert!(res.is_err());
})
} }
#[test] #[test]
fn test_custom_responder() { fn test_custom_responder() {
let req = TestRequest::default().to_http_request(); block_on(async {
let res = block_on( let req = TestRequest::default().to_http_request();
"test" let res = "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);
assert_eq!(res.body().bin_ref(), b"test"); assert_eq!(res.body().bin_ref(), b"test");
assert_eq!( assert_eq!(
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() {
let req = TestRequest::default().to_http_request(); block_on(async {
let res = let req = TestRequest::default().to_http_request();
block_on(("test".to_string(), StatusCode::BAD_REQUEST).respond_to(&req)) let res = ("test".to_string(), StatusCode::BAD_REQUEST)
.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");
assert_eq!( assert_eq!(
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(|result| match result {
.map_err(|_| ()) Ok(service) => {
.and_then(|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,68 +398,75 @@ mod tests {
#[test] #[test]
fn test_route() { fn test_route() {
let mut srv = init_service( block_on(async {
App::new() let mut srv = init_service(
.service( App::new()
web::resource("/test") .service(
.route(web::get().to(|| HttpResponse::Ok())) web::resource("/test")
.route(web::put().to(|| { .route(web::get().to(|| HttpResponse::Ok()))
Err::<HttpResponse, _>(error::ErrorBadRequest("err")) .route(web::put().to(|| {
}))
.route(web::post().to_async(|| {
sleep(Duration::from_millis(100))
.then(|_| HttpResponse::Created())
}))
.route(web::delete().to_async(|| {
sleep(Duration::from_millis(100)).then(|_| {
Err::<HttpResponse, _>(error::ErrorBadRequest("err")) Err::<HttpResponse, _>(error::ErrorBadRequest("err"))
}) }))
})), .route(web::post().to_async(|| {
) async {
.service(web::resource("/json").route(web::get().to_async(|| { delay_for(Duration::from_millis(100)).await;
sleep(Duration::from_millis(25)).then(|_| { Ok::<_, Error>(HttpResponse::Created())
Ok::<_, crate::Error>(web::Json(MyObject { }
name: "test".to_string(), }))
})) .route(web::delete().to_async(|| {
}) async {
}))), delay_for(Duration::from_millis(100)).await;
); Err::<HttpResponse, _>(error::ErrorBadRequest("err"))
}
})),
)
.service(web::resource("/json").route(web::get().to_async(|| {
async {
delay_for(Duration::from_millis(25)).await;
Ok::<_, Error>(web::Json(MyObject {
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\"}"));
})
} }
} }

File diff suppressed because it is too large Load diff

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() {
let mut srv = init_service( block_on(async {
App::new().service(web::service("/test").name("test").finish( let mut srv = init_service(
|req: ServiceRequest| req.into_response(HttpResponse::Ok().finish()), App::new().service(web::service("/test").name("test").finish(
)), |req: ServiceRequest| {
); ok(req.into_response(HttpResponse::Ok().finish()))
let req = TestRequest::with_uri("/test").to_request(); },
let resp = call_service(&mut srv, req); )),
assert_eq!(resp.status(), http::StatusCode::OK); )
.await;
let req = TestRequest::with_uri("/test").to_request();
let resp = srv.call(req).await.unwrap();
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()))
); },
let req = TestRequest::with_uri("/test") ),
.method(http::Method::PUT) ))
.to_request(); .await;
let resp = call_service(&mut srv, req); let req = TestRequest::with_uri("/test")
assert_eq!(resp.status(), http::StatusCode::NOT_FOUND); .method(http::Method::PUT)
.to_request();
let resp = srv.call(req).await.unwrap();
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,74 +507,82 @@ mod tests {
#[test] #[test]
fn test_basics() { fn test_basics() {
let req = TestRequest::with_hdr(header::ContentType::json()) block_on(async {
.version(Version::HTTP_2) let req = TestRequest::with_hdr(header::ContentType::json())
.set(header::Date(SystemTime::now().into())) .version(Version::HTTP_2)
.param("test", "123") .set(header::Date(SystemTime::now().into()))
.data(10u32) .param("test", "123")
.to_http_request(); .data(10u32)
assert!(req.headers().contains_key(header::CONTENT_TYPE)); .to_http_request();
assert!(req.headers().contains_key(header::DATE)); assert!(req.headers().contains_key(header::CONTENT_TYPE));
assert_eq!(&req.match_info()["test"], "123"); assert!(req.headers().contains_key(header::DATE));
assert_eq!(req.version(), Version::HTTP_2); assert_eq!(&req.match_info()["test"], "123");
let data = req.get_app_data::<u32>().unwrap(); assert_eq!(req.version(), Version::HTTP_2);
assert!(req.get_app_data::<u64>().is_none()); let data = req.get_app_data::<u32>().unwrap();
assert_eq!(*data, 10); assert!(req.get_app_data::<u64>().is_none());
assert_eq!(*data.get_ref(), 10); assert_eq!(*data, 10);
assert_eq!(*data.get_ref(), 10);
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() {
let mut app = init_service( block_on(async {
App::new().service( let mut app = init_service(
web::resource("/index.html") App::new().service(
.route(web::put().to(|| HttpResponse::Ok().body("put!"))) web::resource("/index.html")
.route(web::patch().to(|| HttpResponse::Ok().body("patch!"))) .route(web::put().to(|| HttpResponse::Ok().body("put!")))
.route(web::delete().to(|| HttpResponse::Ok().body("delete!"))), .route(web::patch().to(|| HttpResponse::Ok().body("patch!")))
), .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()
.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, 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() {
let mut app = init_service( block_on(async {
App::new().service( let mut app = init_service(
web::resource("/index.html") App::new().service(
.route(web::post().to(|| HttpResponse::Ok().body("welcome!"))), web::resource("/index.html")
), .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,129 +593,147 @@ 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 {
web::post().to(|person: web::Json<Person>| { let mut app =
HttpResponse::Ok().json(person.into_inner()) init_service(App::new().service(web::resource("/people").route(
}), web::post().to(|person: web::Json<Person>| {
))); 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();
let req = TestRequest::post() let req = TestRequest::post()
.uri("/people") .uri("/people")
.header(header::CONTENT_TYPE, "application/json") .header(header::CONTENT_TYPE, "application/json")
.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 {
web::post().to(|person: web::Form<Person>| { let mut app =
HttpResponse::Ok().json(person.into_inner()) init_service(App::new().service(web::resource("/people").route(
}), web::post().to(|person: web::Form<Person>| {
))); HttpResponse::Ok().json(person.into_inner())
}),
)))
.await;
let payload = Person { let payload = Person {
id: "12345".to_string(), id: "12345".to_string(),
name: "User name".to_string(), name: "User name".to_string(),
}; };
let req = TestRequest::post() let req = TestRequest::post()
.uri("/people") .uri("/people")
.set_form(&payload) .set_form(&payload)
.to_request(); .to_request();
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 {
web::post().to(|person: web::Json<Person>| { let mut app =
HttpResponse::Ok().json(person.into_inner()) init_service(App::new().service(web::resource("/people").route(
}), web::post().to(|person: web::Json<Person>| {
))); HttpResponse::Ok().json(person.into_inner())
}),
)))
.await;
let payload = Person { let payload = Person {
id: "12345".to_string(), id: "12345".to_string(),
name: "User name".to_string(), name: "User name".to_string(),
}; };
let req = TestRequest::post() let req = TestRequest::post()
.uri("/people") .uri("/people")
.set_json(&payload) .set_json(&payload)
.to_request(); .to_request();
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;
.content_type("text/plain")
.body(format!("Async with block value: {}", value)),
Err(_) => panic!("Unexpected"),
})
}
let mut app = init_service( match res? {
App::new().service(web::resource("/index.html").to_async(async_with_block)), Ok(value) => Ok(HttpResponse::Ok()
); .content_type("text/plain")
.body(format!("Async with block value: {}", value))),
let req = TestRequest::post().uri("/index.html").to_request(); Err(_) => panic!("Unexpected"),
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(
let mut app = init_service(App::new().service( App::new()
web::resource("/index.html").to_async(move || { .service(web::resource("/index.html").to_async(async_with_block)),
addr.send(Num(1)).from_err().and_then(|res| { )
if res == 1 { .await;
HttpResponse::Ok()
} else {
HttpResponse::BadRequest()
}
})
}),
));
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());
// }
} }

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(move |res| match res {
.map_err(move |e| { 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())
} }
}) }
.map(Form), Ok(item) => Ok(Form(item)),
) })
.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| {
if (body.len() + chunk.len()) > limit { while let Some(item) = stream.next().await {
Err(UrlencodedError::Overflow { let chunk = item?;
size: body.len() + chunk.len(), if (body.len() + chunk.len()) > limit {
limit, return Err(UrlencodedError::Overflow {
}) size: body.len() + chunk.len(),
} else { limit,
body.extend_from_slice(&chunk); });
Ok(body) } else {
body.extend_from_slice(&chunk);
}
} }
})
.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,20 +380,24 @@ 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(
.header(CONTENT_LENGTH, "11") CONTENT_TYPE,
.set_payload(Bytes::from_static(b"hello=world&counter=123")) "application/x-www-form-urlencoded",
.to_http_parts(); )
.header(CONTENT_LENGTH, "11")
.set_payload(Bytes::from_static(b"hello=world&counter=123"))
.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 {
hello: "world".into(), hello: "world".into(),
counter: 123 counter: 123
} }
); );
})
} }
fn eq(err: UrlencodedError, other: UrlencodedError) -> bool { fn eq(err: UrlencodedError, other: UrlencodedError) -> bool {
@ -410,81 +420,93 @@ 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(
.header(CONTENT_LENGTH, "xxxx") CONTENT_TYPE,
.to_http_parts(); "application/x-www-form-urlencoded",
let info = block_on(UrlEncoded::<Info>::new(&req, &mut pl)); )
assert!(eq(info.err().unwrap(), UrlencodedError::UnknownLength)); .header(CONTENT_LENGTH, "xxxx")
let (req, mut pl) =
TestRequest::with_header(CONTENT_TYPE, "application/x-www-form-urlencoded")
.header(CONTENT_LENGTH, "1000000")
.to_http_parts();
let info = block_on(UrlEncoded::<Info>::new(&req, &mut pl));
assert!(eq(
info.err().unwrap(),
UrlencodedError::Overflow { size: 0, limit: 0 }
));
let (req, mut pl) = TestRequest::with_header(CONTENT_TYPE, "text/plain")
.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::UnknownLength));
let (req, mut pl) = TestRequest::with_header(
CONTENT_TYPE,
"application/x-www-form-urlencoded",
)
.header(CONTENT_LENGTH, "1000000")
.to_http_parts();
let info = UrlEncoded::<Info>::new(&req, &mut pl).await;
assert!(eq(
info.err().unwrap(),
UrlencodedError::Overflow { size: 0, limit: 0 }
));
let (req, mut pl) = TestRequest::with_header(CONTENT_TYPE, "text/plain")
.header(CONTENT_LENGTH, "10")
.to_http_parts();
let info = UrlEncoded::<Info>::new(&req, &mut pl).await;
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(
.header(CONTENT_LENGTH, "11") CONTENT_TYPE,
.set_payload(Bytes::from_static(b"hello=world&counter=123")) "application/x-www-form-urlencoded",
.to_http_parts(); )
.header(CONTENT_LENGTH, "11")
.set_payload(Bytes::from_static(b"hello=world&counter=123"))
.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 {
hello: "world".to_owned(), hello: "world".to_owned(),
counter: 123 counter: 123
} }
); );
let (req, mut pl) = TestRequest::with_header( let (req, mut pl) = TestRequest::with_header(
CONTENT_TYPE, CONTENT_TYPE,
"application/x-www-form-urlencoded; charset=utf-8", "application/x-www-form-urlencoded; charset=utf-8",
) )
.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 {
hello: "world".to_owned(), hello: "world".to_owned(),
counter: 123 counter: 123
} }
); );
})
} }
#[test] #[test]
fn test_responder() { fn test_responder() {
let req = TestRequest::default().to_http_request(); block_on(async {
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(),
HeaderValue::from_static("application/x-www-form-urlencoded") HeaderValue::from_static("application/x-www-form-urlencoded")
); );
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(move |res| match res {
.map_err(move |e| { 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())
} }
}) }
.map(Json), Ok(data) => Ok(Json(data)),
) })
.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) }
} }
}) Ok(serde_json::from_slice::<U>(&body)?)
.and_then(|body| Ok(serde_json::from_slice::<U>(&body)?)); }
self.fut = Some(Box::new(fut)); .boxed_local(),
self.poll() );
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,218 +426,234 @@ mod tests {
#[test] #[test]
fn test_responder() { fn test_responder() {
let req = TestRequest::default().to_http_request(); block_on(async {
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(),
header::HeaderValue::from_static("application/json") header::HeaderValue::from_static("application/json")
); );
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() {
let (req, mut pl) = TestRequest::default() block_on(async {
.header( let (req, mut pl) = TestRequest::default()
header::CONTENT_TYPE, .header(
header::HeaderValue::from_static("application/json"), header::CONTENT_TYPE,
) header::HeaderValue::from_static("application/json"),
.header( )
header::CONTENT_LENGTH, .header(
header::HeaderValue::from_static("16"), header::CONTENT_LENGTH,
) header::HeaderValue::from_static("16"),
.set_payload(Bytes::from_static(b"{\"name\": \"test\"}")) )
.data(JsonConfig::default().limit(10).error_handler(|err, _| { .set_payload(Bytes::from_static(b"{\"name\": \"test\"}"))
let msg = MyObject { .data(JsonConfig::default().limit(10).error_handler(|err, _| {
name: "invalid request".to_string(), let msg = MyObject {
}; name: "invalid request".to_string(),
let resp = HttpResponse::BadRequest() };
.body(serde_json::to_string(&msg).unwrap()); let resp = HttpResponse::BadRequest()
InternalError::from_response(err, resp).into() .body(serde_json::to_string(&msg).unwrap());
})) InternalError::from_response(err, resp).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;
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() {
let (req, mut pl) = TestRequest::default() block_on(async {
.header( let (req, mut pl) = TestRequest::default()
header::CONTENT_TYPE, .header(
header::HeaderValue::from_static("application/json"), header::CONTENT_TYPE,
) header::HeaderValue::from_static("application/json"),
.header( )
header::CONTENT_LENGTH, .header(
header::HeaderValue::from_static("16"), header::CONTENT_LENGTH,
) header::HeaderValue::from_static("16"),
.set_payload(Bytes::from_static(b"{\"name\": \"test\"}")) )
.to_http_parts(); .set_payload(Bytes::from_static(b"{\"name\": \"test\"}"))
.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(),
MyObject { MyObject {
name: "test".to_string() name: "test".to_string()
} }
); );
let (req, mut pl) = TestRequest::default() let (req, mut pl) = TestRequest::default()
.header( .header(
header::CONTENT_TYPE, header::CONTENT_TYPE,
header::HeaderValue::from_static("application/json"), header::HeaderValue::from_static("application/json"),
) )
.header( .header(
header::CONTENT_LENGTH, header::CONTENT_LENGTH,
header::HeaderValue::from_static("16"), header::HeaderValue::from_static("16"),
) )
.set_payload(Bytes::from_static(b"{\"name\": \"test\"}")) .set_payload(Bytes::from_static(b"{\"name\": \"test\"}"))
.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"));
let (req, mut pl) = TestRequest::default() let (req, mut pl) = TestRequest::default()
.header( .header(
header::CONTENT_TYPE, header::CONTENT_TYPE,
header::HeaderValue::from_static("application/json"), header::HeaderValue::from_static("application/json"),
) )
.header( .header(
header::CONTENT_LENGTH, header::CONTENT_LENGTH,
header::HeaderValue::from_static("16"), header::HeaderValue::from_static("16"),
) )
.set_payload(Bytes::from_static(b"{\"name\": \"test\"}")) .set_payload(Bytes::from_static(b"{\"name\": \"test\"}"))
.data( .data(
JsonConfig::default() JsonConfig::default()
.limit(10) .limit(10)
.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() {
let (req, mut pl) = TestRequest::default().to_http_parts(); block_on(async {
let json = block_on(JsonBody::<MyObject>::new(&req, &mut pl, None)); let (req, mut pl) = TestRequest::default().to_http_parts();
assert!(json_eq(json.err().unwrap(), JsonPayloadError::ContentType)); let json = JsonBody::<MyObject>::new(&req, &mut pl, None).await;
assert!(json_eq(json.err().unwrap(), JsonPayloadError::ContentType));
let (req, mut pl) = TestRequest::default() let (req, mut pl) = TestRequest::default()
.header( .header(
header::CONTENT_TYPE,
header::HeaderValue::from_static("application/text"),
)
.to_http_parts();
let json = JsonBody::<MyObject>::new(&req, &mut pl, None).await;
assert!(json_eq(json.err().unwrap(), JsonPayloadError::ContentType));
let (req, mut pl) = TestRequest::default()
.header(
header::CONTENT_TYPE,
header::HeaderValue::from_static("application/json"),
)
.header(
header::CONTENT_LENGTH,
header::HeaderValue::from_static("10000"),
)
.to_http_parts();
let json = JsonBody::<MyObject>::new(&req, &mut pl, None)
.limit(100)
.await;
assert!(json_eq(json.err().unwrap(), JsonPayloadError::Overflow));
let (req, mut pl) = TestRequest::default()
.header(
header::CONTENT_TYPE,
header::HeaderValue::from_static("application/json"),
)
.header(
header::CONTENT_LENGTH,
header::HeaderValue::from_static("16"),
)
.set_payload(Bytes::from_static(b"{\"name\": \"test\"}"))
.to_http_parts();
let json = JsonBody::<MyObject>::new(&req, &mut pl, None).await;
assert_eq!(
json.ok().unwrap(),
MyObject {
name: "test".to_owned()
}
);
})
}
#[test]
fn test_with_json_and_bad_content_type() {
block_on(async {
let (req, mut pl) = TestRequest::with_header(
header::CONTENT_TYPE, header::CONTENT_TYPE,
header::HeaderValue::from_static("application/text"), header::HeaderValue::from_static("text/plain"),
)
.to_http_parts();
let json = block_on(JsonBody::<MyObject>::new(&req, &mut pl, None));
assert!(json_eq(json.err().unwrap(), JsonPayloadError::ContentType));
let (req, mut pl) = TestRequest::default()
.header(
header::CONTENT_TYPE,
header::HeaderValue::from_static("application/json"),
)
.header(
header::CONTENT_LENGTH,
header::HeaderValue::from_static("10000"),
)
.to_http_parts();
let json = block_on(JsonBody::<MyObject>::new(&req, &mut pl, None).limit(100));
assert!(json_eq(json.err().unwrap(), JsonPayloadError::Overflow));
let (req, mut pl) = TestRequest::default()
.header(
header::CONTENT_TYPE,
header::HeaderValue::from_static("application/json"),
) )
.header( .header(
header::CONTENT_LENGTH, header::CONTENT_LENGTH,
header::HeaderValue::from_static("16"), header::HeaderValue::from_static("16"),
) )
.set_payload(Bytes::from_static(b"{\"name\": \"test\"}")) .set_payload(Bytes::from_static(b"{\"name\": \"test\"}"))
.data(JsonConfig::default().limit(4096))
.to_http_parts(); .to_http_parts();
let json = block_on(JsonBody::<MyObject>::new(&req, &mut pl, None)); let s = Json::<MyObject>::from_request(&req, &mut pl).await;
assert_eq!( assert!(s.is_err())
json.ok().unwrap(), })
MyObject {
name: "test".to_owned()
}
);
}
#[test]
fn test_with_json_and_bad_content_type() {
let (req, mut pl) = TestRequest::with_header(
header::CONTENT_TYPE,
header::HeaderValue::from_static("text/plain"),
)
.header(
header::CONTENT_LENGTH,
header::HeaderValue::from_static("16"),
)
.set_payload(Bytes::from_static(b"{\"name\": \"test\"}"))
.data(JsonConfig::default().limit(4096))
.to_http_parts();
let s = block_on(Json::<MyObject>::from_request(&req, &mut pl));
assert!(s.is_err())
} }
#[test] #[test]
fn test_with_json_and_good_custom_content_type() { fn test_with_json_and_good_custom_content_type() {
let (req, mut pl) = TestRequest::with_header( block_on(async {
header::CONTENT_TYPE, let (req, mut pl) = TestRequest::with_header(
header::HeaderValue::from_static("text/plain"), header::CONTENT_TYPE,
) header::HeaderValue::from_static("text/plain"),
.header( )
header::CONTENT_LENGTH, .header(
header::HeaderValue::from_static("16"), header::CONTENT_LENGTH,
) header::HeaderValue::from_static("16"),
.set_payload(Bytes::from_static(b"{\"name\": \"test\"}")) )
.data(JsonConfig::default().content_type(|mime: mime::Mime| { .set_payload(Bytes::from_static(b"{\"name\": \"test\"}"))
mime.type_() == mime::TEXT && mime.subtype() == mime::PLAIN .data(JsonConfig::default().content_type(|mime: mime::Mime| {
})) mime.type_() == mime::TEXT && mime.subtype() == mime::PLAIN
.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() {
let (req, mut pl) = TestRequest::with_header( block_on(async {
header::CONTENT_TYPE, let (req, mut pl) = TestRequest::with_header(
header::HeaderValue::from_static("text/html"), header::CONTENT_TYPE,
) header::HeaderValue::from_static("text/html"),
.header( )
header::CONTENT_LENGTH, .header(
header::HeaderValue::from_static("16"), header::CONTENT_LENGTH,
) header::HeaderValue::from_static("16"),
.set_payload(Bytes::from_static(b"{\"name\": \"test\"}")) )
.data(JsonConfig::default().content_type(|mime: mime::Mime| { .set_payload(Bytes::from_static(b"{\"name\": \"test\"}"))
mime.type_() == mime::TEXT && mime.subtype() == mime::PLAIN .data(JsonConfig::default().content_type(|mime: mime::Mime| {
})) mime.type_() == mime::TEXT && mime.subtype() == mime::PLAIN
.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,21 +170,23 @@ where
.map(|c| c.ehandler.clone()) .map(|c| c.ehandler.clone())
.unwrap_or(None); .unwrap_or(None);
de::Deserialize::deserialize(PathDeserializer::new(req.match_info())) ready(
.map(|inner| Path { inner }) de::Deserialize::deserialize(PathDeserializer::new(req.match_info()))
.map_err(move |e| { .map(|inner| Path { inner })
log::debug!( .map_err(move |e| {
"Failed during Path extractor deserialization. \ log::debug!(
Request path: {:?}", "Failed during Path extractor deserialization. \
req.path() Request path: {:?}",
); req.path()
if let Some(error_handler) = error_handler { );
let e = PathError::Deserialize(e); if let Some(error_handler) = error_handler {
(error_handler)(e, req) let e = PathError::Deserialize(e);
} else { (error_handler)(e, req)
ErrorNotFound(e) } else {
} ErrorNotFound(e)
}) }
}),
)
} }
} }
@ -268,100 +271,116 @@ mod tests {
#[test] #[test]
fn test_extract_path_single() { fn test_extract_path_single() {
let resource = ResourceDef::new("/{value}/"); block_on(async {
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() {
let resource = ResourceDef::new("/{key}/{value}/"); block_on(async {
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
assert_eq!((res.0).0, "name"); .unwrap();
assert_eq!((res.0).1, "user1"); assert_eq!((res.0).0, "name");
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() {
let mut req = TestRequest::with_uri("/name/user1/?id=test").to_srv_request(); block_on(async {
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();
assert_eq!(s.value, "user2"); assert_eq!(s.value, "user2");
assert_eq!( assert_eq!(
format!("{}, {:?}", s, s), format!("{}, {:?}", s, s),
"MyStruct(name, user2), MyStruct { key: \"name\", value: \"user2\" }" "MyStruct(name, user2), MyStruct { key: \"name\", value: \"user2\" }"
); );
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)
assert_eq!(s.0, "name"); .await
assert_eq!(s.1, "user1"); .unwrap();
assert_eq!(s.0, "name");
assert_eq!(s.1, "user1");
let mut req = TestRequest::with_uri("/name/32/").to_srv_request(); let mut req = TestRequest::with_uri("/name/32/").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 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)
assert_eq!(s.0, "name"); .await
assert_eq!(s.1, 32); .unwrap();
assert_eq!(s.0, "name");
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)
assert_eq!(res[0], "name".to_owned()); .await
assert_eq!(res[1], "32".to_owned()); .unwrap();
assert_eq!(res[0], "name".to_owned());
assert_eq!(res[1], "32".to_owned());
})
} }
#[test] #[test]
fn test_custom_err_handler() { fn test_custom_err_handler() {
let (req, mut pl) = TestRequest::with_uri("/name/user1/") block_on(async {
.data(PathConfig::default().error_handler(|err, _| { let (req, mut pl) = TestRequest::with_uri("/name/user1/")
error::InternalError::from_response( .data(PathConfig::default().error_handler(|err, _| {
err, error::InternalError::from_response(
HttpResponse::Conflict().finish(), err,
) HttpResponse::Conflict().finish(),
.into() )
})) .into()
.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)
let res: HttpResponse = s.into(); .await
.unwrap_err();
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,33 +203,34 @@ 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::A(Box::new( Either::Left(
HttpMessageBody::new(req, payload) async move {
.limit(limit) let body = fut.await?;
.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"))? .to_owned())
.to_owned()) } else {
} else { Ok(encoding
Ok(encoding .decode_without_bom_handling_and_without_replacement(&body)
.decode_without_bom_handling_and_without_replacement(&body) .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,42 +254,46 @@ mod tests {
#[test] #[test]
fn test_request_extract() { fn test_request_extract() {
let req = TestRequest::with_uri("/name/user1/").to_srv_request(); block_on(async {
let (req, mut pl) = req.into_parts(); let req = TestRequest::with_uri("/name/user1/").to_srv_request();
assert!(Query::<Id>::from_request(&req, &mut pl).is_err()); let (req, mut pl) = req.into_parts();
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() {
let req = TestRequest::with_uri("/name/user1/") block_on(async {
.data(QueryConfig::default().error_handler(|e, _| { let req = TestRequest::with_uri("/name/user1/")
let resp = HttpResponse::UnprocessableEntity().finish(); .data(QueryConfig::default().error_handler(|e, _| {
InternalError::from_response(e, resp).into() let resp = HttpResponse::UnprocessableEntity().finish();
})) InternalError::from_response(e, resp).into()
.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!(
query query
.unwrap_err() .unwrap_err()
.as_response_error() .as_response_error()
.error_response() .error_response()
.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,83 +136,72 @@ 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() {
let mut req = TestRequest::default() block_on(async {
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\
industry. Lorem Ipsum has been the industry's standard dummy\n\ industry. Lorem Ipsum has been the industry's standard dummy\n\
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)) => {
assert_eq!(
s,
"Lorem Ipsum is simply dummy text of the printing and typesetting\n"
);
stream
}
_ => unreachable!("error"),
};
let stream = match block_on(stream.into_future()) { let mut stream = Readlines::new(&mut req);
Ok((Some(s), stream)) => { assert_eq!(
assert_eq!( stream.next().await.unwrap().unwrap(),
s, "Lorem Ipsum is simply dummy text of the printing and typesetting\n"
"industry. Lorem Ipsum has been the industry's standard dummy\n" );
);
stream
}
_ => unreachable!("error"),
};
match block_on(stream.into_future()) { assert_eq!(
Ok((Some(s), _)) => { stream.next().await.unwrap().unwrap(),
assert_eq!( "industry. Lorem Ipsum has been the industry's standard dummy\n"
s, );
"Contrary to popular belief, Lorem Ipsum is not simply random text."
); assert_eq!(
} stream.next().await.unwrap().unwrap(),
_ => unreachable!("error"), "Contrary to popular belief, Lorem Ipsum is not simply random text."
} );
})
} }
} }

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);
assert!(response.status().is_success()); let response = client.get(host.clone()).send().await.unwrap();
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);
assert!(response.status().is_success()); let response = client.get(host.clone()).send().await.unwrap();
assert!(response.status().is_success());
});
// stop // stop
let _ = srv.stop(false); let _ = srv.stop(false);

File diff suppressed because it is too large Load diff