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

View File

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

View File

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

View File

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

View File

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

View File

@ -13,8 +13,7 @@ categories = ["network-programming", "asynchronous",
"web-programming::websocket"]
license = "MIT/Apache-2.0"
edition = "2018"
# workspace = ".."
workspace = ".."
[package.metadata.docs.rs]
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"
serde_derive = "1.0"
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.
pub fn h1<F, P, B>(self, service: F) -> H1Service<T, P, S, B, X, U>
where
B: MessageBody + 'static,
B: MessageBody,
F: IntoServiceFactory<S>,
S::Error: Into<Error> + 'static,
S::Error: Into<Error>,
S::InitError: fmt::Debug,
S::Response: Into<Response<B>> + 'static,
<S::Service as Service>::Future: 'static,
S::Response: Into<Response<B>>,
{
let cfg = ServiceConfig::new(
self.keep_alive,

View File

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

View File

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

View File

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

View File

@ -1,6 +1,6 @@
[package]
name = "actix-session"
version = "0.2.0"
version = "0.3.0-alpha.1"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Session for actix web framework."
readme = "README.md"
@ -24,15 +24,15 @@ default = ["cookie-session"]
cookie-session = ["actix-web/secure-cookies"]
[dependencies]
actix-web = "1.0.0"
actix-service = "0.4.1"
actix-web = "2.0.0-alpha.1"
actix-service = "1.0.0-alpha.1"
bytes = "0.4"
derive_more = "0.15.0"
futures = "0.1.25"
hashbrown = "0.5.0"
futures = "0.3.1"
hashbrown = "0.6.3"
serde = "1.0"
serde_json = "1.0"
time = "0.1.42"
[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]
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-test = { version = "0.3.0-alpha.1", features=["openssl"] }
actix-utils = "0.5.0-alpha.1"
@ -75,17 +75,3 @@ rand = "0.7"
tokio-tcp = "0.1"
webpki = { version = "0.21" }
rus-tls = { version = "0.16.0", package="rustls", features = ["dangerous_configuration"] }
[patch.crates-io]
awc = { path = "." }
actix-http = { path = "../actix-http" }
actix-http-test = { path = "../test-server" }
actix-codec = { path = "../../actix-net/actix-codec" }
actix-connect = { path = "../../actix-net/actix-connect" }
actix-rt = { path = "../../actix-net/actix-rt" }
actix-server = { path = "../../actix-net/actix-server" }
actix-server-config = { path = "../../actix-net/actix-server-config" }
actix-service = { path = "../../actix-net/actix-service" }
actix-threadpool = { path = "../../actix-net/actix-threadpool" }
actix-utils = { path = "../../actix-net/actix-utils" }

View File

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

View File

@ -1,5 +1,3 @@
use futures::IntoFuture;
use actix_web::{
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)
}
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);
Ok("Hello world!\r\n")
}
@ -28,7 +26,7 @@ fn main() -> std::io::Result<()> {
App::new()
.wrap(middleware::DefaultHeaders::new().header("X-Version", "0.2"))
.wrap(middleware::Compress::default())
.wrap(middleware::Logger::default())
// .wrap(middleware::Logger::default())
.service(index)
.service(no_params)
.service(

View File

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

View File

@ -1,5 +1,3 @@
use futures::IntoFuture;
use actix_web::{
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)
}
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);
Ok("Hello world!\r\n")
}
@ -29,7 +27,7 @@ fn main() -> std::io::Result<()> {
App::new()
.wrap(middleware::DefaultHeaders::new().header("X-Version", "0.2"))
.wrap(middleware::Compress::default())
.wrap(middleware::Logger::default())
// .wrap(middleware::Logger::default())
.service(index)
.service(no_params)
.service(
@ -38,7 +36,7 @@ fn main() -> std::io::Result<()> {
middleware::DefaultHeaders::new().header("X-Version-R2", "0.3"),
)
.default_service(
web::route().to(|| HttpResponse::MethodNotAllowed()),
web::route().to(|| ok(HttpResponse::MethodNotAllowed())),
)
.route(web::get().to_async(index_async)),
)

View File

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

View File

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

View File

@ -3,7 +3,7 @@ use std::rc::Rc;
use actix_http::Extensions;
use actix_router::ResourceDef;
use actix_service::{boxed, IntoNewService, NewService};
use actix_service::{boxed, IntoServiceFactory, ServiceFactory};
use crate::data::{Data, DataFactory};
use crate::error::Error;
@ -12,7 +12,7 @@ use crate::resource::Resource;
use crate::rmap::ResourceMap;
use crate::route::Route;
use crate::service::{
HttpServiceFactory, ServiceFactory, ServiceFactoryWrapper, ServiceRequest,
AppServiceFactory, HttpServiceFactory, ServiceFactoryWrapper, ServiceRequest,
ServiceResponse,
};
@ -102,11 +102,11 @@ impl AppService {
&mut self,
rdef: ResourceDef,
guards: Option<Vec<Box<dyn Guard>>>,
service: F,
factory: F,
nested: Option<Rc<ResourceMap>>,
) where
F: IntoNewService<S>,
S: NewService<
F: IntoServiceFactory<S>,
S: ServiceFactory<
Config = (),
Request = ServiceRequest,
Response = ServiceResponse,
@ -116,7 +116,7 @@ impl AppService {
{
self.services.push((
rdef,
boxed::new_service(service.into_new_service()),
boxed::factory(factory.into_factory()),
guards,
nested,
));
@ -174,7 +174,7 @@ impl Default for AppConfigInner {
/// to set of external methods. This could help with
/// modularization of big application configuration.
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) external: Vec<ResourceDef>,
}
@ -251,17 +251,19 @@ mod tests {
#[test]
fn test_data() {
block_on(async {
let cfg = |cfg: &mut ServiceConfig| {
cfg.data(10usize);
};
let mut srv =
init_service(App::new().configure(cfg).service(
let mut srv = init_service(App::new().configure(cfg).service(
web::resource("/").to(|_: web::Data<usize>| HttpResponse::Ok()),
));
))
.await;
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);
})
}
// #[test]
@ -298,6 +300,7 @@ mod tests {
#[test]
fn test_external_resource() {
block_on(async {
let mut srv = init_service(
App::new()
.configure(|cfg| {
@ -315,33 +318,39 @@ mod tests {
))
}),
),
);
)
.await;
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);
let body = read_body(resp);
let body = read_body(resp).await;
assert_eq!(body, Bytes::from_static(b"https://youtube.com/watch/12345"));
})
}
#[test]
fn test_service() {
block_on(async {
let mut srv = init_service(App::new().configure(|cfg| {
cfg.service(
web::resource("/test").route(web::get().to(|| HttpResponse::Created())),
web::resource("/test")
.route(web::get().to(|| HttpResponse::Created())),
)
.route("/index.html", web::get().to(|| HttpResponse::Ok()));
}));
}))
.await;
let req = TestRequest::with_uri("/test")
.method(Method::GET)
.to_request();
let resp = call_service(&mut srv, req);
let resp = call_service(&mut srv, req).await;
assert_eq!(resp.status(), StatusCode::CREATED);
let req = TestRequest::with_uri("/index.html")
.method(Method::GET)
.to_request();
let resp = call_service(&mut srv, req);
let resp = call_service(&mut srv, req).await;
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::Extensions;
use futures::future::{err, ok, Ready};
use crate::dev::Payload;
use crate::extract::FromRequest;
@ -101,19 +102,19 @@ impl<T> Clone for Data<T> {
impl<T: 'static> FromRequest for Data<T> {
type Config = ();
type Error = Error;
type Future = Result<Self, Error>;
type Future = Ready<Result<Self, Error>>;
#[inline]
fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
if let Some(st) = req.get_app_data::<T>() {
Ok(st)
ok(st)
} else {
log::debug!(
"Failed to construct App-level Data extractor. \
Request path: {:?}",
req.path()
);
Err(ErrorInternalServerError(
err(ErrorInternalServerError(
"App data is not configured, to configure use App::data()",
))
}
@ -142,56 +143,65 @@ mod tests {
#[test]
fn test_data_extractor() {
let mut srv =
init_service(App::new().data(10usize).service(
block_on(async {
let mut srv = init_service(App::new().data(10usize).service(
web::resource("/").to(|_: web::Data<usize>| HttpResponse::Ok()),
));
))
.await;
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);
let mut srv =
init_service(App::new().data(10u32).service(
let mut srv = init_service(App::new().data(10u32).service(
web::resource("/").to(|_: web::Data<usize>| HttpResponse::Ok()),
));
))
.await;
let req = TestRequest::default().to_request();
let resp = block_on(srv.call(req)).unwrap();
let resp = srv.call(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
})
}
#[test]
fn test_register_data_extractor() {
block_on(async {
let mut srv =
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 resp = block_on(srv.call(req)).unwrap();
let resp = srv.call(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::OK);
let mut srv =
init_service(App::new().register_data(Data::new(10u32)).service(
web::resource("/").to(|_: web::Data<usize>| HttpResponse::Ok()),
));
))
.await;
let req = TestRequest::default().to_request();
let resp = block_on(srv.call(req)).unwrap();
let resp = srv.call(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
})
}
#[test]
fn test_route_data_extractor() {
let mut srv =
init_service(App::new().service(web::resource("/").data(10usize).route(
web::get().to(|data: web::Data<usize>| {
block_on(async {
let mut srv = init_service(App::new().service(
web::resource("/").data(10usize).route(web::get().to(
|data: web::Data<usize>| {
let _ = data.clone();
HttpResponse::Ok()
}),
)));
},
)),
))
.await;
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);
// different type
@ -201,14 +211,17 @@ mod tests {
.data(10u32)
.route(web::get().to(|_: web::Data<usize>| HttpResponse::Ok())),
),
);
)
.await;
let req = TestRequest::default().to_request();
let resp = block_on(srv.call(req)).unwrap();
let resp = srv.call(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
})
}
#[test]
fn test_override_data() {
block_on(async {
let mut srv = init_service(App::new().data(1usize).service(
web::resource("/").data(10usize).route(web::get().to(
|data: web::Data<usize>| {
@ -217,10 +230,12 @@ mod tests {
HttpResponse::Ok()
},
)),
));
))
.await;
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);
})
}
}

View File

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

View File

@ -1,10 +1,14 @@
use std::convert::Infallible;
use std::future::Future;
use std::marker::PhantomData;
use std::pin::Pin;
use std::task::{Context, Poll};
use actix_http::{Error, Response};
use actix_service::{NewService, Service};
use futures::future::{ok, FutureResult};
use futures::{try_ready, Async, Future, IntoFuture, Poll};
use actix_service::{Service, ServiceFactory};
use futures::future::{ok, Ready};
use futures::ready;
use pin_project::pin_project;
use crate::extract::FromRequest;
use crate::request::HttpRequest;
@ -73,14 +77,14 @@ where
type Request = (T, HttpRequest);
type Response = ServiceResponse;
type Error = Infallible;
type Future = HandlerServiceResponse<<R::Future as IntoFuture>::Future>;
type Future = HandlerServiceResponse<R>;
fn poll_ready(&mut self) -> Poll<(), Self::Error> {
Ok(Async::Ready(()))
fn poll_ready(&mut self, _: &mut Context) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
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 {
fut,
req: Some(req),
@ -88,53 +92,48 @@ where
}
}
pub struct HandlerServiceResponse<T> {
fut: T,
#[pin_project]
pub struct HandlerServiceResponse<T: Responder> {
#[pin]
fut: T::Future,
req: Option<HttpRequest>,
}
impl<T> Future for HandlerServiceResponse<T>
where
T: Future<Item = Response>,
T::Error: Into<Error>,
{
type Item = ServiceResponse;
type Error = Infallible;
impl<T: Responder> Future for HandlerServiceResponse<T> {
type Output = Result<ServiceResponse, Infallible>;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
match self.fut.poll() {
Ok(Async::Ready(res)) => Ok(Async::Ready(ServiceResponse::new(
self.req.take().unwrap(),
res,
))),
Ok(Async::NotReady) => Ok(Async::NotReady),
Err(e) => {
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
let this = self.project();
match this.fut.poll(cx) {
Poll::Ready(Ok(res)) => {
Poll::Ready(Ok(ServiceResponse::new(this.req.take().unwrap(), res)))
}
Poll::Pending => Poll::Pending,
Poll::Ready(Err(e)) => {
let res: Response = e.into().into();
Ok(Async::Ready(ServiceResponse::new(
self.req.take().unwrap(),
res,
)))
Poll::Ready(Ok(ServiceResponse::new(this.req.take().unwrap(), res)))
}
}
}
}
/// Async handler converter factory
pub trait AsyncFactory<T, R>: Clone + 'static
pub trait AsyncFactory<T, R, O, E>: Clone + 'static
where
R: IntoFuture,
R::Item: Responder,
R::Error: Into<Error>,
R: Future<Output = Result<O, E>>,
O: Responder,
E: Into<Error>,
{
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
F: Fn() -> R + Clone + 'static,
R: IntoFuture,
R::Item: Responder,
R::Error: Into<Error>,
R: Future<Output = Result<O, E>>,
O: Responder,
E: Into<Error>,
{
fn call(&self, _: ()) -> R {
(self)()
@ -142,23 +141,23 @@ where
}
#[doc(hidden)]
pub struct AsyncHandler<F, T, R>
pub struct AsyncHandler<F, T, R, O, E>
where
F: AsyncFactory<T, R>,
R: IntoFuture,
R::Item: Responder,
R::Error: Into<Error>,
F: AsyncFactory<T, R, O, E>,
R: Future<Output = Result<O, E>>,
O: Responder,
E: Into<Error>,
{
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
F: AsyncFactory<T, R>,
R: IntoFuture,
R::Item: Responder,
R::Error: Into<Error>,
F: AsyncFactory<T, R, O, E>,
R: Future<Output = Result<O, E>>,
O: Responder,
E: Into<Error>,
{
pub fn new(hnd: F) -> Self {
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
F: AsyncFactory<T, R>,
R: IntoFuture,
R::Item: Responder,
R::Error: Into<Error>,
F: AsyncFactory<T, R, O, E>,
R: Future<Output = Result<O, E>>,
O: Responder,
E: Into<Error>,
{
fn clone(&self) -> Self {
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
F: AsyncFactory<T, R>,
R: IntoFuture,
R::Item: Responder,
R::Error: Into<Error>,
F: AsyncFactory<T, R, O, E>,
R: Future<Output = Result<O, E>>,
O: Responder,
E: Into<Error>,
{
type Request = (T, HttpRequest);
type Response = ServiceResponse;
type Error = Infallible;
type Future = AsyncHandlerServiceResponse<R::Future>;
type Future = AsyncHandlerServiceResponse<R, O, E>;
fn poll_ready(&mut self) -> Poll<(), Self::Error> {
Ok(Async::Ready(()))
fn poll_ready(&mut self, _: &mut Context) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, (param, req): (T, HttpRequest)) -> Self::Future {
AsyncHandlerServiceResponse {
fut: self.hnd.call(param).into_future(),
fut: self.hnd.call(param),
fut2: None,
req: Some(req),
}
@ -209,56 +208,54 @@ where
}
#[doc(hidden)]
pub struct AsyncHandlerServiceResponse<T>
#[pin_project]
pub struct AsyncHandlerServiceResponse<T, R, E>
where
T: Future,
T::Item: Responder,
T: Future<Output = Result<R, E>>,
R: Responder,
E: Into<Error>,
{
#[pin]
fut: T,
fut2: Option<<<T::Item as Responder>::Future as IntoFuture>::Future>,
#[pin]
fut2: Option<R::Future>,
req: Option<HttpRequest>,
}
impl<T> Future for AsyncHandlerServiceResponse<T>
impl<T, R, E> Future for AsyncHandlerServiceResponse<T, R, E>
where
T: Future,
T::Item: Responder,
T::Error: Into<Error>,
T: Future<Output = Result<R, E>>,
R: Responder,
E: Into<Error>,
{
type Item = ServiceResponse;
type Error = Infallible;
type Output = Result<ServiceResponse, Infallible>;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
if let Some(ref mut fut) = self.fut2 {
return match fut.poll() {
Ok(Async::Ready(res)) => Ok(Async::Ready(ServiceResponse::new(
self.req.take().unwrap(),
res,
))),
Ok(Async::NotReady) => Ok(Async::NotReady),
Err(e) => {
fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
let this = self.as_mut().project();
if let Some(fut) = this.fut2.as_pin_mut() {
return match fut.poll(cx) {
Poll::Ready(Ok(res)) => {
Poll::Ready(Ok(ServiceResponse::new(this.req.take().unwrap(), res)))
}
Poll::Pending => Poll::Pending,
Poll::Ready(Err(e)) => {
let res: Response = e.into().into();
Ok(Async::Ready(ServiceResponse::new(
self.req.take().unwrap(),
res,
)))
Poll::Ready(Ok(ServiceResponse::new(this.req.take().unwrap(), res)))
}
};
}
match self.fut.poll() {
Ok(Async::Ready(res)) => {
self.fut2 =
Some(res.respond_to(self.req.as_ref().unwrap()).into_future());
self.poll()
match this.fut.poll(cx) {
Poll::Ready(Ok(res)) => {
let fut = res.respond_to(this.req.as_ref().unwrap());
self.as_mut().project().fut2.set(Some(fut));
self.poll(cx)
}
Ok(Async::NotReady) => Ok(Async::NotReady),
Err(e) => {
Poll::Pending => Poll::Pending,
Poll::Ready(Err(e)) => {
let res: Response = e.into().into();
Ok(Async::Ready(ServiceResponse::new(
self.req.take().unwrap(),
res,
)))
Poll::Ready(Ok(ServiceResponse::new(this.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
S: Service<
Request = (T, HttpRequest),
@ -293,7 +290,7 @@ where
type Error = (Error, ServiceRequest);
type InitError = ();
type Service = ExtractService<T, S>;
type Future = FutureResult<Self::Service, ()>;
type Future = Ready<Result<Self::Service, ()>>;
fn new_service(&self, _: &()) -> Self::Future {
ok(ExtractService {
@ -321,13 +318,13 @@ where
type Error = (Error, ServiceRequest);
type Future = ExtractResponse<T, S>;
fn poll_ready(&mut self) -> Poll<(), Self::Error> {
Ok(Async::Ready(()))
fn poll_ready(&mut self, _: &mut Context) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, req: ServiceRequest) -> Self::Future {
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 {
fut,
@ -338,10 +335,13 @@ where
}
}
#[pin_project]
pub struct ExtractResponse<T: FromRequest, S: Service> {
req: HttpRequest,
service: S,
fut: <T::Future as IntoFuture>::Future,
#[pin]
fut: T::Future,
#[pin]
fut_s: Option<S::Future>,
}
@ -353,21 +353,26 @@ where
Error = Infallible,
>,
{
type Item = ServiceResponse;
type Error = (Error, ServiceRequest);
type Output = Result<ServiceResponse, (Error, ServiceRequest)>;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
if let Some(ref mut fut) = self.fut_s {
return fut.poll().map_err(|_| panic!());
fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
let this = self.as_mut().project();
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| {
let req = ServiceRequest::new(self.req.clone());
(e.into(), req)
}));
self.fut_s = Some(self.service.call((item, self.req.clone())));
self.poll()
match ready!(this.fut.poll(cx)) {
Err(e) => {
let req = ServiceRequest::new(this.req.clone());
Poll::Ready(Err((e.into(), req)))
}
Ok(item) => {
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,
Res: IntoFuture,
Res::Item: Responder,
Res::Error: Into<Error>,
Res: Future<Output = Result<O, E1>>,
O: Responder,
E1: Into<Error>,
{
fn call(&self, param: ($($T,)+)) -> Res {
(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
//! for Rust.
//!
@ -68,8 +68,8 @@
//! ## Package feature
//!
//! * `client` - enables http client (default enabled)
//! * `ssl` - enables ssl support via `openssl` crate, supports `http/2`
//! * `rust-tls` - enables ssl support via `rustls` crate, supports `http/2`
//! * `openssl` - enables ssl support via `openssl` crate, supports `http/2`
//! * `rustls` - enables ssl support via `rustls` crate, supports `http/2`
//! * `secure-cookies` - enables secure cookies support, includes `ring` crate as
//! dependency
//! * `brotli` - enables `brotli` compression support, requires `c`
@ -78,7 +78,6 @@
//! `c` compiler (default enabled)
//! * `flate2-rust` - experimental rust based implementation for
//! `gzip`, `deflate` compression.
//! * `uds` - Unix domain support, enables `HttpServer::bind_uds()` method.
//!
#![allow(clippy::type_complexity, clippy::new_without_default)]
@ -143,9 +142,10 @@ pub mod dev {
pub use crate::service::{
HttpServiceFactory, ServiceRequest, ServiceResponse, WebService,
};
pub use crate::types::form::UrlEncoded;
pub use crate::types::json::JsonBody;
pub use crate::types::readlines::Readlines;
//pub use crate::types::form::UrlEncoded;
//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::encoding::Decoder as Decompress;
@ -176,18 +176,16 @@ pub mod client {
//! use actix_web::client::Client;
//!
//! fn main() {
//! System::new("test").block_on(lazy(|| {
//! System::new("test").block_on(async {
//! 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")
//! .send() // <- Send http request
//! .map_err(|_| ())
//! .and_then(|response| { // <- server http response
//! .send().await; // <- Send http request
//!
//! println!("Response: {:?}", response);
//! Ok(())
//! })
//! }));
//! });
//! }
//! ```
pub use awc::error::{

View File

@ -1,15 +1,18 @@
//! `Middleware` for compressing response body.
use std::cmp;
use std::future::Future;
use std::marker::PhantomData;
use std::pin::Pin;
use std::str::FromStr;
use std::task::{Context, Poll};
use actix_http::body::MessageBody;
use actix_http::encoding::Encoder;
use actix_http::http::header::{ContentEncoding, ACCEPT_ENCODING};
use actix_http::{Error, Response, ResponseBuilder};
use actix_service::{Service, Transform};
use futures::future::{ok, FutureResult};
use futures::{Async, Future, Poll};
use futures::future::{ok, Ready};
use pin_project::pin_project;
use crate::service::{ServiceRequest, ServiceResponse};
@ -78,7 +81,7 @@ where
type Error = Error;
type InitError = ();
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 {
ok(CompressMiddleware {
@ -103,8 +106,8 @@ where
type Error = Error;
type Future = CompressResponse<S, B>;
fn poll_ready(&mut self) -> Poll<(), Self::Error> {
self.service.poll_ready()
fn poll_ready(&mut self, cx: &mut Context) -> Poll<Result<(), Self::Error>> {
self.service.poll_ready(cx)
}
fn call(&mut self, req: ServiceRequest) -> Self::Future {
@ -128,11 +131,13 @@ where
}
#[doc(hidden)]
#[pin_project]
pub struct CompressResponse<S, B>
where
S: Service,
B: MessageBody,
{
#[pin]
fut: S::Future,
encoding: ContentEncoding,
_t: PhantomData<(B)>,
@ -143,21 +148,25 @@ where
B: MessageBody,
S: Service<Request = ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
{
type Item = ServiceResponse<Encoder<B>>;
type Error = Error;
type Output = Result<ServiceResponse<Encoder<B>>, Error>;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
let resp = futures::try_ready!(self.fut.poll());
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
let this = self.project();
match futures::ready!(this.fut.poll(cx)) {
Ok(resp) => {
let enc = if let Some(enc) = resp.response().extensions().get::<Enc>() {
enc.0
} else {
self.encoding
*this.encoding
};
Ok(Async::Ready(resp.map_body(move |head, body| {
Encoder::response(enc, head, body)
})))
Poll::Ready(Ok(
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
use std::future::Future;
use std::pin::Pin;
use std::rc::Rc;
use std::task::{Context, Poll};
use actix_service::{Service, Transform};
use futures::future::{ok, FutureResult};
use futures::{Future, Poll};
use futures::future::{ok, FutureExt, LocalBoxFuture, Ready};
use crate::http::header::{HeaderName, HeaderValue, CONTENT_TYPE};
use crate::http::{HeaderMap, HttpTryFrom};
@ -96,7 +98,7 @@ where
type Error = Error;
type InitError = ();
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 {
ok(DefaultHeadersMiddleware {
@ -119,16 +121,19 @@ where
type Request = ServiceRequest;
type Response = ServiceResponse<B>;
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> {
self.service.poll_ready()
fn poll_ready(&mut self, cx: &mut Context) -> Poll<Result<(), Self::Error>> {
self.service.poll_ready(cx)
}
fn call(&mut self, req: ServiceRequest) -> Self::Future {
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
for (key, value) in inner.headers.iter() {
if !res.headers().contains_key(key) {
@ -142,15 +147,16 @@ where
HeaderValue::from_static("application/octet-stream"),
);
}
res
}))
Ok(res)
}
.boxed_local()
}
}
#[cfg(test)]
mod tests {
use actix_service::IntoService;
use futures::future::ok;
use super::*;
use crate::dev::ServiceRequest;
@ -160,46 +166,50 @@ mod tests {
#[test]
fn test_default_headers() {
let mut mw = block_on(
DefaultHeaders::new()
block_on(async {
let mut mw = DefaultHeaders::new()
.header(CONTENT_TYPE, "0001")
.new_transform(ok_service()),
)
.new_transform(ok_service())
.await
.unwrap();
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");
let req = TestRequest::default().to_srv_request();
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")
.new_transform(srv.into_service()),
)
.new_transform(srv.into_service())
.await
.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");
})
}
#[test]
fn test_content_type() {
let srv = |req: ServiceRequest| req.into_response(HttpResponse::Ok().finish());
let mut mw = block_on(
DefaultHeaders::new()
block_on(async {
let srv =
|req: ServiceRequest| ok(req.into_response(HttpResponse::Ok().finish()));
let mut mw = DefaultHeaders::new()
.content_type()
.new_transform(srv.into_service()),
)
.new_transform(srv.into_service())
.await
.unwrap();
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(),
"application/octet-stream"
);
})
}
}

View File

@ -2,13 +2,15 @@
use std::collections::HashSet;
use std::env;
use std::fmt::{self, Display, Formatter};
use std::future::Future;
use std::marker::PhantomData;
use std::pin::Pin;
use std::rc::Rc;
use std::task::{Context, Poll};
use actix_service::{Service, Transform};
use bytes::Bytes;
use futures::future::{ok, FutureResult};
use futures::{Async, Future, Poll};
use futures::future::{ok, Ready};
use log::debug;
use regex::Regex;
use time;
@ -125,7 +127,7 @@ where
type Error = Error;
type InitError = ();
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 {
ok(LoggerMiddleware {
@ -151,8 +153,8 @@ where
type Error = Error;
type Future = LoggerResponse<S, B>;
fn poll_ready(&mut self) -> Poll<(), Self::Error> {
self.service.poll_ready()
fn poll_ready(&mut self, cx: &mut Context) -> Poll<Result<(), Self::Error>> {
self.service.poll_ready(cx)
}
fn call(&mut self, req: ServiceRequest) -> Self::Future {
@ -181,11 +183,13 @@ where
}
#[doc(hidden)]
#[pin_project::pin_project]
pub struct LoggerResponse<S, B>
where
B: MessageBody,
S: Service,
{
#[pin]
fut: S::Future,
time: time::Tm,
format: Option<Format>,
@ -197,11 +201,15 @@ where
B: MessageBody,
S: Service<Request = ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
{
type Item = ServiceResponse<StreamLog<B>>;
type Error = Error;
type Output = Result<ServiceResponse<StreamLog<B>>, Error>;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
let res = futures::try_ready!(self.fut.poll());
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
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 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 {
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 {
body,
time,
format,
size: 0,
time: self.time,
format: self.format.take(),
})
})))
}
@ -252,13 +263,13 @@ impl<B: MessageBody> MessageBody for StreamLog<B> {
self.body.size()
}
fn poll_next(&mut self) -> Poll<Option<Bytes>, Error> {
match self.body.poll_next()? {
Async::Ready(Some(chunk)) => {
fn poll_next(&mut self, cx: &mut Context) -> Poll<Option<Result<Bytes, Error>>> {
match self.body.poll_next(cx) {
Poll::Ready(Some(Ok(chunk))) => {
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)]
mod tests {
use actix_service::{IntoService, Service, Transform};
use futures::future::ok;
use super::*;
use crate::http::{header, StatusCode};
@ -472,11 +484,11 @@ mod tests {
#[test]
fn test_logger() {
let srv = |req: ServiceRequest| {
req.into_response(
ok(req.into_response(
HttpResponse::build(StatusCode::OK)
.header("X-Test", "ttt")
.finish(),
)
))
};
let logger = Logger::new("%% %{User-Agent}i %{X-Test}o %{HOME}e %D test");

View File

@ -2,13 +2,13 @@
mod compress;
pub use self::compress::{BodyEncoding, Compress};
mod condition;
//mod condition;
mod defaultheaders;
pub mod errhandlers;
//pub mod errhandlers;
mod logger;
mod normalize;
//mod normalize;
pub use self::condition::Condition;
//pub use self::condition::Condition;
pub use self::defaultheaders::DefaultHeaders;
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::{Error, Extensions, HttpMessage, Message, Payload, RequestHead};
use actix_router::{Path, Url};
use futures::future::{ok, Ready};
use crate::config::AppConfig;
use crate::data::Data;
@ -289,11 +290,11 @@ impl Drop for HttpRequest {
impl FromRequest for HttpRequest {
type Config = ();
type Error = Error;
type Future = Result<Self, Error>;
type Future = Ready<Result<Self, Error>>;
#[inline]
fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
Ok(req.clone())
ok(req.clone())
}
}
@ -349,7 +350,7 @@ mod tests {
use super::*;
use crate::dev::{ResourceDef, ResourceMap};
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};
#[test]
@ -467,6 +468,7 @@ mod tests {
#[test]
fn test_app_data() {
block_on(async {
let mut srv = init_service(App::new().data(10usize).service(
web::resource("/").to(|req: HttpRequest| {
if req.app_data::<usize>().is_some() {
@ -475,10 +477,11 @@ mod tests {
HttpResponse::BadRequest()
}
}),
));
))
.await;
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);
let mut srv = init_service(App::new().data(10u32).service(
@ -489,15 +492,18 @@ mod tests {
HttpResponse::BadRequest()
}
}),
));
))
.await;
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);
})
}
#[test]
fn test_extensions_dropped() {
block_on(async {
struct Tracker {
pub dropped: bool,
}
@ -520,13 +526,15 @@ mod tests {
});
HttpResponse::Ok()
}),
));
))
.await;
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!(tracker.borrow().dropped);
})
}
}

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

@ -7,10 +7,10 @@ use actix_http::test::TestRequest as HttpTestRequest;
use actix_http::{cookie::Cookie, Extensions, Request};
use actix_router::{Path, ResourceDef, Url};
use actix_server_config::ServerConfig;
use actix_service::{IntoNewService, IntoService, NewService, Service};
use actix_service::{IntoService, IntoServiceFactory, Service, ServiceFactory};
use bytes::{Bytes, BytesMut};
use futures::future::{ok, Future};
use futures::Stream;
use futures::future::{ok, Future, FutureExt};
use futures::stream::{Stream, StreamExt};
use serde::de::DeserializeOwned;
use serde::Serialize;
use serde_json;
@ -39,7 +39,7 @@ pub fn default_service(
) -> impl Service<Request = ServiceRequest, Response = ServiceResponse<Body>, Error = Error>
{
(move |req: ServiceRequest| {
req.into_response(HttpResponse::build(status_code).finish())
ok(req.into_response(HttpResponse::build(status_code).finish()))
})
.into_service()
}
@ -66,12 +66,12 @@ pub fn default_service(
/// 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,
) -> impl Service<Request = Request, Response = ServiceResponse<B>, Error = E>
where
R: IntoNewService<S>,
S: NewService<
R: IntoServiceFactory<S>,
S: ServiceFactory<
Config = ServerConfig,
Request = Request,
Response = ServiceResponse<B>,
@ -80,9 +80,8 @@ where
S::InitError: std::fmt::Debug,
{
let cfg = ServerConfig::new("127.0.0.1:8080".parse().unwrap());
let srv = app.into_new_service();
let fut = run_on(move || srv.new_service(&cfg));
block_on(fut).unwrap()
let srv = app.into_factory();
srv.new_service(&cfg).await.unwrap()
}
/// Calls service and waits for response future completion.
@ -106,12 +105,12 @@ where
/// 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
S: Service<Request = R, Response = ServiceResponse<B>, Error = E>,
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
@ -138,22 +137,22 @@ where
/// 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
S: Service<Request = Request, Response = ServiceResponse<B>, Error = Error>,
B: MessageBody,
{
block_on(run_on(move || {
app.call(req).and_then(|mut resp: ServiceResponse<B>| {
resp.take_body()
.fold(BytesMut::new(), move |mut body, chunk| {
body.extend_from_slice(&chunk);
Ok::<_, Error>(body)
})
.map(|body: BytesMut| body.freeze())
})
}))
.unwrap_or_else(|_| panic!("read_response failed at block_on unwrap"))
let mut resp = app
.call(req)
.await
.unwrap_or_else(|_| panic!("read_response failed at block_on unwrap"));
let mut body = resp.take_body();
let mut bytes = BytesMut::new();
while let Some(item) = body.next().await {
bytes.extend_from_slice(&item.unwrap());
}
bytes.freeze()
}
/// Helper function that returns a response body of a ServiceResponse.
@ -181,19 +180,27 @@ where
/// 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
B: MessageBody,
{
block_on(run_on(move || {
res.take_body()
.fold(BytesMut::new(), move |mut body, chunk| {
body.extend_from_slice(&chunk);
Ok::<_, Error>(body)
})
.map(|body: BytesMut| body.freeze())
}))
.unwrap_or_else(|_| panic!("read_response failed at block_on unwrap"))
let mut body = res.take_body();
let mut bytes = BytesMut::new();
while let Some(item) = body.next().await {
bytes.extend_from_slice(&item.unwrap());
}
bytes.freeze()
}
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
@ -230,27 +237,16 @@ where
/// 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
S: Service<Request = Request, Response = ServiceResponse<B>, Error = Error>,
B: MessageBody,
T: DeserializeOwned,
{
block_on(run_on(move || {
app.call(req).and_then(|mut resp: ServiceResponse<B>| {
resp.take_body()
.fold(BytesMut::new(), move |mut body, chunk| {
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"))
let body = read_response(app, req).await;
serde_json::from_slice(&body)
.unwrap_or_else(|_| panic!("read_response_json failed during deserialization"))
}
/// Test `Request` builder.
@ -511,6 +507,7 @@ mod tests {
#[test]
fn test_basics() {
block_on(async {
let req = TestRequest::with_hdr(header::ContentType::json())
.version(Version::HTTP_2)
.set(header::Date(SystemTime::now().into()))
@ -529,10 +526,12 @@ mod tests {
assert!(req.app_data::<u64>().is_none());
let data = req.app_data::<u32>().unwrap();
assert_eq!(*data, 10);
})
}
#[test]
fn test_request_methods() {
block_on(async {
let mut app = init_service(
App::new().service(
web::resource("/index.html")
@ -540,14 +539,15 @@ mod tests {
.route(web::patch().to(|| HttpResponse::Ok().body("patch!")))
.route(web::delete().to(|| HttpResponse::Ok().body("delete!"))),
),
);
)
.await;
let put_req = TestRequest::put()
.uri("/index.html")
.header(header::CONTENT_TYPE, "application/json")
.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!"));
let patch_req = TestRequest::patch()
@ -555,30 +555,34 @@ mod tests {
.header(header::CONTENT_TYPE, "application/json")
.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!"));
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!"));
})
}
#[test]
fn test_response() {
block_on(async {
let mut app = init_service(
App::new().service(
web::resource("/index.html")
.route(web::post().to(|| HttpResponse::Ok().body("welcome!"))),
),
);
)
.await;
let req = TestRequest::post()
.uri("/index.html")
.header(header::CONTENT_TYPE, "application/json")
.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!"));
})
}
#[derive(Serialize, Deserialize)]
@ -589,11 +593,14 @@ mod tests {
#[test]
fn test_response_json() {
let mut app = init_service(App::new().service(web::resource("/people").route(
block_on(async {
let mut app =
init_service(App::new().service(web::resource("/people").route(
web::post().to(|person: web::Json<Person>| {
HttpResponse::Ok().json(person.into_inner())
}),
)));
)))
.await;
let payload = r#"{"id":"12345","name":"User name"}"#.as_bytes();
@ -603,17 +610,21 @@ mod tests {
.set_payload(payload)
.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");
})
}
#[test]
fn test_request_response_form() {
let mut app = init_service(App::new().service(web::resource("/people").route(
block_on(async {
let mut app =
init_service(App::new().service(web::resource("/people").route(
web::post().to(|person: web::Form<Person>| {
HttpResponse::Ok().json(person.into_inner())
}),
)));
)))
.await;
let payload = Person {
id: "12345".to_string(),
@ -627,18 +638,22 @@ mod tests {
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.name, "User name");
})
}
#[test]
fn test_request_response_json() {
let mut app = init_service(App::new().service(web::resource("/people").route(
block_on(async {
let mut app =
init_service(App::new().service(web::resource("/people").route(
web::post().to(|person: web::Json<Person>| {
HttpResponse::Ok().json(person.into_inner())
}),
)));
)))
.await;
let payload = Person {
id: "12345".to_string(),
@ -652,66 +667,73 @@ mod tests {
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.name, "User name");
})
}
#[test]
fn test_async_with_block() {
fn async_with_block() -> impl Future<Item = HttpResponse, Error = Error> {
web::block(move || Some(4).ok_or("wrong")).then(|res| match res {
Ok(value) => HttpResponse::Ok()
block_on(async {
async fn async_with_block() -> Result<HttpResponse, Error> {
let res = web::block(move || Some(4usize).ok_or("wrong")).await;
match res? {
Ok(value) => Ok(HttpResponse::Ok()
.content_type("text/plain")
.body(format!("Async with block value: {}", value)),
.body(format!("Async with block value: {}", value))),
Err(_) => panic!("Unexpected"),
})
}
}
let mut app = init_service(
App::new().service(web::resource("/index.html").to_async(async_with_block)),
);
App::new()
.service(web::resource("/index.html").to_async(async_with_block)),
)
.await;
let req = TestRequest::post().uri("/index.html").to_request();
let res = block_fn(|| app.call(req)).unwrap();
let res = app.call(req).await.unwrap();
assert!(res.status().is_success());
}
#[test]
fn test_actor() {
use actix::Actor;
struct MyActor;
struct Num(usize);
impl actix::Message for Num {
type Result = usize;
}
impl actix::Actor for MyActor {
type Context = actix::Context<Self>;
}
impl actix::Handler<Num> for MyActor {
type Result = usize;
fn handle(&mut self, msg: Num, _: &mut Self::Context) -> Self::Result {
msg.0
}
}
let addr = run_on(|| MyActor.start());
let mut app = init_service(App::new().service(
web::resource("/index.html").to_async(move || {
addr.send(Num(1)).from_err().and_then(|res| {
if res == 1 {
HttpResponse::Ok()
} else {
HttpResponse::BadRequest()
}
})
}),
));
let req = TestRequest::post().uri("/index.html").to_request();
let res = block_fn(|| app.call(req)).unwrap();
assert!(res.status().is_success());
}
// #[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
use std::future::Future;
use std::pin::Pin;
use std::rc::Rc;
use std::task::{Context, Poll};
use std::{fmt, ops};
use actix_http::{Error, HttpMessage, Payload, Response};
use bytes::BytesMut;
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::Serialize;
@ -110,7 +114,7 @@ where
{
type Config = FormConfig;
type Error = Error;
type Future = Box<dyn Future<Item = Self, Error = Error>>;
type Future = LocalBoxFuture<'static, Result<Self, Error>>;
#[inline]
fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {
@ -120,18 +124,19 @@ where
.map(|c| (c.limit, c.ehandler.clone()))
.unwrap_or((16384, None));
Box::new(
UrlEncoded::new(req, payload)
.limit(limit)
.map_err(move |e| {
.map(move |res| match res {
Err(e) => {
if let Some(err) = err {
(*err)(e, &req2)
Err((*err)(e, &req2))
} else {
e.into()
Err(e.into())
}
}
Ok(item) => Ok(Form(item)),
})
.map(Form),
)
.boxed_local()
}
}
@ -149,15 +154,15 @@ impl<T: fmt::Display> fmt::Display for Form<T> {
impl<T: Serialize> Responder for Form<T> {
type Error = Error;
type Future = Result<Response, Error>;
type Future = Ready<Result<Response, Error>>;
fn respond_to(self, _: &HttpRequest) -> Self::Future {
let body = match serde_urlencoded::to_string(&self.0) {
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())
.body(body))
}
@ -240,7 +245,7 @@ pub struct UrlEncoded<U> {
length: Option<usize>,
encoding: &'static Encoding,
err: Option<UrlencodedError>,
fut: Option<Box<dyn Future<Item = U, Error = UrlencodedError>>>,
fut: Option<LocalBoxFuture<'static, Result<U, UrlencodedError>>>,
}
impl<U> UrlEncoded<U> {
@ -301,45 +306,45 @@ impl<U> Future for UrlEncoded<U>
where
U: DeserializeOwned + 'static,
{
type Item = U;
type Error = UrlencodedError;
type Output = Result<U, 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 {
return fut.poll();
return Pin::new(fut).poll(cx);
}
if let Some(err) = self.err.take() {
return Err(err);
return Poll::Ready(Err(err));
}
// payload size
let limit = self.limit;
if let Some(len) = self.length.take() {
if len > limit {
return Err(UrlencodedError::Overflow { size: len, limit });
return Poll::Ready(Err(UrlencodedError::Overflow { size: len, limit }));
}
}
// future
let encoding = self.encoding;
let fut = self
.stream
.take()
.unwrap()
.from_err()
.fold(BytesMut::with_capacity(8192), move |mut body, chunk| {
let mut stream = self.stream.take().unwrap();
self.fut = Some(
async move {
let mut body = BytesMut::with_capacity(8192);
while let Some(item) = stream.next().await {
let chunk = item?;
if (body.len() + chunk.len()) > limit {
Err(UrlencodedError::Overflow {
return Err(UrlencodedError::Overflow {
size: body.len() + chunk.len(),
limit,
})
});
} else {
body.extend_from_slice(&chunk);
Ok(body)
}
})
.and_then(move |body| {
}
if encoding == UTF_8 {
serde_urlencoded::from_bytes::<U>(&body)
.map_err(|_| UrlencodedError::Parse)
@ -351,9 +356,10 @@ where
serde_urlencoded::from_str::<U>(&body)
.map_err(|_| UrlencodedError::Parse)
}
});
self.fut = Some(Box::new(fut));
self.poll()
}
.boxed_local(),
);
self.poll(cx)
}
}
@ -374,13 +380,16 @@ mod tests {
#[test]
fn test_form() {
let (req, mut pl) =
TestRequest::with_header(CONTENT_TYPE, "application/x-www-form-urlencoded")
block_on(async {
let (req, mut pl) = TestRequest::with_header(
CONTENT_TYPE,
"application/x-www-form-urlencoded",
)
.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!(
s,
Info {
@ -388,6 +397,7 @@ mod tests {
counter: 123
}
);
})
}
fn eq(err: UrlencodedError, other: UrlencodedError) -> bool {
@ -410,18 +420,23 @@ mod tests {
#[test]
fn test_urlencoded_error() {
let (req, mut pl) =
TestRequest::with_header(CONTENT_TYPE, "application/x-www-form-urlencoded")
block_on(async {
let (req, mut pl) = TestRequest::with_header(
CONTENT_TYPE,
"application/x-www-form-urlencoded",
)
.header(CONTENT_LENGTH, "xxxx")
.to_http_parts();
let info = block_on(UrlEncoded::<Info>::new(&req, &mut pl));
let info = UrlEncoded::<Info>::new(&req, &mut pl).await;
assert!(eq(info.err().unwrap(), UrlencodedError::UnknownLength));
let (req, mut pl) =
TestRequest::with_header(CONTENT_TYPE, "application/x-www-form-urlencoded")
let (req, mut pl) = TestRequest::with_header(
CONTENT_TYPE,
"application/x-www-form-urlencoded",
)
.header(CONTENT_LENGTH, "1000000")
.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::Overflow { size: 0, limit: 0 }
@ -430,19 +445,23 @@ mod tests {
let (req, mut pl) = TestRequest::with_header(CONTENT_TYPE, "text/plain")
.header(CONTENT_LENGTH, "10")
.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));
})
}
#[test]
fn test_urlencoded() {
let (req, mut pl) =
TestRequest::with_header(CONTENT_TYPE, "application/x-www-form-urlencoded")
block_on(async {
let (req, mut pl) = TestRequest::with_header(
CONTENT_TYPE,
"application/x-www-form-urlencoded",
)
.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!(
info,
Info {
@ -459,7 +478,7 @@ mod tests {
.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!(
info,
Info {
@ -467,17 +486,19 @@ mod tests {
counter: 123
}
);
})
}
#[test]
fn test_responder() {
block_on(async {
let req = TestRequest::default().to_http_request();
let form = Form(Info {
hello: "world".to_string(),
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.headers().get(CONTENT_TYPE).unwrap(),
@ -486,5 +507,6 @@ mod tests {
use crate::responder::tests::BodyTest;
assert_eq!(resp.body().bin_ref(), b"hello=world&counter=123");
})
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,11 +1,12 @@
//! Essentials helper functions and types for application registration.
use actix_http::http::Method;
use futures::{Future, IntoFuture};
use futures::Future;
pub use actix_http::Response as HttpResponse;
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::handler::{AsyncFactory, Factory};
use crate::resource::Resource;
@ -256,21 +257,21 @@ where
/// # use futures::future::{ok, Future};
/// use actix_web::{web, App, HttpResponse, Error};
///
/// fn index() -> impl Future<Item=HttpResponse, Error=Error> {
/// ok(HttpResponse::Ok().finish())
/// async fn index() -> Result<HttpResponse, Error> {
/// Ok(HttpResponse::Ok().finish())
/// }
///
/// App::new().service(web::resource("/").route(
/// 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
F: AsyncFactory<I, R>,
F: AsyncFactory<I, R, O, E>,
I: FromRequest + 'static,
R: IntoFuture + 'static,
R::Item: Responder,
R::Error: Into<Error>,
R: Future<Output = Result<O, E>> + 'static,
O: Responder + 'static,
E: Into<Error> + 'static,
{
Route::new().to_async(handler)
}
@ -279,10 +280,11 @@ where
///
/// ```rust
/// # 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 {
/// req.into_response(HttpResponse::Ok().finish())
/// fn my_service(req: dev::ServiceRequest) -> Ready<Result<dev::ServiceResponse, Error>> {
/// ok(req.into_response(HttpResponse::Ok().finish()))
/// }
///
/// fn main() {
@ -299,11 +301,10 @@ pub fn service(path: &str) -> WebService {
/// Execute blocking function on a thread pool, returns future that resolves
/// 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
F: FnOnce() -> Result<I, E> + Send + 'static,
I: Send + 'static,
E: Send + std::fmt::Debug + 'static,
F: FnOnce() -> R + Send + 'static,
R: Send + '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 }
[dev-dependencies]
#actix-web = "1.0.7"
actix-web = "2.0.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 actix_codec::{AsyncRead, AsyncWrite, Framed};
use actix_rt::{System};
use actix_rt::System;
use actix_server::{Server, ServiceFactory};
use awc::{error::PayloadError, ws, Client, ClientRequest, ClientResponse, Connector};
use bytes::Bytes;

View File

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

View File

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