1
0
mirror of https://github.com/fafhrd91/actix-web synced 2025-07-05 02:16:33 +02:00

Compare commits

...

25 Commits

Author SHA1 Message Date
cbe0226177 update changes 2019-05-18 10:47:08 -07:00
e8c8626878 update deps 2019-05-18 09:54:23 -07:00
4b215e0839 Support Query<T>::from_query() (#846) 2019-05-17 13:10:46 -07:00
e1ff3bf8fa fix resource match with params #841 2019-05-15 10:31:40 -07:00
80f4ef9aac When using codegen with paths that have parameters then only the first endpoint resolves (#842) 2019-05-15 09:21:07 -07:00
bba90d7f22 Query config (#839)
* add QueryConfig

* expose QueryConfig in web module

* fmt

* use associated type for QueryConfig

* update CHANGES.md
2019-05-14 13:54:30 -07:00
f8af3b86e5 export set_date 2019-05-14 08:48:11 -07:00
6c3d8b8738 Make JsonConfig send (#830)
* replace Rc with Arc

* add Send trait requirement for Fn in JsonConfig error handler

* add Sync trait requirement for Fn in JsonConfig error handler

* use associated type inside JsonConfig

* fix lint: members in the impl has the same order in the trait

* Update CHANGES.md
2019-05-12 20:04:08 -07:00
5a90e33bcc update deps 2019-05-12 12:01:24 -07:00
86b569e320 version 2019-05-12 11:56:01 -07:00
2350a2dc68 Handle cancellation of uploads #834 #736 2019-05-12 11:43:05 -07:00
36d017dcc6 update deps 2019-05-12 11:41:43 -07:00
3bb081852c prep actix-session release 2019-05-12 10:53:21 -07:00
1ca58e876b prepare beta4 release 2019-05-12 10:49:21 -07:00
e9cbcbaf03 update dependencies 2019-05-12 10:18:02 -07:00
07c9eec803 prepare awc release 2019-05-12 10:04:38 -07:00
beae9ca0f7 update changes 2019-05-12 09:57:16 -07:00
07b9707ca1 prepare actix-http release 2019-05-12 09:56:55 -07:00
45c05978b0 Allow to set/override app data on scope level 2019-05-12 09:42:05 -07:00
df08baf67f update actix-net dependencies 2019-05-12 08:34:51 -07:00
4066375737 Update CHANGES.md 2019-05-10 14:45:30 -07:00
a77b0b054a Make App::configure take an FnOnce (#825) 2019-05-10 14:44:49 -07:00
a17ff492a1 fix formatting 2019-05-04 22:18:59 -07:00
33b4c05557 add payload stream migration entry 2019-05-04 22:18:02 -07:00
005c055a7f prepare actix-web release 2019-05-04 20:05:20 -07:00
65 changed files with 858 additions and 411 deletions

View File

@ -1,5 +1,33 @@
# Changes # Changes
## [1.0.0-rc] - 2019-05-xx
### Add
* Add `Query<T>::from_query()` to extract parameters from a query string. #846
* `QueryConfig`, similar to `JsonConfig` for customizing error handling of query extractors.
### Changes
* `JsonConfig` is now `Send + Sync`, this implies that `error_handler` must be `Send + Sync` too.
### Fixed
* Codegen with parameters in the path only resolves the first registered endpoint #841
## [1.0.0-beta.4] - 2019-05-12
### Add
* Allow to set/override app data on scope level
### Changes
* `App::configure` take an `FnOnce` instead of `Fn`
* Upgrade actix-net crates
## [1.0.0-beta.3] - 2019-05-04 ## [1.0.0-beta.3] - 2019-05-04
### Added ### Added

View File

@ -1,6 +1,6 @@
[package] [package]
name = "actix-web" name = "actix-web"
version = "1.0.0-beta.2" version = "1.0.0-rc"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"] authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Actix web is a simple, pragmatic and extremely fast web framework for Rust." description = "Actix web is a simple, pragmatic and extremely fast web framework for Rust."
readme = "README.md" readme = "README.md"
@ -41,7 +41,7 @@ members = [
] ]
[features] [features]
default = ["brotli", "flate2-zlib", "secure-cookies", "client"] default = ["brotli", "flate2-zlib", "secure-cookies", "client", "fail"]
# http client # http client
client = ["awc"] client = ["awc"]
@ -58,6 +58,8 @@ flate2-rust = ["actix-http/flate2-rust"]
# sessions feature, session require "ring" crate and c compiler # sessions feature, session require "ring" crate and c compiler
secure-cookies = ["actix-http/secure-cookies"] secure-cookies = ["actix-http/secure-cookies"]
fail = ["actix-http/fail"]
# openssl # openssl
ssl = ["openssl", "actix-server/ssl", "awc/ssl"] ssl = ["openssl", "actix-server/ssl", "awc/ssl"]
@ -66,27 +68,26 @@ rust-tls = ["rustls", "actix-server/rust-tls"]
[dependencies] [dependencies]
actix-codec = "0.1.2" actix-codec = "0.1.2"
actix-service = "0.3.6" actix-service = "0.4.0"
actix-utils = "0.3.4" actix-utils = "0.4.1"
actix-router = "0.1.3" actix-router = "0.1.5"
actix-rt = "0.2.2" actix-rt = "0.2.2"
actix-web-codegen = "0.1.0-beta.1" actix-web-codegen = "0.1.0-beta.1"
actix-http = { version = "0.1.4", features=["fail"] } actix-http = "0.2.0"
actix-server = "0.4.3" actix-server = "0.5.0"
actix-server-config = "0.1.1" actix-server-config = "0.1.1"
actix-threadpool = "0.1.0" actix-threadpool = "0.1.0"
actix = { version = "0.8.1", features=["http"], optional = true } awc = { version = "0.2.0", optional = true }
awc = { version = "0.1.1", optional = true }
bytes = "0.4" bytes = "0.4"
derive_more = "0.14" derive_more = "0.14"
encoding = "0.2" encoding = "0.2"
futures = "0.1" futures = "0.1.25"
hashbrown = "0.2.2" hashbrown = "0.3.0"
log = "0.4" log = "0.4"
mime = "0.3" mime = "0.3"
net2 = "0.2.33" net2 = "0.2.33"
parking_lot = "0.7" parking_lot = "0.8"
regex = "1.0" regex = "1.0"
serde = { version = "1.0", features=["derive"] } serde = { version = "1.0", features=["derive"] }
serde_json = "1.0" serde_json = "1.0"
@ -96,12 +97,12 @@ url = { version="1.7", features=["query_encoding"] }
# ssl support # ssl support
openssl = { version="0.10", optional = true } openssl = { version="0.10", optional = true }
rustls = { version = "^0.15", optional = true } rustls = { version = "0.15", optional = true }
[dev-dependencies] [dev-dependencies]
actix-http = { version = "0.1.4", features=["ssl", "brotli", "flate2-zlib"] } actix-http = { version = "0.2.0", features=["ssl", "brotli", "flate2-zlib"] }
actix-http-test = { version = "0.1.1", features=["ssl"] } actix-http-test = { version = "0.2.0", features=["ssl"] }
actix-files = { version = "0.1.0-beta.1" } actix-files = { version = "0.1.0-beta.4" }
rand = "0.6" rand = "0.6"
env_logger = "0.6" env_logger = "0.6"
serde_derive = "1.0" serde_derive = "1.0"
@ -122,4 +123,5 @@ actix-web-codegen = { path = "actix-web-codegen" }
actix-web-actors = { path = "actix-web-actors" } actix-web-actors = { path = "actix-web-actors" }
actix-session = { path = "actix-session" } actix-session = { path = "actix-session" }
actix-files = { path = "actix-files" } actix-files = { path = "actix-files" }
actix-multipart = { path = "actix-multipart" }
awc = { path = "awc" } awc = { path = "awc" }

View File

@ -92,6 +92,36 @@
App.new().service(web::resource("/welcome").to(welcome)) App.new().service(web::resource("/welcome").to(welcome))
``` ```
* `HttpRequest` does not provide access to request's payload stream.
instead of
```rust
fn index(req: &HttpRequest) -> Box<Future<Item=HttpResponse, Error=Error>> {
req
.payload()
.from_err()
.fold((), |_, chunk| {
...
})
.map(|_| HttpResponse::Ok().finish())
.responder()
}
```
use `Payload` extractor
```rust
fn index(stream: web::Payload) -> impl Future<Item=HttpResponse, Error=Error> {
stream
.from_err()
.fold((), |_, chunk| {
...
})
.map(|_| HttpResponse::Ok().finish())
}
```
* `State` is now `Data`. You register Data during the App initialization process * `State` is now `Data`. You register Data during the App initialization process
and then access it from handlers either using a Data extractor or using and then access it from handlers either using a Data extractor or using
HttpRequest's api. HttpRequest's api.

View File

@ -1,5 +1,9 @@
# Changes # Changes
## [0.1.0-beta.4] - 2019-05-12
* Update actix-web to beta.4
## [0.1.0-beta.1] - 2019-04-20 ## [0.1.0-beta.1] - 2019-04-20
* Update actix-web to beta.1 * Update actix-web to beta.1

View File

@ -1,6 +1,6 @@
[package] [package]
name = "actix-files" name = "actix-files"
version = "0.1.0-beta.1" version = "0.1.0-beta.4"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"] authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Static files support for actix web." description = "Static files support for actix web."
readme = "README.md" readme = "README.md"
@ -18,8 +18,8 @@ name = "actix_files"
path = "src/lib.rs" path = "src/lib.rs"
[dependencies] [dependencies]
actix-web = "1.0.0-beta.1" actix-web = "1.0.0-beta.5"
actix-service = "0.3.4" actix-service = "0.4.0"
bitflags = "1" bitflags = "1"
bytes = "0.4" bytes = "0.4"
futures = "0.1.25" futures = "0.1.25"
@ -31,4 +31,4 @@ percent-encoding = "1.0"
v_htmlescape = "0.4" v_htmlescape = "0.4"
[dev-dependencies] [dev-dependencies]
actix-web = { version = "1.0.0-beta.1", features=["ssl"] } actix-web = { version = "1.0.0-beta.5", features=["ssl"] }

View File

@ -334,6 +334,7 @@ impl Files {
where where
F: IntoNewService<U>, F: IntoNewService<U>,
U: NewService< U: NewService<
Config = (),
Request = ServiceRequest, Request = ServiceRequest,
Response = ServiceResponse, Response = ServiceResponse,
Error = Error, Error = Error,
@ -363,6 +364,7 @@ impl HttpServiceFactory for Files {
} }
impl NewService for Files { impl NewService for Files {
type Config = ();
type Request = ServiceRequest; type Request = ServiceRequest;
type Response = ServiceResponse; type Response = ServiceResponse;
type Error = Error; type Error = Error;

View File

@ -1,6 +1,6 @@
[package] [package]
name = "actix-framed" name = "actix-framed"
version = "0.1.0" version = "0.2.0"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"] authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Actix framed app server" description = "Actix framed app server"
readme = "README.md" readme = "README.md"
@ -21,17 +21,18 @@ path = "src/lib.rs"
[dependencies] [dependencies]
actix-codec = "0.1.2" actix-codec = "0.1.2"
actix-service = "0.3.6" actix-service = "0.4.0"
actix-utils = "0.3.4" actix-utils = "0.4.0"
actix-router = "0.1.2" actix-router = "0.1.2"
actix-rt = "0.2.2" actix-rt = "0.2.2"
actix-http = "0.1.0" actix-http = "0.2.0"
actix-server-config = "0.1.1"
bytes = "0.4" bytes = "0.4"
futures = "0.1.25" futures = "0.1.25"
log = "0.4" log = "0.4"
[dev-dependencies] [dev-dependencies]
actix-server = { version = "0.4.3", features=["ssl"] } actix-server = { version = "0.5.0", features=["ssl"] }
actix-connect = { version = "0.1.4", features=["ssl"] } actix-connect = { version = "0.2.0", features=["ssl"] }
actix-http-test = { version = "0.1.0", features=["ssl"] } actix-http-test = { version = "0.2.0", features=["ssl"] }

View File

@ -1,5 +1,10 @@
# Changes # Changes
## [0.2.0] - 2019-05-12
* Update dependencies
## [0.1.0] - 2019-04-16 ## [0.1.0] - 2019-04-16
* Update tests * Update tests

View File

@ -4,6 +4,7 @@ use actix_codec::{AsyncRead, AsyncWrite, Framed};
use actix_http::h1::{Codec, SendResponse}; use actix_http::h1::{Codec, SendResponse};
use actix_http::{Error, Request, Response}; use actix_http::{Error, Request, Response};
use actix_router::{Path, Router, Url}; use actix_router::{Path, Router, Url};
use actix_server_config::ServerConfig;
use actix_service::{IntoNewService, NewService, Service}; use actix_service::{IntoNewService, NewService, Service};
use actix_utils::cloneable::CloneableService; use actix_utils::cloneable::CloneableService;
use futures::{Async, Future, Poll}; use futures::{Async, Future, Poll};
@ -49,6 +50,7 @@ impl<T: 'static, S: 'static> FramedApp<T, S> {
where where
U: HttpServiceFactory, U: HttpServiceFactory,
U::Factory: NewService< U::Factory: NewService<
Config = (),
Request = FramedRequest<T, S>, Request = FramedRequest<T, S>,
Response = (), Response = (),
Error = Error, Error = Error,
@ -88,11 +90,12 @@ pub struct FramedAppFactory<T, S> {
services: Rc<Vec<(String, BoxedHttpNewService<FramedRequest<T, S>>)>>, services: Rc<Vec<(String, BoxedHttpNewService<FramedRequest<T, S>>)>>,
} }
impl<T, S, C> NewService<C> for FramedAppFactory<T, S> impl<T, S> NewService for FramedAppFactory<T, S>
where where
T: AsyncRead + AsyncWrite + 'static, T: AsyncRead + AsyncWrite + 'static,
S: 'static, S: 'static,
{ {
type Config = ServerConfig;
type Request = (Request, Framed<T, Codec>); type Request = (Request, Framed<T, Codec>);
type Response = (); type Response = ();
type Error = Error; type Error = Error;
@ -100,7 +103,7 @@ where
type Service = CloneableService<FramedAppService<T, S>>; type Service = CloneableService<FramedAppService<T, S>>;
type Future = CreateService<T, S>; type Future = CreateService<T, S>;
fn new_service(&self, _: &C) -> Self::Future { fn new_service(&self, _: &ServerConfig) -> Self::Future {
CreateService { CreateService {
fut: self fut: self
.services .services

View File

@ -13,6 +13,7 @@ pub(crate) type BoxedHttpService<Req> = Box<
pub(crate) type BoxedHttpNewService<Req> = Box< pub(crate) type BoxedHttpNewService<Req> = Box<
NewService< NewService<
Config = (),
Request = Req, Request = Req,
Response = (), Response = (),
Error = Error, Error = Error,
@ -39,12 +40,13 @@ where
impl<T> NewService for HttpNewService<T> impl<T> NewService for HttpNewService<T>
where where
T: NewService<Response = (), Error = Error>, T: NewService<Config = (), Response = (), Error = Error>,
T::Request: 'static, T::Request: 'static,
T::Future: 'static, T::Future: 'static,
T::Service: Service<Future = Box<Future<Item = (), Error = Error>>> + 'static, T::Service: Service<Future = Box<Future<Item = (), Error = Error>>> + 'static,
<T::Service as Service>::Future: 'static, <T::Service as Service>::Future: 'static,
{ {
type Config = ();
type Request = T::Request; type Request = T::Request;
type Response = (); type Response = ();
type Error = Error; type Error = Error;

View File

@ -106,6 +106,7 @@ where
R::Future: 'static, R::Future: 'static,
R::Error: fmt::Display, R::Error: fmt::Display,
{ {
type Config = ();
type Request = FramedRequest<Io, S>; type Request = FramedRequest<Io, S>;
type Response = (); type Response = ();
type Error = Error; type Error = Error;

View File

@ -12,22 +12,23 @@ use futures::{Async, Future, IntoFuture, Poll, Sink};
/// Service that verifies incoming request if it is valid websocket /// Service that verifies incoming request if it is valid websocket
/// upgrade request. In case of error returns `HandshakeError` /// upgrade request. In case of error returns `HandshakeError`
pub struct VerifyWebSockets<T> { pub struct VerifyWebSockets<T, C> {
_t: PhantomData<T>, _t: PhantomData<(T, C)>,
} }
impl<T> Default for VerifyWebSockets<T> { impl<T, C> Default for VerifyWebSockets<T, C> {
fn default() -> Self { fn default() -> Self {
VerifyWebSockets { _t: PhantomData } VerifyWebSockets { _t: PhantomData }
} }
} }
impl<T, C> NewService<C> for VerifyWebSockets<T> { impl<T, C> NewService for VerifyWebSockets<T, C> {
type Config = C;
type Request = (Request, Framed<T, Codec>); type Request = (Request, Framed<T, Codec>);
type Response = (Request, Framed<T, Codec>); type Response = (Request, Framed<T, Codec>);
type Error = (HandshakeError, Framed<T, Codec>); type Error = (HandshakeError, Framed<T, Codec>);
type InitError = (); type InitError = ();
type Service = VerifyWebSockets<T>; type Service = VerifyWebSockets<T, C>;
type Future = FutureResult<Self::Service, Self::InitError>; type Future = FutureResult<Self::Service, Self::InitError>;
fn new_service(&self, _: &C) -> Self::Future { fn new_service(&self, _: &C) -> Self::Future {
@ -35,7 +36,7 @@ impl<T, C> NewService<C> for VerifyWebSockets<T> {
} }
} }
impl<T> Service for VerifyWebSockets<T> { impl<T, C> Service for VerifyWebSockets<T, C> {
type Request = (Request, Framed<T, Codec>); type Request = (Request, Framed<T, Codec>);
type Response = (Request, Framed<T, Codec>); type Response = (Request, Framed<T, Codec>);
type Error = (HandshakeError, Framed<T, Codec>); type Error = (HandshakeError, Framed<T, Codec>);
@ -54,9 +55,9 @@ impl<T> Service for VerifyWebSockets<T> {
} }
/// Send http/1 error response /// Send http/1 error response
pub struct SendError<T, R, E>(PhantomData<(T, R, E)>); pub struct SendError<T, R, E, C>(PhantomData<(T, R, E, C)>);
impl<T, R, E> Default for SendError<T, R, E> impl<T, R, E, C> Default for SendError<T, R, E, C>
where where
T: AsyncRead + AsyncWrite, T: AsyncRead + AsyncWrite,
E: ResponseError, E: ResponseError,
@ -66,17 +67,18 @@ where
} }
} }
impl<T, R, E, C> NewService<C> for SendError<T, R, E> impl<T, R, E, C> NewService for SendError<T, R, E, C>
where where
T: AsyncRead + AsyncWrite + 'static, T: AsyncRead + AsyncWrite + 'static,
R: 'static, R: 'static,
E: ResponseError + 'static, E: ResponseError + 'static,
{ {
type Config = C;
type Request = Result<R, (E, Framed<T, Codec>)>; type Request = Result<R, (E, Framed<T, Codec>)>;
type Response = R; type Response = R;
type Error = (E, Framed<T, Codec>); type Error = (E, Framed<T, Codec>);
type InitError = (); type InitError = ();
type Service = SendError<T, R, E>; type Service = SendError<T, R, E, C>;
type Future = FutureResult<Self::Service, Self::InitError>; type Future = FutureResult<Self::Service, Self::InitError>;
fn new_service(&self, _: &C) -> Self::Future { fn new_service(&self, _: &C) -> Self::Future {
@ -84,7 +86,7 @@ where
} }
} }
impl<T, R, E> Service for SendError<T, R, E> impl<T, R, E, C> Service for SendError<T, R, E, C>
where where
T: AsyncRead + AsyncWrite + 'static, T: AsyncRead + AsyncWrite + 'static,
R: 'static, R: 'static,

View File

@ -1,5 +1,18 @@
# Changes # Changes
## [0.2.0] - 2019-05-12
### Changed
* Update actix-service to 0.4
* Expect and upgrade services accept `ServerConfig` config.
### Deleted
* `OneRequest` service
## [0.1.5] - 2019-05-04 ## [0.1.5] - 2019-05-04
### Fixed ### Fixed

View File

@ -1,6 +1,6 @@
[package] [package]
name = "actix-http" name = "actix-http"
version = "0.1.5" version = "0.2.0"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"] authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Actix http primitives" description = "Actix http primitives"
readme = "README.md" readme = "README.md"
@ -44,10 +44,10 @@ fail = ["failure"]
secure-cookies = ["ring"] secure-cookies = ["ring"]
[dependencies] [dependencies]
actix-service = "0.3.6" actix-service = "0.4.0"
actix-codec = "0.1.2" actix-codec = "0.1.2"
actix-connect = "0.1.5" actix-connect = "0.2.0"
actix-utils = "0.3.5" actix-utils = "0.4.0"
actix-server-config = "0.1.1" actix-server-config = "0.1.1"
actix-threadpool = "0.1.0" actix-threadpool = "0.1.0"
@ -59,8 +59,8 @@ copyless = "0.1.2"
derive_more = "0.14" derive_more = "0.14"
either = "1.5.2" either = "1.5.2"
encoding = "0.2" encoding = "0.2"
futures = "0.1" futures = "0.1.25"
hashbrown = "0.2.2" hashbrown = "0.3.0"
h2 = "0.1.16" h2 = "0.1.16"
http = "0.1.17" http = "0.1.17"
httparse = "1.3" httparse = "1.3"
@ -97,9 +97,9 @@ chrono = "0.4.6"
[dev-dependencies] [dev-dependencies]
actix-rt = "0.2.2" actix-rt = "0.2.2"
actix-server = { version = "0.4.3", features=["ssl"] } actix-server = { version = "0.5.0", features=["ssl"] }
actix-connect = { version = "0.1.4", features=["ssl"] } actix-connect = { version = "0.2.0", features=["ssl"] }
actix-http-test = { version = "0.1.0", features=["ssl"] } actix-http-test = { version = "0.2.0", features=["ssl"] }
env_logger = "0.6" env_logger = "0.6"
serde_derive = "1.0" serde_derive = "1.0"
openssl = { version="0.10" } openssl = { version="0.10" }

View File

@ -29,7 +29,7 @@ pub struct HttpServiceBuilder<T, S, X = ExpectHandler, U = UpgradeHandler<T>> {
impl<T, S> HttpServiceBuilder<T, S, ExpectHandler, UpgradeHandler<T>> impl<T, S> HttpServiceBuilder<T, S, ExpectHandler, UpgradeHandler<T>>
where where
S: NewService<SrvConfig, Request = Request>, S: NewService<Config = SrvConfig, Request = Request>,
S::Error: Into<Error>, S::Error: Into<Error>,
S::InitError: fmt::Debug, S::InitError: fmt::Debug,
{ {
@ -48,13 +48,17 @@ where
impl<T, S, X, U> HttpServiceBuilder<T, S, X, U> impl<T, S, X, U> HttpServiceBuilder<T, S, X, U>
where where
S: NewService<SrvConfig, Request = Request>, S: NewService<Config = SrvConfig, Request = Request>,
S::Error: Into<Error>, S::Error: Into<Error>,
S::InitError: fmt::Debug, S::InitError: fmt::Debug,
X: NewService<Request = Request, Response = Request>, X: NewService<Config = SrvConfig, Request = Request, Response = Request>,
X::Error: Into<Error>, X::Error: Into<Error>,
X::InitError: fmt::Debug, X::InitError: fmt::Debug,
U: NewService<Request = (Request, Framed<T, Codec>), Response = ()>, U: NewService<
Config = SrvConfig,
Request = (Request, Framed<T, Codec>),
Response = (),
>,
U::Error: fmt::Display, U::Error: fmt::Display,
U::InitError: fmt::Debug, U::InitError: fmt::Debug,
{ {
@ -101,7 +105,7 @@ where
pub fn expect<F, X1>(self, expect: F) -> HttpServiceBuilder<T, S, X1, U> pub fn expect<F, X1>(self, expect: F) -> HttpServiceBuilder<T, S, X1, U>
where where
F: IntoNewService<X1>, F: IntoNewService<X1>,
X1: NewService<Request = Request, Response = Request>, X1: NewService<Config = SrvConfig, Request = Request, Response = Request>,
X1::Error: Into<Error>, X1::Error: Into<Error>,
X1::InitError: fmt::Debug, X1::InitError: fmt::Debug,
{ {
@ -122,7 +126,11 @@ where
pub fn upgrade<F, U1>(self, upgrade: F) -> HttpServiceBuilder<T, S, X, U1> pub fn upgrade<F, U1>(self, upgrade: F) -> HttpServiceBuilder<T, S, X, U1>
where where
F: IntoNewService<U1>, F: IntoNewService<U1>,
U1: NewService<Request = (Request, Framed<T, Codec>), Response = ()>, U1: NewService<
Config = SrvConfig,
Request = (Request, Framed<T, Codec>),
Response = (),
>,
U1::Error: fmt::Display, U1::Error: fmt::Display,
U1::InitError: fmt::Debug, U1::InitError: fmt::Debug,
{ {
@ -140,7 +148,7 @@ where
pub fn h1<F, P, B>(self, service: F) -> H1Service<T, P, S, B, X, U> pub fn h1<F, P, B>(self, service: F) -> H1Service<T, P, S, B, X, U>
where where
B: MessageBody + 'static, B: MessageBody + 'static,
F: IntoNewService<S, SrvConfig>, F: IntoNewService<S>,
S::Error: Into<Error>, S::Error: Into<Error>,
S::InitError: fmt::Debug, S::InitError: fmt::Debug,
S::Response: Into<Response<B>>, S::Response: Into<Response<B>>,
@ -159,7 +167,7 @@ where
pub fn h2<F, P, B>(self, service: F) -> H2Service<T, P, S, B> pub fn h2<F, P, B>(self, service: F) -> H2Service<T, P, S, B>
where where
B: MessageBody + 'static, B: MessageBody + 'static,
F: IntoNewService<S, SrvConfig>, F: IntoNewService<S>,
S::Error: Into<Error>, S::Error: Into<Error>,
S::InitError: fmt::Debug, S::InitError: fmt::Debug,
S::Response: Into<Response<B>>, S::Response: Into<Response<B>>,
@ -177,7 +185,7 @@ where
pub fn finish<F, P, B>(self, service: F) -> HttpService<T, P, S, B, X, U> pub fn finish<F, P, B>(self, service: F) -> HttpService<T, P, S, B, X, U>
where where
B: MessageBody + 'static, B: MessageBody + 'static,
F: IntoNewService<S, SrvConfig>, F: IntoNewService<S>,
S::Error: Into<Error>, S::Error: Into<Error>,
S::InitError: fmt::Debug, S::InitError: fmt::Debug,
S::Response: Into<Response<B>>, S::Response: Into<Response<B>>,

View File

@ -158,7 +158,8 @@ impl ServiceConfig {
self.0.timer.now() self.0.timer.now()
} }
pub(crate) fn set_date(&self, dst: &mut BytesMut) { #[doc(hidden)]
pub fn set_date(&self, dst: &mut BytesMut) {
let mut buf: [u8; 39] = [0; 39]; let mut buf: [u8; 39] = [0; 39];
buf[..6].copy_from_slice(b"date: "); buf[..6].copy_from_slice(b"date: ");
buf[6..35].copy_from_slice(&self.0.timer.date().bytes); buf[6..35].copy_from_slice(&self.0.timer.date().bytes);

View File

@ -31,7 +31,7 @@ const AVERAGE_HEADER_SIZE: usize = 30;
/// HTTP/1 Codec /// HTTP/1 Codec
pub struct Codec { pub struct Codec {
pub(crate) config: ServiceConfig, config: ServiceConfig,
decoder: decoder::MessageDecoder<Request>, decoder: decoder::MessageDecoder<Request>,
payload: Option<PayloadDecoder>, payload: Option<PayloadDecoder>,
version: Version, version: Version,
@ -104,6 +104,11 @@ impl Codec {
MessageType::Payload MessageType::Payload
} }
} }
#[inline]
pub fn config(&self) -> &ServiceConfig {
&self.config
}
} }
impl Decoder for Codec { impl Decoder for Codec {

View File

@ -567,7 +567,7 @@ where
} }
if updated && self.ka_timer.is_some() { if updated && self.ka_timer.is_some() {
if let Some(expire) = self.codec.config.keep_alive_expire() { if let Some(expire) = self.codec.config().keep_alive_expire() {
self.ka_expire = expire; self.ka_expire = expire;
} }
} }
@ -579,7 +579,7 @@ where
if self.ka_timer.is_none() { if self.ka_timer.is_none() {
// shutdown timeout // shutdown timeout
if self.flags.contains(Flags::SHUTDOWN) { if self.flags.contains(Flags::SHUTDOWN) {
if let Some(interval) = self.codec.config.client_disconnect_timer() { if let Some(interval) = self.codec.config().client_disconnect_timer() {
self.ka_timer = Some(Delay::new(interval)); self.ka_timer = Some(Delay::new(interval));
} else { } else {
self.flags.insert(Flags::READ_DISCONNECT); self.flags.insert(Flags::READ_DISCONNECT);
@ -607,7 +607,7 @@ where
// start shutdown timer // start shutdown timer
if let Some(deadline) = if let Some(deadline) =
self.codec.config.client_disconnect_timer() self.codec.config().client_disconnect_timer()
{ {
if let Some(timer) = self.ka_timer.as_mut() { if let Some(timer) = self.ka_timer.as_mut() {
timer.reset(deadline); timer.reset(deadline);
@ -632,7 +632,8 @@ where
self.flags.insert(Flags::STARTED | Flags::SHUTDOWN); self.flags.insert(Flags::STARTED | Flags::SHUTDOWN);
self.state = State::None; self.state = State::None;
} }
} else if let Some(deadline) = self.codec.config.keep_alive_expire() } else if let Some(deadline) =
self.codec.config().keep_alive_expire()
{ {
if let Some(timer) = self.ka_timer.as_mut() { if let Some(timer) = self.ka_timer.as_mut() {
timer.reset(deadline); timer.reset(deadline);

View File

@ -1,3 +1,4 @@
use actix_server_config::ServerConfig;
use actix_service::{NewService, Service}; use actix_service::{NewService, Service};
use futures::future::{ok, FutureResult}; use futures::future::{ok, FutureResult};
use futures::{Async, Poll}; use futures::{Async, Poll};
@ -8,6 +9,7 @@ use crate::request::Request;
pub struct ExpectHandler; pub struct ExpectHandler;
impl NewService for ExpectHandler { impl NewService for ExpectHandler {
type Config = ServerConfig;
type Request = Request; type Request = Request;
type Response = Request; type Response = Request;
type Error = Error; type Error = Error;
@ -15,7 +17,7 @@ impl NewService for ExpectHandler {
type InitError = Error; type InitError = Error;
type Future = FutureResult<Self::Service, Self::InitError>; type Future = FutureResult<Self::Service, Self::InitError>;
fn new_service(&self, _: &()) -> Self::Future { fn new_service(&self, _: &ServerConfig) -> Self::Future {
ok(ExpectHandler) ok(ExpectHandler)
} }
} }

View File

@ -29,14 +29,14 @@ pub struct H1Service<T, P, S, B, X = ExpectHandler, U = UpgradeHandler<T>> {
impl<T, P, S, B> H1Service<T, P, S, B> impl<T, P, S, B> H1Service<T, P, S, B>
where where
S: NewService<SrvConfig, Request = Request>, S: NewService<Config = SrvConfig, Request = Request>,
S::Error: Into<Error>, S::Error: Into<Error>,
S::InitError: fmt::Debug, S::InitError: fmt::Debug,
S::Response: Into<Response<B>>, S::Response: Into<Response<B>>,
B: MessageBody, B: MessageBody,
{ {
/// Create new `HttpService` instance with default config. /// Create new `HttpService` instance with default config.
pub fn new<F: IntoNewService<S, SrvConfig>>(service: F) -> Self { pub fn new<F: IntoNewService<S>>(service: F) -> Self {
let cfg = ServiceConfig::new(KeepAlive::Timeout(5), 5000, 0); let cfg = ServiceConfig::new(KeepAlive::Timeout(5), 5000, 0);
H1Service { H1Service {
@ -49,10 +49,7 @@ where
} }
/// Create new `HttpService` instance with config. /// Create new `HttpService` instance with config.
pub fn with_config<F: IntoNewService<S, SrvConfig>>( pub fn with_config<F: IntoNewService<S>>(cfg: ServiceConfig, service: F) -> Self {
cfg: ServiceConfig,
service: F,
) -> Self {
H1Service { H1Service {
cfg, cfg,
srv: service.into_new_service(), srv: service.into_new_service(),
@ -65,7 +62,7 @@ where
impl<T, P, S, B, X, U> H1Service<T, P, S, B, X, U> impl<T, P, S, B, X, U> H1Service<T, P, S, B, X, U>
where where
S: NewService<SrvConfig, Request = Request>, S: NewService<Config = SrvConfig, Request = Request>,
S::Error: Into<Error>, S::Error: Into<Error>,
S::Response: Into<Response<B>>, S::Response: Into<Response<B>>,
S::InitError: fmt::Debug, S::InitError: fmt::Debug,
@ -102,21 +99,26 @@ where
} }
} }
impl<T, P, S, B, X, U> NewService<SrvConfig> for H1Service<T, P, S, B, X, U> impl<T, P, S, B, X, U> NewService for H1Service<T, P, S, B, X, U>
where where
T: IoStream, T: IoStream,
S: NewService<SrvConfig, Request = Request>, S: NewService<Config = SrvConfig, Request = Request>,
S::Error: Into<Error>, S::Error: Into<Error>,
S::Response: Into<Response<B>>, S::Response: Into<Response<B>>,
S::InitError: fmt::Debug, S::InitError: fmt::Debug,
B: MessageBody, B: MessageBody,
X: NewService<Request = Request, Response = Request>, X: NewService<Config = SrvConfig, Request = Request, Response = Request>,
X::Error: Into<Error>, X::Error: Into<Error>,
X::InitError: fmt::Debug, X::InitError: fmt::Debug,
U: NewService<Request = (Request, Framed<T, Codec>), Response = ()>, U: NewService<
Config = SrvConfig,
Request = (Request, Framed<T, Codec>),
Response = (),
>,
U::Error: fmt::Display, U::Error: fmt::Display,
U::InitError: fmt::Debug, U::InitError: fmt::Debug,
{ {
type Config = SrvConfig;
type Request = Io<T, P>; type Request = Io<T, P>;
type Response = (); type Response = ();
type Error = DispatchError; type Error = DispatchError;
@ -127,8 +129,8 @@ where
fn new_service(&self, cfg: &SrvConfig) -> Self::Future { fn new_service(&self, cfg: &SrvConfig) -> Self::Future {
H1ServiceResponse { H1ServiceResponse {
fut: self.srv.new_service(cfg).into_future(), fut: self.srv.new_service(cfg).into_future(),
fut_ex: Some(self.expect.new_service(&())), fut_ex: Some(self.expect.new_service(cfg)),
fut_upg: self.upgrade.as_ref().map(|f| f.new_service(&())), fut_upg: self.upgrade.as_ref().map(|f| f.new_service(cfg)),
expect: None, expect: None,
upgrade: None, upgrade: None,
cfg: Some(self.cfg.clone()), cfg: Some(self.cfg.clone()),
@ -140,7 +142,7 @@ where
#[doc(hidden)] #[doc(hidden)]
pub struct H1ServiceResponse<T, P, S, B, X, U> pub struct H1ServiceResponse<T, P, S, B, X, U>
where where
S: NewService<SrvConfig, Request = Request>, S: NewService<Request = Request>,
S::Error: Into<Error>, S::Error: Into<Error>,
S::InitError: fmt::Debug, S::InitError: fmt::Debug,
X: NewService<Request = Request, Response = Request>, X: NewService<Request = Request, Response = Request>,
@ -162,7 +164,7 @@ where
impl<T, P, S, B, X, U> Future for H1ServiceResponse<T, P, S, B, X, U> impl<T, P, S, B, X, U> Future for H1ServiceResponse<T, P, S, B, X, U>
where where
T: IoStream, T: IoStream,
S: NewService<SrvConfig, Request = Request>, S: NewService<Request = Request>,
S::Error: Into<Error>, S::Error: Into<Error>,
S::Response: Into<Response<B>>, S::Response: Into<Response<B>>,
S::InitError: fmt::Debug, S::InitError: fmt::Debug,
@ -320,10 +322,11 @@ where
} }
} }
impl<T, P> NewService<SrvConfig> for OneRequest<T, P> impl<T, P> NewService for OneRequest<T, P>
where where
T: IoStream, T: IoStream,
{ {
type Config = SrvConfig;
type Request = Io<T, P>; type Request = Io<T, P>;
type Response = (Request, Framed<T, Codec>); type Response = (Request, Framed<T, Codec>);
type Error = ParseError; type Error = ParseError;

View File

@ -1,6 +1,7 @@
use std::marker::PhantomData; use std::marker::PhantomData;
use actix_codec::Framed; use actix_codec::Framed;
use actix_server_config::ServerConfig;
use actix_service::{NewService, Service}; use actix_service::{NewService, Service};
use futures::future::FutureResult; use futures::future::FutureResult;
use futures::{Async, Poll}; use futures::{Async, Poll};
@ -12,6 +13,7 @@ use crate::request::Request;
pub struct UpgradeHandler<T>(PhantomData<T>); pub struct UpgradeHandler<T>(PhantomData<T>);
impl<T> NewService for UpgradeHandler<T> { impl<T> NewService for UpgradeHandler<T> {
type Config = ServerConfig;
type Request = (Request, Framed<T, Codec>); type Request = (Request, Framed<T, Codec>);
type Response = (); type Response = ();
type Error = Error; type Error = Error;
@ -19,7 +21,7 @@ impl<T> NewService for UpgradeHandler<T> {
type InitError = Error; type InitError = Error;
type Future = FutureResult<Self::Service, Self::InitError>; type Future = FutureResult<Self::Service, Self::InitError>;
fn new_service(&self, _: &()) -> Self::Future { fn new_service(&self, _: &ServerConfig) -> Self::Future {
unimplemented!() unimplemented!()
} }
} }

View File

@ -31,14 +31,14 @@ pub struct H2Service<T, P, S, B> {
impl<T, P, S, B> H2Service<T, P, S, B> impl<T, P, S, B> H2Service<T, P, S, B>
where where
S: NewService<SrvConfig, Request = Request>, S: NewService<Config = SrvConfig, Request = Request>,
S::Error: Into<Error>, S::Error: Into<Error>,
S::Response: Into<Response<B>>, S::Response: Into<Response<B>>,
<S::Service as Service>::Future: 'static, <S::Service as Service>::Future: 'static,
B: MessageBody + 'static, B: MessageBody + 'static,
{ {
/// Create new `HttpService` instance. /// Create new `HttpService` instance.
pub fn new<F: IntoNewService<S, SrvConfig>>(service: F) -> Self { pub fn new<F: IntoNewService<S>>(service: F) -> Self {
let cfg = ServiceConfig::new(KeepAlive::Timeout(5), 5000, 0); let cfg = ServiceConfig::new(KeepAlive::Timeout(5), 5000, 0);
H2Service { H2Service {
@ -49,10 +49,7 @@ where
} }
/// Create new `HttpService` instance with config. /// Create new `HttpService` instance with config.
pub fn with_config<F: IntoNewService<S, SrvConfig>>( pub fn with_config<F: IntoNewService<S>>(cfg: ServiceConfig, service: F) -> Self {
cfg: ServiceConfig,
service: F,
) -> Self {
H2Service { H2Service {
cfg, cfg,
srv: service.into_new_service(), srv: service.into_new_service(),
@ -61,15 +58,16 @@ where
} }
} }
impl<T, P, S, B> NewService<SrvConfig> for H2Service<T, P, S, B> impl<T, P, S, B> NewService for H2Service<T, P, S, B>
where where
T: IoStream, T: IoStream,
S: NewService<SrvConfig, Request = Request>, S: NewService<Config = SrvConfig, Request = Request>,
S::Error: Into<Error>, S::Error: Into<Error>,
S::Response: Into<Response<B>>, S::Response: Into<Response<B>>,
<S::Service as Service>::Future: 'static, <S::Service as Service>::Future: 'static,
B: MessageBody + 'static, B: MessageBody + 'static,
{ {
type Config = SrvConfig;
type Request = Io<T, P>; type Request = Io<T, P>;
type Response = (); type Response = ();
type Error = DispatchError; type Error = DispatchError;
@ -87,7 +85,7 @@ where
} }
#[doc(hidden)] #[doc(hidden)]
pub struct H2ServiceResponse<T, P, S: NewService<SrvConfig, Request = Request>, B> { pub struct H2ServiceResponse<T, P, S: NewService, B> {
fut: <S::Future as IntoFuture>::Future, fut: <S::Future as IntoFuture>::Future,
cfg: Option<ServiceConfig>, cfg: Option<ServiceConfig>,
_t: PhantomData<(T, P, B)>, _t: PhantomData<(T, P, B)>,
@ -96,7 +94,7 @@ pub struct H2ServiceResponse<T, P, S: NewService<SrvConfig, Request = Request>,
impl<T, P, S, B> Future for H2ServiceResponse<T, P, S, B> impl<T, P, S, B> Future for H2ServiceResponse<T, P, S, B>
where where
T: IoStream, T: IoStream,
S: NewService<SrvConfig, Request = Request>, S: NewService<Config = SrvConfig, Request = Request>,
S::Error: Into<Error>, S::Error: Into<Error>,
S::Response: Into<Response<B>>, S::Response: Into<Response<B>>,
<S::Service as Service>::Future: 'static, <S::Service as Service>::Future: 'static,

View File

@ -30,7 +30,7 @@ pub struct HttpService<T, P, S, B, X = h1::ExpectHandler, U = h1::UpgradeHandler
impl<T, S, B> HttpService<T, (), S, B> impl<T, S, B> HttpService<T, (), S, B>
where where
S: NewService<SrvConfig, Request = Request>, S: NewService<Config = SrvConfig, Request = Request>,
S::Error: Into<Error>, S::Error: Into<Error>,
S::InitError: fmt::Debug, S::InitError: fmt::Debug,
S::Response: Into<Response<B>>, S::Response: Into<Response<B>>,
@ -45,7 +45,7 @@ where
impl<T, P, S, B> HttpService<T, P, S, B> impl<T, P, S, B> HttpService<T, P, S, B>
where where
S: NewService<SrvConfig, Request = Request>, S: NewService<Config = SrvConfig, Request = Request>,
S::Error: Into<Error>, S::Error: Into<Error>,
S::InitError: fmt::Debug, S::InitError: fmt::Debug,
S::Response: Into<Response<B>>, S::Response: Into<Response<B>>,
@ -53,7 +53,7 @@ where
B: MessageBody + 'static, B: MessageBody + 'static,
{ {
/// Create new `HttpService` instance. /// Create new `HttpService` instance.
pub fn new<F: IntoNewService<S, SrvConfig>>(service: F) -> Self { pub fn new<F: IntoNewService<S>>(service: F) -> Self {
let cfg = ServiceConfig::new(KeepAlive::Timeout(5), 5000, 0); let cfg = ServiceConfig::new(KeepAlive::Timeout(5), 5000, 0);
HttpService { HttpService {
@ -66,7 +66,7 @@ where
} }
/// Create new `HttpService` instance with config. /// Create new `HttpService` instance with config.
pub(crate) fn with_config<F: IntoNewService<S, SrvConfig>>( pub(crate) fn with_config<F: IntoNewService<S>>(
cfg: ServiceConfig, cfg: ServiceConfig,
service: F, service: F,
) -> Self { ) -> Self {
@ -82,7 +82,7 @@ where
impl<T, P, S, B, X, U> HttpService<T, P, S, B, X, U> impl<T, P, S, B, X, U> HttpService<T, P, S, B, X, U>
where where
S: NewService<SrvConfig, Request = Request>, S: NewService<Config = SrvConfig, Request = Request>,
S::Error: Into<Error>, S::Error: Into<Error>,
S::InitError: fmt::Debug, S::InitError: fmt::Debug,
S::Response: Into<Response<B>>, S::Response: Into<Response<B>>,
@ -95,7 +95,7 @@ where
/// request will be forwarded to main service. /// request will be forwarded to main service.
pub fn expect<X1>(self, expect: X1) -> HttpService<T, P, S, B, X1, U> pub fn expect<X1>(self, expect: X1) -> HttpService<T, P, S, B, X1, U>
where where
X1: NewService<Request = Request, Response = Request>, X1: NewService<Config = SrvConfig, Request = Request, Response = Request>,
X1::Error: Into<Error>, X1::Error: Into<Error>,
X1::InitError: fmt::Debug, X1::InitError: fmt::Debug,
{ {
@ -114,7 +114,11 @@ where
/// and this service get called with original request and framed object. /// and this service get called with original request and framed object.
pub fn upgrade<U1>(self, upgrade: Option<U1>) -> HttpService<T, P, S, B, X, U1> pub fn upgrade<U1>(self, upgrade: Option<U1>) -> HttpService<T, P, S, B, X, U1>
where where
U1: NewService<Request = (Request, Framed<T, h1::Codec>), Response = ()>, U1: NewService<
Config = SrvConfig,
Request = (Request, Framed<T, h1::Codec>),
Response = (),
>,
U1::Error: fmt::Display, U1::Error: fmt::Display,
U1::InitError: fmt::Debug, U1::InitError: fmt::Debug,
{ {
@ -128,22 +132,27 @@ where
} }
} }
impl<T, P, S, B, X, U> NewService<SrvConfig> for HttpService<T, P, S, B, X, U> impl<T, P, S, B, X, U> NewService for HttpService<T, P, S, B, X, U>
where where
T: IoStream, T: IoStream,
S: NewService<SrvConfig, Request = Request>, S: NewService<Config = SrvConfig, Request = Request>,
S::Error: Into<Error>, S::Error: Into<Error>,
S::InitError: fmt::Debug, S::InitError: fmt::Debug,
S::Response: Into<Response<B>>, S::Response: Into<Response<B>>,
<S::Service as Service>::Future: 'static, <S::Service as Service>::Future: 'static,
B: MessageBody + 'static, B: MessageBody + 'static,
X: NewService<Request = Request, Response = Request>, X: NewService<Config = SrvConfig, Request = Request, Response = Request>,
X::Error: Into<Error>, X::Error: Into<Error>,
X::InitError: fmt::Debug, X::InitError: fmt::Debug,
U: NewService<Request = (Request, Framed<T, h1::Codec>), Response = ()>, U: NewService<
Config = SrvConfig,
Request = (Request, Framed<T, h1::Codec>),
Response = (),
>,
U::Error: fmt::Display, U::Error: fmt::Display,
U::InitError: fmt::Debug, U::InitError: fmt::Debug,
{ {
type Config = SrvConfig;
type Request = ServerIo<T, P>; type Request = ServerIo<T, P>;
type Response = (); type Response = ();
type Error = DispatchError; type Error = DispatchError;
@ -154,8 +163,8 @@ where
fn new_service(&self, cfg: &SrvConfig) -> Self::Future { fn new_service(&self, cfg: &SrvConfig) -> Self::Future {
HttpServiceResponse { HttpServiceResponse {
fut: self.srv.new_service(cfg).into_future(), fut: self.srv.new_service(cfg).into_future(),
fut_ex: Some(self.expect.new_service(&())), fut_ex: Some(self.expect.new_service(cfg)),
fut_upg: self.upgrade.as_ref().map(|f| f.new_service(&())), fut_upg: self.upgrade.as_ref().map(|f| f.new_service(cfg)),
expect: None, expect: None,
upgrade: None, upgrade: None,
cfg: Some(self.cfg.clone()), cfg: Some(self.cfg.clone()),
@ -165,14 +174,7 @@ where
} }
#[doc(hidden)] #[doc(hidden)]
pub struct HttpServiceResponse< pub struct HttpServiceResponse<T, P, S: NewService, B, X: NewService, U: NewService> {
T,
P,
S: NewService<SrvConfig>,
B,
X: NewService,
U: NewService,
> {
fut: S::Future, fut: S::Future,
fut_ex: Option<X::Future>, fut_ex: Option<X::Future>,
fut_upg: Option<U::Future>, fut_upg: Option<U::Future>,
@ -185,7 +187,7 @@ pub struct HttpServiceResponse<
impl<T, P, S, B, X, U> Future for HttpServiceResponse<T, P, S, B, X, U> impl<T, P, S, B, X, U> Future for HttpServiceResponse<T, P, S, B, X, U>
where where
T: IoStream, T: IoStream,
S: NewService<SrvConfig, Request = Request>, S: NewService<Request = Request>,
S::Error: Into<Error>, S::Error: Into<Error>,
S::InitError: fmt::Debug, S::InitError: fmt::Debug,
S::Response: Into<Response<B>>, S::Response: Into<Response<B>>,

View File

@ -5,7 +5,7 @@ use std::{net, thread};
use actix_codec::{AsyncRead, AsyncWrite}; use actix_codec::{AsyncRead, AsyncWrite};
use actix_http_test::TestServer; use actix_http_test::TestServer;
use actix_server_config::ServerConfig; use actix_server_config::ServerConfig;
use actix_service::{fn_cfg_factory, fn_service, NewService}; use actix_service::{new_service_cfg, service_fn, NewService};
use bytes::{Bytes, BytesMut}; use bytes::{Bytes, BytesMut};
use futures::future::{self, ok, Future}; use futures::future::{self, ok, Future};
use futures::stream::{once, Stream}; use futures::stream::{once, Stream};
@ -163,7 +163,7 @@ fn test_h2_body() -> std::io::Result<()> {
fn test_expect_continue() { fn test_expect_continue() {
let srv = TestServer::new(|| { let srv = TestServer::new(|| {
HttpService::build() HttpService::build()
.expect(fn_service(|req: Request| { .expect(service_fn(|req: Request| {
if req.head().uri.query() == Some("yes=") { if req.head().uri.query() == Some("yes=") {
Ok(req) Ok(req)
} else { } else {
@ -190,7 +190,7 @@ fn test_expect_continue() {
fn test_expect_continue_h1() { fn test_expect_continue_h1() {
let srv = TestServer::new(|| { let srv = TestServer::new(|| {
HttpService::build() HttpService::build()
.expect(fn_service(|req: Request| { .expect(service_fn(|req: Request| {
sleep(Duration::from_millis(20)).then(move |_| { sleep(Duration::from_millis(20)).then(move |_| {
if req.head().uri.query() == Some("yes=") { if req.head().uri.query() == Some("yes=") {
Ok(req) Ok(req)
@ -912,7 +912,7 @@ fn test_h1_body_chunked_implicit() {
#[test] #[test]
fn test_h1_response_http_error_handling() { fn test_h1_response_http_error_handling() {
let mut srv = TestServer::new(|| { let mut srv = TestServer::new(|| {
HttpService::build().h1(fn_cfg_factory(|_: &ServerConfig| { HttpService::build().h1(new_service_cfg(|_: &ServerConfig| {
Ok::<_, ()>(|_| { Ok::<_, ()>(|_| {
let broken_header = Bytes::from_static(b"\0\0\0"); let broken_header = Bytes::from_static(b"\0\0\0");
ok::<_, ()>( ok::<_, ()>(
@ -943,7 +943,7 @@ fn test_h2_response_http_error_handling() {
.map_err(|e| println!("Openssl error: {}", e)) .map_err(|e| println!("Openssl error: {}", e))
.and_then( .and_then(
HttpService::build() HttpService::build()
.h2(fn_cfg_factory(|_: &ServerConfig| { .h2(new_service_cfg(|_: &ServerConfig| {
Ok::<_, ()>(|_| { Ok::<_, ()>(|_| {
let broken_header = Bytes::from_static(b"\0\0\0"); let broken_header = Bytes::from_static(b"\0\0\0");
ok::<_, ()>( ok::<_, ()>(

View File

@ -1,5 +1,11 @@
# Changes # Changes
## [0.1.0-beta.4] - 2019-05-12
* Handle cancellation of uploads #834 #736
* Upgrade to actix-web 1.0.0-beta.4
## [0.1.0-beta.1] - 2019-04-21 ## [0.1.0-beta.1] - 2019-04-21
* Do not support nested multipart * Do not support nested multipart

View File

@ -1,6 +1,6 @@
[package] [package]
name = "actix-multipart" name = "actix-multipart"
version = "0.1.0-beta.1" version = "0.1.0-beta.4"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"] authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Multipart support for actix web framework." description = "Multipart support for actix web framework."
readme = "README.md" readme = "README.md"
@ -18,8 +18,8 @@ name = "actix_multipart"
path = "src/lib.rs" path = "src/lib.rs"
[dependencies] [dependencies]
actix-web = "1.0.0-beta.1" actix-web = "1.0.0-beta.5"
actix-service = "0.3.6" actix-service = "0.4.0"
bytes = "0.4" bytes = "0.4"
derive_more = "0.14" derive_more = "0.14"
httparse = "1.3" httparse = "1.3"
@ -31,4 +31,4 @@ twoway = "0.2"
[dev-dependencies] [dev-dependencies]
actix-rt = "0.2.2" actix-rt = "0.2.2"
actix-http = "0.1.1" actix-http = "0.2.0"

View File

@ -28,6 +28,9 @@ pub enum MultipartError {
/// Payload error /// Payload error
#[display(fmt = "{}", _0)] #[display(fmt = "{}", _0)]
Payload(PayloadError), Payload(PayloadError),
/// Not consumed
#[display(fmt = "Multipart stream is not consumed")]
NotConsumed,
} }
/// Return `BadRequest` for `MultipartError` /// Return `BadRequest` for `MultipartError`

View File

@ -1,5 +1,5 @@
//! Multipart payload support //! Multipart payload support
use std::cell::{RefCell, UnsafeCell}; use std::cell::{Cell, RefCell, UnsafeCell};
use std::marker::PhantomData; use std::marker::PhantomData;
use std::rc::Rc; use std::rc::Rc;
use std::{cmp, fmt}; use std::{cmp, fmt};
@ -116,6 +116,8 @@ impl Stream for Multipart {
payload.poll_stream()?; payload.poll_stream()?;
} }
inner.poll(&self.safety) inner.poll(&self.safety)
} else if !self.safety.is_clean() {
Err(MultipartError::NotConsumed)
} else { } else {
Ok(Async::NotReady) Ok(Async::NotReady)
} }
@ -415,6 +417,8 @@ impl Stream for Field {
} }
inner.poll(&self.safety) inner.poll(&self.safety)
} else if !self.safety.is_clean() {
return Err(MultipartError::NotConsumed);
} else { } else {
Ok(Async::NotReady) Ok(Async::NotReady)
} }
@ -655,6 +659,7 @@ struct Safety {
task: Option<Task>, task: Option<Task>,
level: usize, level: usize,
payload: Rc<PhantomData<bool>>, payload: Rc<PhantomData<bool>>,
clean: Rc<Cell<bool>>,
} }
impl Safety { impl Safety {
@ -663,12 +668,17 @@ impl Safety {
Safety { Safety {
task: None, task: None,
level: Rc::strong_count(&payload), level: Rc::strong_count(&payload),
clean: Rc::new(Cell::new(true)),
payload, payload,
} }
} }
fn current(&self) -> bool { fn current(&self) -> bool {
Rc::strong_count(&self.payload) == self.level Rc::strong_count(&self.payload) == self.level && self.clean.get()
}
fn is_clean(&self) -> bool {
self.clean.get()
} }
} }
@ -678,6 +688,7 @@ impl Clone for Safety {
Safety { Safety {
task: Some(current_task()), task: Some(current_task()),
level: Rc::strong_count(&payload), level: Rc::strong_count(&payload),
clean: self.clean.clone(),
payload, payload,
} }
} }
@ -687,7 +698,7 @@ impl Drop for Safety {
fn drop(&mut self) { fn drop(&mut self) {
// parent task is dead // parent task is dead
if Rc::strong_count(&self.payload) != self.level { if Rc::strong_count(&self.payload) != self.level {
panic!("Safety get dropped but it is not from top-most task"); self.clean.set(true);
} }
if let Some(task) = self.task.take() { if let Some(task) = self.task.take() {
task.notify() task.notify()

View File

@ -1,5 +1,9 @@
# Changes # Changes
## [0.1.0-beta.4] - 2019-05-12
* Use actix-web 1.0.0-beta.4
## [0.1.0-beta.2] - 2019-04-28 ## [0.1.0-beta.2] - 2019-04-28
* Add helper trait `UserSession` which allows to get session for ServiceRequest and HttpRequest * Add helper trait `UserSession` which allows to get session for ServiceRequest and HttpRequest

View File

@ -1,6 +1,6 @@
[package] [package]
name = "actix-session" name = "actix-session"
version = "0.1.0-beta.2" version = "0.1.0-beta.4"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"] authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Session for actix web framework." description = "Session for actix web framework."
readme = "README.md" readme = "README.md"
@ -24,12 +24,12 @@ default = ["cookie-session"]
cookie-session = ["actix-web/secure-cookies"] cookie-session = ["actix-web/secure-cookies"]
[dependencies] [dependencies]
actix-web = "1.0.0-beta.2" actix-web = "1.0.0-beta.5"
actix-service = "0.3.4" actix-service = "0.4.0"
bytes = "0.4" bytes = "0.4"
derive_more = "0.14" derive_more = "0.14"
futures = "0.1.25" futures = "0.1.25"
hashbrown = "0.2.2" hashbrown = "0.3.0"
serde = "1.0" serde = "1.0"
serde_json = "1.0" serde_json = "1.0"
time = "0.1.42" time = "0.1.42"

View File

@ -1,6 +1,6 @@
[package] [package]
name = "actix-web-actors" name = "actix-web-actors"
version = "1.0.0-alpha.3" version = "1.0.0-beta.4"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"] authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Actix actors support for actix web framework." description = "Actix actors support for actix web framework."
readme = "README.md" readme = "README.md"
@ -18,13 +18,13 @@ name = "actix_web_actors"
path = "src/lib.rs" path = "src/lib.rs"
[dependencies] [dependencies]
actix = "0.8.0" actix = "0.8.2"
actix-web = "1.0.0-beta.1" actix-web = "1.0.0-beta.5"
actix-http = "0.1.1" actix-http = "0.2.0"
actix-codec = "0.1.2" actix-codec = "0.1.2"
bytes = "0.4" bytes = "0.4"
futures = "0.1.25" futures = "0.1.25"
[dev-dependencies] [dev-dependencies]
env_logger = "0.6" env_logger = "0.6"
actix-http-test = { version = "0.1.0", features=["ssl"] } actix-http-test = { version = "0.2.0", features=["ssl"] }

View File

@ -1,5 +1,9 @@
# Changes # Changes
## [0.1.0] - 2019-05-18
* Release
## [0.1.0-beta.1] - 2019-04-20 ## [0.1.0-beta.1] - 2019-04-20
* Gen code for actix-web 1.0.0-beta.1 * Gen code for actix-web 1.0.0-beta.1

View File

@ -1,6 +1,6 @@
[package] [package]
name = "actix-web-codegen" name = "actix-web-codegen"
version = "0.1.0-beta.1" version = "0.1.0"
description = "Actix web proc macros" description = "Actix web proc macros"
readme = "README.md" readme = "README.md"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"] authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
@ -16,7 +16,7 @@ quote = "0.6"
syn = { version = "0.15", features = ["full", "parsing"] } syn = { version = "0.15", features = ["full", "parsing"] }
[dev-dependencies] [dev-dependencies]
actix-web = { version = "1.0.0-alpha.6" } actix-web = { version = "1.0.0-beta.5" }
actix-http = { version = "0.1.1", features=["ssl"] } actix-http = { version = "0.2.0", features=["ssl"] }
actix-http-test = { version = "0.1.0", features=["ssl"] } actix-http-test = { version = "0.2.0", features=["ssl"] }
futures = { version = "0.1" } futures = { version = "0.1" }

View File

@ -1,7 +1,7 @@
use actix_http::HttpService; use actix_http::HttpService;
use actix_http_test::TestServer; use actix_http_test::TestServer;
use actix_web::{http, App, HttpResponse, Responder}; use actix_web::{http, web::Path, App, HttpResponse, Responder};
use actix_web_codegen::{get, post, put}; use actix_web_codegen::{delete, get, post, put};
use futures::{future, Future}; use futures::{future, Future};
#[get("/test")] #[get("/test")]
@ -29,6 +29,45 @@ fn auto_sync() -> impl Future<Item = HttpResponse, Error = actix_web::Error> {
future::ok(HttpResponse::Ok().finish()) future::ok(HttpResponse::Ok().finish())
} }
#[put("/test/{param}")]
fn put_param_test(_: Path<String>) -> impl Responder {
HttpResponse::Created()
}
#[delete("/test/{param}")]
fn delete_param_test(_: Path<String>) -> impl Responder {
HttpResponse::NoContent()
}
#[get("/test/{param}")]
fn get_param_test(_: Path<String>) -> impl Responder {
HttpResponse::Ok()
}
#[test]
fn test_params() {
let mut srv = TestServer::new(|| {
HttpService::new(
App::new()
.service(get_param_test)
.service(put_param_test)
.service(delete_param_test),
)
});
let request = srv.request(http::Method::GET, srv.url("/test/it"));
let response = srv.block_on(request.send()).unwrap();
assert_eq!(response.status(), http::StatusCode::OK);
let request = srv.request(http::Method::PUT, srv.url("/test/it"));
let response = srv.block_on(request.send()).unwrap();
assert_eq!(response.status(), http::StatusCode::CREATED);
let request = srv.request(http::Method::DELETE, srv.url("/test/it"));
let response = srv.block_on(request.send()).unwrap();
assert_eq!(response.status(), http::StatusCode::NO_CONTENT);
}
#[test] #[test]
fn test_body() { fn test_body() {
let mut srv = TestServer::new(|| { let mut srv = TestServer::new(|| {

View File

@ -1,9 +1,15 @@
# Changes # Changes
## [0.2.0] - 2019-05-12
### Added ### Added
* Allow to send headers in `Camel-Case` form. * Allow to send headers in `Camel-Case` form.
### Changed
* Upgrade actix-http dependency.
## [0.1.1] - 2019-04-19 ## [0.1.1] - 2019-04-19

View File

@ -1,6 +1,6 @@
[package] [package]
name = "awc" name = "awc"
version = "0.1.1" version = "0.2.0"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"] authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Actix http client." description = "Actix http client."
readme = "README.md" readme = "README.md"
@ -40,8 +40,8 @@ flate2-rust = ["actix-http/flate2-rust"]
[dependencies] [dependencies]
actix-codec = "0.1.2" actix-codec = "0.1.2"
actix-service = "0.3.6" actix-service = "0.4.0"
actix-http = "0.1.4" actix-http = "0.2.0"
base64 = "0.10.1" base64 = "0.10.1"
bytes = "0.4" bytes = "0.4"
derive_more = "0.14" derive_more = "0.14"
@ -58,11 +58,11 @@ openssl = { version="0.10", optional = true }
[dev-dependencies] [dev-dependencies]
actix-rt = "0.2.2" actix-rt = "0.2.2"
actix-web = { version = "1.0.0-beta.1", features=["ssl"] } actix-web = { version = "1.0.0-beta.4", features=["ssl"] }
actix-http = { version = "0.1.4", features=["ssl"] } actix-http = { version = "0.2.0", features=["ssl"] }
actix-http-test = { version = "0.1.1", features=["ssl"] } actix-http-test = { version = "0.2.0", features=["ssl"] }
actix-utils = "0.3.4" actix-utils = "0.4.0"
actix-server = { version = "0.4.3", features=["ssl"] } actix-server = { version = "0.5.0", features=["ssl"] }
brotli2 = { version="0.3.2" } brotli2 = { version="0.3.2" }
flate2 = { version="1.0.2" } flate2 = { version="1.0.2" }
env_logger = "0.6" env_logger = "0.6"

View File

@ -140,51 +140,46 @@ impl ClientBuilder {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::test;
#[test] #[test]
fn client_basic_auth() { fn client_basic_auth() {
test::run_on(|| { let client = ClientBuilder::new().basic_auth("username", Some("password"));
let client = ClientBuilder::new().basic_auth("username", Some("password")); assert_eq!(
assert_eq!( client
client .config
.config .headers
.headers .get(header::AUTHORIZATION)
.get(header::AUTHORIZATION) .unwrap()
.unwrap() .to_str()
.to_str() .unwrap(),
.unwrap(), "Basic dXNlcm5hbWU6cGFzc3dvcmQ="
"Basic dXNlcm5hbWU6cGFzc3dvcmQ=" );
);
let client = ClientBuilder::new().basic_auth("username", None); let client = ClientBuilder::new().basic_auth("username", None);
assert_eq!( assert_eq!(
client client
.config .config
.headers .headers
.get(header::AUTHORIZATION) .get(header::AUTHORIZATION)
.unwrap() .unwrap()
.to_str() .to_str()
.unwrap(), .unwrap(),
"Basic dXNlcm5hbWU=" "Basic dXNlcm5hbWU="
); );
});
} }
#[test] #[test]
fn client_bearer_auth() { fn client_bearer_auth() {
test::run_on(|| { let client = ClientBuilder::new().bearer_auth("someS3cr3tAutht0k3n");
let client = ClientBuilder::new().bearer_auth("someS3cr3tAutht0k3n"); assert_eq!(
assert_eq!( client
client .config
.config .headers
.headers .get(header::AUTHORIZATION)
.get(header::AUTHORIZATION) .unwrap()
.unwrap() .to_str()
.to_str() .unwrap(),
.unwrap(), "Bearer someS3cr3tAutht0k3n"
"Bearer someS3cr3tAutht0k3n" );
);
})
} }
} }

View File

@ -347,10 +347,11 @@ where
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use actix_http_test::block_on;
use futures::Async; use futures::Async;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{http::header, test::block_on, test::TestResponse}; use crate::{http::header, test::TestResponse};
#[test] #[test]
fn test_body() { fn test_body() {

View File

@ -6,39 +6,10 @@ use actix_http::http::header::{self, Header, HeaderValue, IntoHeaderValue};
use actix_http::http::{HeaderName, HttpTryFrom, StatusCode, Version}; use actix_http::http::{HeaderName, HttpTryFrom, StatusCode, Version};
use actix_http::{h1, Payload, ResponseHead}; use actix_http::{h1, Payload, ResponseHead};
use bytes::Bytes; use bytes::Bytes;
#[cfg(test)]
use futures::Future;
use percent_encoding::{percent_encode, USERINFO_ENCODE_SET}; use percent_encoding::{percent_encode, USERINFO_ENCODE_SET};
use crate::ClientResponse; use crate::ClientResponse;
#[cfg(test)]
thread_local! {
static RT: std::cell::RefCell<actix_rt::Runtime> = {
std::cell::RefCell::new(actix_rt::Runtime::new().unwrap())
};
}
#[cfg(test)]
pub(crate) fn run_on<F, R>(f: F) -> R
where
F: Fn() -> R,
{
RT.with(move |rt| {
rt.borrow_mut()
.block_on(futures::future::lazy(|| Ok::<_, ()>(f())))
})
.unwrap()
}
#[cfg(test)]
pub(crate) fn block_on<F>(f: F) -> Result<F::Item, F::Error>
where
F: Future,
{
RT.with(move |rt| rt.borrow_mut().block_on(f))
}
/// Test `ClientResponse` builder /// Test `ClientResponse` builder
pub struct TestResponse { pub struct TestResponse {
head: ResponseHead, head: ResponseHead,

View File

@ -318,7 +318,9 @@ impl WebsocketsRequest {
} }
} else { } else {
log::trace!("Invalid connection header: {:?}", conn); log::trace!("Invalid connection header: {:?}", conn);
return Err(WsClientError::InvalidConnectionHeader(conn.clone())); return Err(WsClientError::InvalidConnectionHeader(
conn.clone(),
));
} }
} else { } else {
log::trace!("Missing connection header"); log::trace!("Missing connection header");
@ -462,29 +464,28 @@ mod tests {
#[test] #[test]
fn basics() { fn basics() {
actix_http_test::run_on(|| { let req = Client::new()
let req = Client::new() .ws("http://localhost/")
.ws("http://localhost/") .origin("test-origin")
.origin("test-origin") .max_frame_size(100)
.max_frame_size(100) .server_mode()
.server_mode() .protocols(&["v1", "v2"])
.protocols(&["v1", "v2"]) .set_header_if_none(header::CONTENT_TYPE, "json")
.set_header_if_none(header::CONTENT_TYPE, "json") .set_header_if_none(header::CONTENT_TYPE, "text")
.set_header_if_none(header::CONTENT_TYPE, "text") .cookie(Cookie::build("cookie1", "value1").finish());
.cookie(Cookie::build("cookie1", "value1").finish()); assert_eq!(
assert_eq!( req.origin.as_ref().unwrap().to_str().unwrap(),
req.origin.as_ref().unwrap().to_str().unwrap(), "test-origin"
"test-origin" );
); assert_eq!(req.max_size, 100);
assert_eq!(req.max_size, 100); assert_eq!(req.server_mode, true);
assert_eq!(req.server_mode, true); assert_eq!(req.protocols, Some("v1,v2".to_string()));
assert_eq!(req.protocols, Some("v1,v2".to_string())); assert_eq!(
assert_eq!( req.head.headers.get(header::CONTENT_TYPE).unwrap(),
req.head.headers.get(header::CONTENT_TYPE).unwrap(), header::HeaderValue::from_static("json")
header::HeaderValue::from_static("json") );
);
let _ = req.connect(); let _ = actix_http_test::block_fn(move || req.connect());
});
assert!(Client::new().ws("/").connect().poll().is_err()); assert!(Client::new().ws("/").connect().poll().is_err());
assert!(Client::new().ws("http:///test").connect().poll().is_err()); assert!(Client::new().ws("http:///test").connect().poll().is_err());

View File

@ -15,7 +15,7 @@ use rand::Rng;
use actix_codec::{AsyncRead, AsyncWrite}; use actix_codec::{AsyncRead, AsyncWrite};
use actix_http::HttpService; use actix_http::HttpService;
use actix_http_test::TestServer; use actix_http_test::TestServer;
use actix_service::{fn_service, NewService}; use actix_service::{service_fn, NewService};
use actix_web::http::{Cookie, Version}; use actix_web::http::{Cookie, Version};
use actix_web::middleware::{BodyEncoding, Compress}; use actix_web::middleware::{BodyEncoding, Compress};
use actix_web::{http::header, web, App, Error, HttpMessage, HttpRequest, HttpResponse}; use actix_web::{http::header, web, App, Error, HttpMessage, HttpRequest, HttpResponse};
@ -182,7 +182,7 @@ fn test_connection_reuse() {
let mut srv = TestServer::new(move || { let mut srv = TestServer::new(move || {
let num2 = num2.clone(); let num2 = num2.clone();
fn_service(move |io| { service_fn(move |io| {
num2.fetch_add(1, Ordering::Relaxed); num2.fetch_add(1, Ordering::Relaxed);
Ok(io) Ok(io)
}) })
@ -216,7 +216,7 @@ fn test_connection_reuse_h2() {
let mut srv = TestServer::new(move || { let mut srv = TestServer::new(move || {
let num2 = num2.clone(); let num2 = num2.clone();
fn_service(move |io| { service_fn(move |io| {
num2.fetch_add(1, Ordering::Relaxed); num2.fetch_add(1, Ordering::Relaxed);
Ok(io) Ok(io)
}) })
@ -268,7 +268,7 @@ fn test_connection_force_close() {
let mut srv = TestServer::new(move || { let mut srv = TestServer::new(move || {
let num2 = num2.clone(); let num2 = num2.clone();
fn_service(move |io| { service_fn(move |io| {
num2.fetch_add(1, Ordering::Relaxed); num2.fetch_add(1, Ordering::Relaxed);
Ok(io) Ok(io)
}) })
@ -300,7 +300,7 @@ fn test_connection_server_close() {
let mut srv = TestServer::new(move || { let mut srv = TestServer::new(move || {
let num2 = num2.clone(); let num2 = num2.clone();
fn_service(move |io| { service_fn(move |io| {
num2.fetch_add(1, Ordering::Relaxed); num2.fetch_add(1, Ordering::Relaxed);
Ok(io) Ok(io)
}) })
@ -335,7 +335,7 @@ fn test_connection_wait_queue() {
let mut srv = TestServer::new(move || { let mut srv = TestServer::new(move || {
let num2 = num2.clone(); let num2 = num2.clone();
fn_service(move |io| { service_fn(move |io| {
num2.fetch_add(1, Ordering::Relaxed); num2.fetch_add(1, Ordering::Relaxed);
Ok(io) Ok(io)
}) })
@ -380,7 +380,7 @@ fn test_connection_wait_queue_force_close() {
let mut srv = TestServer::new(move || { let mut srv = TestServer::new(move || {
let num2 = num2.clone(); let num2 = num2.clone();
fn_service(move |io| { service_fn(move |io| {
num2.fetch_add(1, Ordering::Relaxed); num2.fetch_add(1, Ordering::Relaxed);
Ok(io) Ok(io)
}) })

View File

@ -1,17 +1,11 @@
use std::io; use std::io;
use actix_codec::Framed; use actix_codec::Framed;
use actix_http::{body::BodySize, h1, ws, Error, HttpService, Request, Response};
use actix_http_test::TestServer; use actix_http_test::TestServer;
use actix_server::Io;
use actix_service::{fn_service, NewService};
use actix_utils::framed::IntoFramed;
use actix_utils::stream::TakeItem;
use bytes::{Bytes, BytesMut}; use bytes::{Bytes, BytesMut};
use futures::future::{ok, Either}; use futures::future::ok;
use futures::{Future, Sink, Stream}; use futures::{Future, Sink, Stream};
use tokio_tcp::TcpStream;
use actix_http::{body::BodySize, h1, ws, Request, ResponseError, ServiceConfig};
fn ws_service(req: ws::Frame) -> impl Future<Item = ws::Message, Error = io::Error> { fn ws_service(req: ws::Frame) -> impl Future<Item = ws::Message, Error = io::Error> {
match req { match req {
@ -37,52 +31,20 @@ fn ws_service(req: ws::Frame) -> impl Future<Item = ws::Message, Error = io::Err
#[test] #[test]
fn test_simple() { fn test_simple() {
let mut srv = TestServer::new(|| { let mut srv = TestServer::new(|| {
fn_service(|io: Io<TcpStream>| Ok(io.into_parts().0)) HttpService::build()
.and_then(IntoFramed::new(|| h1::Codec::new(ServiceConfig::default()))) .upgrade(|(req, framed): (Request, Framed<_, _>)| {
.and_then(TakeItem::new().map_err(|_| ())) let res = ws::handshake_response(req.head()).finish();
.and_then( // send handshake response
|(req, framed): (Option<h1::Message<Request>>, Framed<_, _>)| { framed
// validate request .send(h1::Message::Item((res.drop_body(), BodySize::None)))
if let Some(h1::Message::Item(req)) = req { .map_err(|e: io::Error| e.into())
match ws::verify_handshake(req.head()) { .and_then(|framed| {
Err(e) => { // start websocket service
// validation failed let framed = framed.into_framed(ws::Codec::new());
let res = e.error_response(); ws::Transport::with(framed, ws_service)
Either::A( })
framed })
.send(h1::Message::Item(( .finish(|_| ok::<_, Error>(Response::NotFound()))
res.drop_body(),
BodySize::Empty,
)))
.map_err(|_| ())
.map(|_| ()),
)
}
Ok(_) => {
let res = ws::handshake_response(req.head()).finish();
Either::B(
// send handshake response
framed
.send(h1::Message::Item((
res.drop_body(),
BodySize::None,
)))
.map_err(|_| ())
.and_then(|framed| {
// start websocket service
let framed =
framed.into_framed(ws::Codec::new());
ws::Transport::with(framed, ws_service)
.map_err(|_| ())
}),
)
}
}
} else {
panic!()
}
},
)
}); });
// client service // client service

View File

@ -4,7 +4,6 @@ use std::marker::PhantomData;
use std::rc::Rc; use std::rc::Rc;
use actix_http::body::{Body, MessageBody}; use actix_http::body::{Body, MessageBody};
use actix_server_config::ServerConfig;
use actix_service::boxed::{self, BoxedNewService}; use actix_service::boxed::{self, BoxedNewService};
use actix_service::{ use actix_service::{
apply_transform, IntoNewService, IntoTransform, NewService, Transform, apply_transform, IntoNewService, IntoTransform, NewService, Transform,
@ -59,6 +58,7 @@ impl<T, B> App<T, B>
where where
B: MessageBody, B: MessageBody,
T: NewService< T: NewService<
Config = (),
Request = ServiceRequest, Request = ServiceRequest,
Response = ServiceResponse<B>, Response = ServiceResponse<B>,
Error = Error, Error = Error,
@ -95,8 +95,8 @@ where
/// web::get().to(index))); /// web::get().to(index)));
/// } /// }
/// ``` /// ```
pub fn data<U: Into<Data<U>> + 'static>(mut self, data: U) -> Self { pub fn data<U: 'static>(mut self, data: U) -> Self {
self.data.push(Box::new(data.into())); self.data.push(Box::new(Data::new(data)));
self self
} }
@ -128,7 +128,7 @@ where
/// ``` /// ```
pub fn configure<F>(mut self, f: F) -> Self pub fn configure<F>(mut self, f: F) -> Self
where where
F: Fn(&mut ServiceConfig), F: FnOnce(&mut ServiceConfig),
{ {
let mut cfg = ServiceConfig::new(); let mut cfg = ServiceConfig::new();
f(&mut cfg); f(&mut cfg);
@ -234,6 +234,7 @@ where
where where
F: IntoNewService<U>, F: IntoNewService<U>,
U: NewService< U: NewService<
Config = (),
Request = ServiceRequest, Request = ServiceRequest,
Response = ServiceResponse, Response = ServiceResponse,
Error = Error, Error = Error,
@ -319,6 +320,7 @@ where
mw: F, mw: F,
) -> App< ) -> App<
impl NewService< impl NewService<
Config = (),
Request = ServiceRequest, Request = ServiceRequest,
Response = ServiceResponse<B1>, Response = ServiceResponse<B1>,
Error = Error, Error = Error,
@ -384,6 +386,7 @@ where
mw: F, mw: F,
) -> App< ) -> App<
impl NewService< impl NewService<
Config = (),
Request = ServiceRequest, Request = ServiceRequest,
Response = ServiceResponse<B1>, Response = ServiceResponse<B1>,
Error = Error, Error = Error,
@ -400,10 +403,11 @@ where
} }
} }
impl<T, B> IntoNewService<AppInit<T, B>, ServerConfig> for App<T, B> impl<T, B> IntoNewService<AppInit<T, B>> for App<T, B>
where where
B: MessageBody, B: MessageBody,
T: NewService< T: NewService<
Config = (),
Request = ServiceRequest, Request = ServiceRequest,
Response = ServiceResponse<B>, Response = ServiceResponse<B>,
Error = Error, Error = Error,

View File

@ -6,7 +6,7 @@ use actix_http::{Extensions, Request, Response};
use actix_router::{Path, ResourceDef, ResourceInfo, Router, Url}; use actix_router::{Path, ResourceDef, ResourceInfo, Router, Url};
use actix_server_config::ServerConfig; use actix_server_config::ServerConfig;
use actix_service::boxed::{self, BoxedNewService, BoxedService}; use actix_service::boxed::{self, BoxedNewService, BoxedService};
use actix_service::{fn_service, NewService, Service}; use actix_service::{service_fn, NewService, Service};
use futures::future::{ok, Either, FutureResult}; use futures::future::{ok, Either, FutureResult};
use futures::{Async, Future, Poll}; use futures::{Async, Future, Poll};
@ -31,6 +31,7 @@ type BoxedResponse = Either<
pub struct AppInit<T, B> pub struct AppInit<T, B>
where where
T: NewService< T: NewService<
Config = (),
Request = ServiceRequest, Request = ServiceRequest,
Response = ServiceResponse<B>, Response = ServiceResponse<B>,
Error = Error, Error = Error,
@ -46,15 +47,17 @@ where
pub(crate) external: RefCell<Vec<ResourceDef>>, pub(crate) external: RefCell<Vec<ResourceDef>>,
} }
impl<T, B> NewService<ServerConfig> for AppInit<T, B> impl<T, B> NewService for AppInit<T, B>
where where
T: NewService< T: NewService<
Config = (),
Request = ServiceRequest, Request = ServiceRequest,
Response = ServiceResponse<B>, Response = ServiceResponse<B>,
Error = Error, Error = Error,
InitError = (), InitError = (),
>, >,
{ {
type Config = ServerConfig;
type Request = Request; type Request = Request;
type Response = ServiceResponse<B>; type Response = ServiceResponse<B>;
type Error = T::Error; type Error = T::Error;
@ -65,7 +68,7 @@ where
fn new_service(&self, cfg: &ServerConfig) -> Self::Future { fn new_service(&self, cfg: &ServerConfig) -> Self::Future {
// update resource default service // update resource default service
let default = self.default.clone().unwrap_or_else(|| { let default = self.default.clone().unwrap_or_else(|| {
Rc::new(boxed::new_service(fn_service(|req: ServiceRequest| { Rc::new(boxed::new_service(service_fn(|req: ServiceRequest| {
Ok(req.into_response(Response::NotFound().finish())) Ok(req.into_response(Response::NotFound().finish()))
}))) })))
}); });
@ -148,6 +151,7 @@ where
impl<T, B> Future for AppInitResult<T, B> impl<T, B> Future for AppInitResult<T, B>
where where
T: NewService< T: NewService<
Config = (),
Request = ServiceRequest, Request = ServiceRequest,
Response = ServiceResponse<B>, Response = ServiceResponse<B>,
Error = Error, Error = Error,
@ -233,6 +237,7 @@ pub struct AppRoutingFactory {
} }
impl NewService for AppRoutingFactory { impl NewService for AppRoutingFactory {
type Config = ();
type Request = ServiceRequest; type Request = ServiceRequest;
type Response = ServiceResponse; type Response = ServiceResponse;
type Error = Error; type Error = Error;
@ -391,6 +396,7 @@ impl AppEntry {
} }
impl NewService for AppEntry { impl NewService for AppEntry {
type Config = ();
type Request = ServiceRequest; type Request = ServiceRequest;
type Response = ServiceResponse; type Response = ServiceResponse;
type Error = Error; type Error = Error;

View File

@ -31,7 +31,7 @@ pub struct AppService {
Option<Guards>, Option<Guards>,
Option<Rc<ResourceMap>>, Option<Rc<ResourceMap>>,
)>, )>,
route_data: Rc<Vec<Box<DataFactory>>>, service_data: Rc<Vec<Box<DataFactory>>>,
} }
impl AppService { impl AppService {
@ -39,12 +39,12 @@ impl AppService {
pub(crate) fn new( pub(crate) fn new(
config: AppConfig, config: AppConfig,
default: Rc<HttpNewService>, default: Rc<HttpNewService>,
route_data: Rc<Vec<Box<DataFactory>>>, service_data: Rc<Vec<Box<DataFactory>>>,
) -> Self { ) -> Self {
AppService { AppService {
config, config,
default, default,
route_data, service_data,
root: true, root: true,
services: Vec::new(), services: Vec::new(),
} }
@ -75,7 +75,7 @@ impl AppService {
default: self.default.clone(), default: self.default.clone(),
services: Vec::new(), services: Vec::new(),
root: false, root: false,
route_data: self.route_data.clone(), service_data: self.service_data.clone(),
} }
} }
@ -90,11 +90,11 @@ impl AppService {
} }
/// Set global route data /// Set global route data
pub fn set_route_data(&self, extensions: &mut Extensions) -> bool { pub fn set_service_data(&self, extensions: &mut Extensions) -> bool {
for f in self.route_data.iter() { for f in self.service_data.iter() {
f.create(extensions); f.create(extensions);
} }
!self.route_data.is_empty() !self.service_data.is_empty()
} }
/// Register http service /// Register http service
@ -107,6 +107,7 @@ impl AppService {
) where ) where
F: IntoNewService<S>, F: IntoNewService<S>,
S: NewService< S: NewService<
Config = (),
Request = ServiceRequest, Request = ServiceRequest,
Response = ServiceResponse, Response = ServiceResponse,
Error = Error, Error = Error,
@ -191,8 +192,8 @@ impl ServiceConfig {
/// by using `Data<T>` extractor where `T` is data type. /// by using `Data<T>` extractor where `T` is data type.
/// ///
/// This is same as `App::data()` method. /// This is same as `App::data()` method.
pub fn data<S: Into<Data<S>> + 'static>(&mut self, data: S) -> &mut Self { pub fn data<S: 'static>(&mut self, data: S) -> &mut Self {
self.data.push(Box::new(data.into())); self.data.push(Box::new(Data::new(data)));
self self
} }

View File

@ -93,12 +93,6 @@ impl<T> Clone for Data<T> {
} }
} }
impl<T> From<T> for Data<T> {
fn from(data: T) -> Self {
Data::new(data)
}
}
impl<T: 'static> FromRequest for Data<T> { impl<T: 'static> FromRequest for Data<T> {
type Config = (); type Config = ();
type Error = Error; type Error = Error;

View File

@ -6,6 +6,7 @@ use url::ParseError as UrlParseError;
use crate::http::StatusCode; use crate::http::StatusCode;
use crate::HttpResponse; use crate::HttpResponse;
use serde_urlencoded::de;
/// Errors which can occur when attempting to generate resource uri. /// Errors which can occur when attempting to generate resource uri.
#[derive(Debug, PartialEq, Display, From)] #[derive(Debug, PartialEq, Display, From)]
@ -91,6 +92,25 @@ impl ResponseError for JsonPayloadError {
} }
} }
/// A set of errors that can occur during parsing query strings
#[derive(Debug, Display, From)]
pub enum QueryPayloadError {
/// Deserialize error
#[display(fmt = "Query deserialize error: {}", _0)]
Deserialize(de::Error),
}
/// Return `BadRequest` for `QueryPayloadError`
impl ResponseError for QueryPayloadError {
fn error_response(&self) -> HttpResponse {
match *self {
QueryPayloadError::Deserialize(_) => {
HttpResponse::new(StatusCode::BAD_REQUEST)
}
}
}
}
/// Error type returned when reading body as lines. /// Error type returned when reading body as lines.
#[derive(From, Display, Debug)] #[derive(From, Display, Debug)]
pub enum ReadlinesError { pub enum ReadlinesError {
@ -143,6 +163,15 @@ mod tests {
assert_eq!(resp.status(), StatusCode::BAD_REQUEST); assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
} }
#[test]
fn test_query_payload_error() {
let resp: HttpResponse = QueryPayloadError::Deserialize(
serde_urlencoded::from_str::<i32>("bad query").unwrap_err(),
)
.error_response();
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
}
#[test] #[test]
fn test_readlines_error() { fn test_readlines_error() {
let resp: HttpResponse = ReadlinesError::LimitOverflow.error_response(); let resp: HttpResponse = ReadlinesError::LimitOverflow.error_response();

View File

@ -1,7 +1,8 @@
use std::convert::Infallible;
use std::marker::PhantomData; use std::marker::PhantomData;
use actix_http::{Error, Payload, Response}; use actix_http::{Error, Payload, Response};
use actix_service::{NewService, Service, Void}; use actix_service::{NewService, Service};
use futures::future::{ok, FutureResult}; use futures::future::{ok, FutureResult};
use futures::{try_ready, Async, Future, IntoFuture, Poll}; use futures::{try_ready, Async, Future, IntoFuture, Poll};
@ -71,7 +72,7 @@ where
{ {
type Request = (T, HttpRequest); type Request = (T, HttpRequest);
type Response = ServiceResponse; type Response = ServiceResponse;
type Error = Void; type Error = Infallible;
type Future = HandlerServiceResponse<<R::Future as IntoFuture>::Future>; type Future = HandlerServiceResponse<<R::Future as IntoFuture>::Future>;
fn poll_ready(&mut self) -> Poll<(), Self::Error> { fn poll_ready(&mut self) -> Poll<(), Self::Error> {
@ -98,7 +99,7 @@ where
T::Error: Into<Error>, T::Error: Into<Error>,
{ {
type Item = ServiceResponse; type Item = ServiceResponse;
type Error = Void; type Error = Infallible;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> { fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
match self.fut.poll() { match self.fut.poll() {
@ -191,7 +192,7 @@ where
{ {
type Request = (T, HttpRequest); type Request = (T, HttpRequest);
type Response = ServiceResponse; type Response = ServiceResponse;
type Error = Void; type Error = Infallible;
type Future = AsyncHandlerServiceResponse<R::Future>; type Future = AsyncHandlerServiceResponse<R::Future>;
fn poll_ready(&mut self) -> Poll<(), Self::Error> { fn poll_ready(&mut self) -> Poll<(), Self::Error> {
@ -225,7 +226,7 @@ where
T::Error: Into<Error>, T::Error: Into<Error>,
{ {
type Item = ServiceResponse; type Item = ServiceResponse;
type Error = Void; type Error = Infallible;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> { fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
if let Some(ref mut fut) = self.fut2 { if let Some(ref mut fut) = self.fut2 {
@ -280,9 +281,13 @@ impl<T: FromRequest, S> Extract<T, S> {
impl<T: FromRequest, S> NewService for Extract<T, S> impl<T: FromRequest, S> NewService for Extract<T, S>
where where
S: Service<Request = (T, HttpRequest), Response = ServiceResponse, Error = Void> S: Service<
+ Clone, Request = (T, HttpRequest),
Response = ServiceResponse,
Error = Infallible,
> + Clone,
{ {
type Config = ();
type Request = ServiceRequest; type Request = ServiceRequest;
type Response = ServiceResponse; type Response = ServiceResponse;
type Error = (Error, ServiceRequest); type Error = (Error, ServiceRequest);
@ -305,8 +310,11 @@ pub struct ExtractService<T: FromRequest, S> {
impl<T: FromRequest, S> Service for ExtractService<T, S> impl<T: FromRequest, S> Service for ExtractService<T, S>
where where
S: Service<Request = (T, HttpRequest), Response = ServiceResponse, Error = Void> S: Service<
+ Clone, Request = (T, HttpRequest),
Response = ServiceResponse,
Error = Infallible,
> + Clone,
{ {
type Request = ServiceRequest; type Request = ServiceRequest;
type Response = ServiceResponse; type Response = ServiceResponse;
@ -339,7 +347,11 @@ pub struct ExtractResponse<T: FromRequest, S: Service> {
impl<T: FromRequest, S> Future for ExtractResponse<T, S> impl<T: FromRequest, S> Future for ExtractResponse<T, S>
where where
S: Service<Request = (T, HttpRequest), Response = ServiceResponse, Error = Void>, S: Service<
Request = (T, HttpRequest),
Response = ServiceResponse,
Error = Infallible,
>,
{ {
type Item = ServiceResponse; type Item = ServiceResponse;
type Error = (Error, ServiceRequest); type Error = (Error, ServiceRequest);

View File

@ -805,14 +805,15 @@ where
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use actix_service::{FnService, Transform}; use actix_service::{IntoService, Transform};
use super::*; use super::*;
use crate::test::{self, block_on, TestRequest}; use crate::test::{self, block_on, TestRequest};
impl Cors { impl Cors {
fn finish<S, B>(self, srv: S) -> CorsMiddleware<S> fn finish<F, S, B>(self, srv: F) -> CorsMiddleware<S>
where where
F: IntoService<S>,
S: Service< S: Service<
Request = ServiceRequest, Request = ServiceRequest,
Response = ServiceResponse<B>, Response = ServiceResponse<B>,
@ -822,7 +823,8 @@ mod tests {
B: 'static, B: 'static,
{ {
block_on( block_on(
IntoTransform::<CorsFactory, S>::into_transform(self).new_transform(srv), IntoTransform::<CorsFactory, S>::into_transform(self)
.new_transform(srv.into_service()),
) )
.unwrap() .unwrap()
} }
@ -1063,11 +1065,11 @@ mod tests {
.allowed_headers(exposed_headers.clone()) .allowed_headers(exposed_headers.clone())
.expose_headers(exposed_headers.clone()) .expose_headers(exposed_headers.clone())
.allowed_header(header::CONTENT_TYPE) .allowed_header(header::CONTENT_TYPE)
.finish(FnService::new(move |req: ServiceRequest| { .finish(|req: ServiceRequest| {
req.into_response( req.into_response(
HttpResponse::Ok().header(header::VARY, "Accept").finish(), HttpResponse::Ok().header(header::VARY, "Accept").finish(),
) )
})); });
let req = TestRequest::with_header("Origin", "https://www.example.com") let req = TestRequest::with_header("Origin", "https://www.example.com")
.method(Method::OPTIONS) .method(Method::OPTIONS)
.to_srv_request(); .to_srv_request();

View File

@ -150,7 +150,7 @@ where
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use actix_service::FnService; use actix_service::IntoService;
use super::*; use super::*;
use crate::dev::ServiceRequest; use crate::dev::ServiceRequest;
@ -172,13 +172,13 @@ mod tests {
assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), "0001"); assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), "0001");
let req = TestRequest::default().to_srv_request(); let req = TestRequest::default().to_srv_request();
let srv = FnService::new(|req: ServiceRequest| { let srv = |req: ServiceRequest| {
req.into_response(HttpResponse::Ok().header(CONTENT_TYPE, "0002").finish()) req.into_response(HttpResponse::Ok().header(CONTENT_TYPE, "0002").finish())
}); };
let mut mw = block_on( let mut mw = block_on(
DefaultHeaders::new() DefaultHeaders::new()
.header(CONTENT_TYPE, "0001") .header(CONTENT_TYPE, "0001")
.new_transform(srv), .new_transform(srv.into_service()),
) )
.unwrap(); .unwrap();
let resp = block_on(mw.call(req)).unwrap(); let resp = block_on(mw.call(req)).unwrap();
@ -187,11 +187,13 @@ mod tests {
#[test] #[test]
fn test_content_type() { fn test_content_type() {
let srv = FnService::new(|req: ServiceRequest| { let srv = |req: ServiceRequest| req.into_response(HttpResponse::Ok().finish());
req.into_response(HttpResponse::Ok().finish()) let mut mw = block_on(
}); DefaultHeaders::new()
let mut mw = .content_type()
block_on(DefaultHeaders::new().content_type().new_transform(srv)).unwrap(); .new_transform(srv.into_service()),
)
.unwrap();
let req = TestRequest::default().to_srv_request(); let req = TestRequest::default().to_srv_request();
let resp = block_on(mw.call(req)).unwrap(); let resp = block_on(mw.call(req)).unwrap();

View File

@ -142,7 +142,7 @@ where
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use actix_service::FnService; use actix_service::IntoService;
use futures::future::ok; use futures::future::ok;
use super::*; use super::*;
@ -159,14 +159,14 @@ mod tests {
#[test] #[test]
fn test_handler() { fn test_handler() {
let srv = FnService::new(|req: ServiceRequest| { let srv = |req: ServiceRequest| {
req.into_response(HttpResponse::InternalServerError().finish()) req.into_response(HttpResponse::InternalServerError().finish())
}); };
let mut mw = test::block_on( let mut mw = test::block_on(
ErrorHandlers::new() ErrorHandlers::new()
.handler(StatusCode::INTERNAL_SERVER_ERROR, render_500) .handler(StatusCode::INTERNAL_SERVER_ERROR, render_500)
.new_transform(srv), .new_transform(srv.into_service()),
) )
.unwrap(); .unwrap();
@ -185,14 +185,14 @@ mod tests {
#[test] #[test]
fn test_handler_async() { fn test_handler_async() {
let srv = FnService::new(|req: ServiceRequest| { let srv = |req: ServiceRequest| {
req.into_response(HttpResponse::InternalServerError().finish()) req.into_response(HttpResponse::InternalServerError().finish())
}); };
let mut mw = test::block_on( let mut mw = test::block_on(
ErrorHandlers::new() ErrorHandlers::new()
.handler(StatusCode::INTERNAL_SERVER_ERROR, render_500_async) .handler(StatusCode::INTERNAL_SERVER_ERROR, render_500_async)
.new_transform(srv), .new_transform(srv.into_service()),
) )
.unwrap(); .unwrap();

View File

@ -457,7 +457,7 @@ impl<'a> fmt::Display for FormatDisplay<'a> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use actix_service::{FnService, Service, Transform}; use actix_service::{IntoService, Service, Transform};
use super::*; use super::*;
use crate::http::{header, StatusCode}; use crate::http::{header, StatusCode};
@ -465,16 +465,16 @@ mod tests {
#[test] #[test]
fn test_logger() { fn test_logger() {
let srv = FnService::new(|req: ServiceRequest| { let srv = |req: ServiceRequest| {
req.into_response( req.into_response(
HttpResponse::build(StatusCode::OK) HttpResponse::build(StatusCode::OK)
.header("X-Test", "ttt") .header("X-Test", "ttt")
.finish(), .finish(),
) )
}); };
let logger = Logger::new("%% %{User-Agent}i %{X-Test}o %{HOME}e %D test"); let logger = Logger::new("%% %{User-Agent}i %{X-Test}o %{HOME}e %D test");
let mut srv = block_on(logger.new_transform(srv)).unwrap(); let mut srv = block_on(logger.new_transform(srv.into_service())).unwrap();
let req = TestRequest::with_header( let req = TestRequest::with_header(
header::USER_AGENT, header::USER_AGENT,

View File

@ -100,7 +100,7 @@ where
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use actix_service::FnService; use actix_service::IntoService;
use super::*; use super::*;
use crate::dev::ServiceRequest; use crate::dev::ServiceRequest;
@ -122,12 +122,13 @@ mod tests {
#[test] #[test]
fn test_in_place_normalization() { fn test_in_place_normalization() {
let srv = FnService::new(|req: ServiceRequest| { let srv = |req: ServiceRequest| {
assert_eq!("/v1/something/", req.path()); assert_eq!("/v1/something/", req.path());
req.into_response(HttpResponse::Ok().finish()) req.into_response(HttpResponse::Ok().finish())
}); };
let mut normalize = block_on(NormalizePath.new_transform(srv)).unwrap(); let mut normalize =
block_on(NormalizePath.new_transform(srv.into_service())).unwrap();
let req = TestRequest::with_uri("/v1//something////").to_srv_request(); let req = TestRequest::with_uri("/v1//something////").to_srv_request();
let res = block_on(normalize.call(req)).unwrap(); let res = block_on(normalize.call(req)).unwrap();
@ -138,12 +139,13 @@ mod tests {
fn should_normalize_nothing() { fn should_normalize_nothing() {
const URI: &str = "/v1/something/"; const URI: &str = "/v1/something/";
let srv = FnService::new(|req: ServiceRequest| { let srv = |req: ServiceRequest| {
assert_eq!(URI, req.path()); assert_eq!(URI, req.path());
req.into_response(HttpResponse::Ok().finish()) req.into_response(HttpResponse::Ok().finish())
}); };
let mut normalize = block_on(NormalizePath.new_transform(srv)).unwrap(); let mut normalize =
block_on(NormalizePath.new_transform(srv.into_service())).unwrap();
let req = TestRequest::with_uri(URI).to_srv_request(); let req = TestRequest::with_uri(URI).to_srv_request();
let res = block_on(normalize.call(req)).unwrap(); let res = block_on(normalize.call(req)).unwrap();

View File

@ -75,6 +75,7 @@ impl Resource {
impl<T> Resource<T> impl<T> Resource<T>
where where
T: NewService< T: NewService<
Config = (),
Request = ServiceRequest, Request = ServiceRequest,
Response = ServiceResponse, Response = ServiceResponse,
Error = Error, Error = Error,
@ -274,6 +275,7 @@ where
mw: F, mw: F,
) -> Resource< ) -> Resource<
impl NewService< impl NewService<
Config = (),
Request = ServiceRequest, Request = ServiceRequest,
Response = ServiceResponse, Response = ServiceResponse,
Error = Error, Error = Error,
@ -340,6 +342,7 @@ where
mw: F, mw: F,
) -> Resource< ) -> Resource<
impl NewService< impl NewService<
Config = (),
Request = ServiceRequest, Request = ServiceRequest,
Response = ServiceResponse, Response = ServiceResponse,
Error = Error, Error = Error,
@ -360,6 +363,7 @@ where
where where
F: IntoNewService<U>, F: IntoNewService<U>,
U: NewService< U: NewService<
Config = (),
Request = ServiceRequest, Request = ServiceRequest,
Response = ServiceResponse, Response = ServiceResponse,
Error = Error, Error = Error,
@ -380,6 +384,7 @@ where
impl<T> HttpServiceFactory for Resource<T> impl<T> HttpServiceFactory for Resource<T>
where where
T: NewService< T: NewService<
Config = (),
Request = ServiceRequest, Request = ServiceRequest,
Response = ServiceResponse, Response = ServiceResponse,
Error = Error, Error = Error,
@ -402,7 +407,7 @@ where
} }
// custom app data storage // custom app data storage
if let Some(ref mut ext) = self.data { if let Some(ref mut ext) = self.data {
config.set_route_data(ext); config.set_service_data(ext);
} }
config.register_service(rdef, guards, self, None) config.register_service(rdef, guards, self, None)
} }
@ -411,6 +416,7 @@ where
impl<T> IntoNewService<T> for Resource<T> impl<T> IntoNewService<T> for Resource<T>
where where
T: NewService< T: NewService<
Config = (),
Request = ServiceRequest, Request = ServiceRequest,
Response = ServiceResponse, Response = ServiceResponse,
Error = Error, Error = Error,
@ -435,6 +441,7 @@ pub struct ResourceFactory {
} }
impl NewService for ResourceFactory { impl NewService for ResourceFactory {
type Config = ();
type Request = ServiceRequest; type Request = ServiceRequest;
type Response = ServiceResponse; type Response = ServiceResponse;
type Error = Error; type Error = Error;
@ -575,6 +582,7 @@ impl ResourceEndpoint {
} }
impl NewService for ResourceEndpoint { impl NewService for ResourceEndpoint {
type Config = ();
type Request = ServiceRequest; type Request = ServiceRequest;
type Response = ServiceResponse; type Response = ServiceResponse;
type Error = Error; type Error = Error;
@ -598,7 +606,7 @@ mod tests {
use crate::http::{header, HeaderValue, Method, StatusCode}; use crate::http::{header, HeaderValue, Method, StatusCode};
use crate::service::{ServiceRequest, ServiceResponse}; use crate::service::{ServiceRequest, ServiceResponse};
use crate::test::{call_service, init_service, TestRequest}; use crate::test::{call_service, init_service, TestRequest};
use crate::{web, App, Error, HttpResponse}; use crate::{guard, web, App, Error, HttpResponse};
fn md<S, B>( fn md<S, B>(
req: ServiceRequest, req: ServiceRequest,
@ -715,4 +723,45 @@ mod tests {
let resp = call_service(&mut srv, req); let resp = call_service(&mut srv, req);
assert_eq!(resp.status(), StatusCode::BAD_REQUEST); assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
} }
#[test]
fn test_resource_guards() {
let mut srv = init_service(
App::new()
.service(
web::resource("/test/{p}")
.guard(guard::Get())
.to(|| HttpResponse::Ok()),
)
.service(
web::resource("/test/{p}")
.guard(guard::Put())
.to(|| HttpResponse::Created()),
)
.service(
web::resource("/test/{p}")
.guard(guard::Delete())
.to(|| HttpResponse::NoContent()),
),
);
let req = TestRequest::with_uri("/test/it")
.method(Method::GET)
.to_request();
let resp = call_service(&mut srv, req);
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);
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);
assert_eq!(resp.status(), StatusCode::NO_CONTENT);
}
} }

View File

@ -26,6 +26,7 @@ type BoxedRouteService<Req, Res> = Box<
type BoxedRouteNewService<Req, Res> = Box< type BoxedRouteNewService<Req, Res> = Box<
NewService< NewService<
Config = (),
Request = Req, Request = Req,
Response = Res, Response = Res,
Error = Error, Error = Error,
@ -61,6 +62,7 @@ impl Route {
} }
impl NewService for Route { impl NewService for Route {
type Config = ();
type Request = ServiceRequest; type Request = ServiceRequest;
type Response = ServiceResponse; type Response = ServiceResponse;
type Error = Error; type Error = Error;
@ -283,6 +285,7 @@ where
impl<T> RouteNewService<T> impl<T> RouteNewService<T>
where where
T: NewService< T: NewService<
Config = (),
Request = ServiceRequest, Request = ServiceRequest,
Response = ServiceResponse, Response = ServiceResponse,
Error = (Error, ServiceRequest), Error = (Error, ServiceRequest),
@ -299,6 +302,7 @@ where
impl<T> NewService for RouteNewService<T> impl<T> NewService for RouteNewService<T>
where where
T: NewService< T: NewService<
Config = (),
Request = ServiceRequest, Request = ServiceRequest,
Response = ServiceResponse, Response = ServiceResponse,
Error = (Error, ServiceRequest), Error = (Error, ServiceRequest),
@ -307,6 +311,7 @@ where
T::Service: 'static, T::Service: 'static,
<T::Service as Service>::Future: 'static, <T::Service as Service>::Future: 'static,
{ {
type Config = ();
type Request = ServiceRequest; type Request = ServiceRequest;
type Response = ServiceResponse; type Response = ServiceResponse;
type Error = Error; type Error = Error;

View File

@ -2,15 +2,16 @@ use std::cell::RefCell;
use std::fmt; use std::fmt;
use std::rc::Rc; use std::rc::Rc;
use actix_http::Response; use actix_http::{Extensions, Response};
use actix_router::{ResourceDef, ResourceInfo, Router}; use actix_router::{ResourceDef, ResourceInfo, Router};
use actix_service::boxed::{self, BoxedNewService, BoxedService}; use actix_service::boxed::{self, BoxedNewService, BoxedService};
use actix_service::{ use actix_service::{
ApplyTransform, IntoNewService, IntoTransform, NewService, Service, Transform, apply_transform, IntoNewService, IntoTransform, NewService, Service, Transform,
}; };
use futures::future::{ok, Either, Future, FutureResult}; use futures::future::{ok, Either, Future, FutureResult};
use futures::{Async, IntoFuture, Poll}; use futures::{Async, IntoFuture, Poll};
use crate::data::Data;
use crate::dev::{AppService, HttpServiceFactory}; use crate::dev::{AppService, HttpServiceFactory};
use crate::error::Error; use crate::error::Error;
use crate::guard::Guard; use crate::guard::Guard;
@ -61,6 +62,7 @@ type BoxedResponse = Either<
pub struct Scope<T = ScopeEndpoint> { pub struct Scope<T = ScopeEndpoint> {
endpoint: T, endpoint: T,
rdef: String, rdef: String,
data: Option<Extensions>,
services: Vec<Box<ServiceFactory>>, services: Vec<Box<ServiceFactory>>,
guards: Vec<Box<Guard>>, guards: Vec<Box<Guard>>,
default: Rc<RefCell<Option<Rc<HttpNewService>>>>, default: Rc<RefCell<Option<Rc<HttpNewService>>>>,
@ -74,6 +76,7 @@ impl Scope {
Scope { Scope {
endpoint: ScopeEndpoint::new(fref.clone()), endpoint: ScopeEndpoint::new(fref.clone()),
rdef: path.to_string(), rdef: path.to_string(),
data: None,
guards: Vec::new(), guards: Vec::new(),
services: Vec::new(), services: Vec::new(),
default: Rc::new(RefCell::new(None)), default: Rc::new(RefCell::new(None)),
@ -85,6 +88,7 @@ impl Scope {
impl<T> Scope<T> impl<T> Scope<T>
where where
T: NewService< T: NewService<
Config = (),
Request = ServiceRequest, Request = ServiceRequest,
Response = ServiceResponse, Response = ServiceResponse,
Error = Error, Error = Error,
@ -116,6 +120,39 @@ where
self self
} }
/// Set or override application data. Application data could be accessed
/// by using `Data<T>` extractor where `T` is data type.
///
/// ```rust
/// use std::cell::Cell;
/// use actix_web::{web, App};
///
/// struct MyData {
/// counter: Cell<usize>,
/// }
///
/// fn index(data: web::Data<MyData>) {
/// data.counter.set(data.counter.get() + 1);
/// }
///
/// fn main() {
/// let app = App::new().service(
/// web::scope("/app")
/// .data(MyData{ counter: Cell::new(0) })
/// .service(
/// web::resource("/index.html").route(
/// web::get().to(index)))
/// );
/// }
/// ```
pub fn data<U: 'static>(mut self, data: U) -> Self {
if self.data.is_none() {
self.data = Some(Extensions::new());
}
self.data.as_mut().unwrap().insert(Data::new(data));
self
}
/// Register http service. /// Register http service.
/// ///
/// This is similar to `App's` service registration. /// This is similar to `App's` service registration.
@ -188,6 +225,7 @@ where
where where
F: IntoNewService<U>, F: IntoNewService<U>,
U: NewService< U: NewService<
Config = (),
Request = ServiceRequest, Request = ServiceRequest,
Response = ServiceResponse, Response = ServiceResponse,
Error = Error, Error = Error,
@ -218,6 +256,7 @@ where
mw: F, mw: F,
) -> Scope< ) -> Scope<
impl NewService< impl NewService<
Config = (),
Request = ServiceRequest, Request = ServiceRequest,
Response = ServiceResponse, Response = ServiceResponse,
Error = Error, Error = Error,
@ -234,10 +273,11 @@ where
>, >,
F: IntoTransform<M, T::Service>, F: IntoTransform<M, T::Service>,
{ {
let endpoint = ApplyTransform::new(mw, self.endpoint); let endpoint = apply_transform(mw, self.endpoint);
Scope { Scope {
endpoint, endpoint,
rdef: self.rdef, rdef: self.rdef,
data: self.data,
guards: self.guards, guards: self.guards,
services: self.services, services: self.services,
default: self.default, default: self.default,
@ -280,6 +320,7 @@ where
mw: F, mw: F,
) -> Scope< ) -> Scope<
impl NewService< impl NewService<
Config = (),
Request = ServiceRequest, Request = ServiceRequest,
Response = ServiceResponse, Response = ServiceResponse,
Error = Error, Error = Error,
@ -297,13 +338,14 @@ where
impl<T> HttpServiceFactory for Scope<T> impl<T> HttpServiceFactory for Scope<T>
where where
T: NewService< T: NewService<
Config = (),
Request = ServiceRequest, Request = ServiceRequest,
Response = ServiceResponse, Response = ServiceResponse,
Error = Error, Error = Error,
InitError = (), InitError = (),
> + 'static, > + 'static,
{ {
fn register(self, config: &mut AppService) { fn register(mut self, config: &mut AppService) {
// update default resource if needed // update default resource if needed
if self.default.borrow().is_none() { if self.default.borrow().is_none() {
*self.default.borrow_mut() = Some(config.default_service()); *self.default.borrow_mut() = Some(config.default_service());
@ -317,8 +359,14 @@ where
let mut rmap = ResourceMap::new(ResourceDef::root_prefix(&self.rdef)); let mut rmap = ResourceMap::new(ResourceDef::root_prefix(&self.rdef));
// custom app data storage
if let Some(ref mut ext) = self.data {
config.set_service_data(ext);
}
// complete scope pipeline creation // complete scope pipeline creation
*self.factory_ref.borrow_mut() = Some(ScopeFactory { *self.factory_ref.borrow_mut() = Some(ScopeFactory {
data: self.data.take().map(|data| Rc::new(data)),
default: self.default.clone(), default: self.default.clone(),
services: Rc::new( services: Rc::new(
cfg.into_services() cfg.into_services()
@ -350,11 +398,13 @@ where
} }
pub struct ScopeFactory { pub struct ScopeFactory {
data: Option<Rc<Extensions>>,
services: Rc<Vec<(ResourceDef, HttpNewService, RefCell<Option<Guards>>)>>, services: Rc<Vec<(ResourceDef, HttpNewService, RefCell<Option<Guards>>)>>,
default: Rc<RefCell<Option<Rc<HttpNewService>>>>, default: Rc<RefCell<Option<Rc<HttpNewService>>>>,
} }
impl NewService for ScopeFactory { impl NewService for ScopeFactory {
type Config = ();
type Request = ServiceRequest; type Request = ServiceRequest;
type Response = ServiceResponse; type Response = ServiceResponse;
type Error = Error; type Error = Error;
@ -382,6 +432,7 @@ impl NewService for ScopeFactory {
}) })
.collect(), .collect(),
default: None, default: None,
data: self.data.clone(),
default_fut, default_fut,
} }
} }
@ -391,6 +442,7 @@ impl NewService for ScopeFactory {
#[doc(hidden)] #[doc(hidden)]
pub struct ScopeFactoryResponse { pub struct ScopeFactoryResponse {
fut: Vec<CreateScopeServiceItem>, fut: Vec<CreateScopeServiceItem>,
data: Option<Rc<Extensions>>,
default: Option<HttpService>, default: Option<HttpService>,
default_fut: Option<Box<Future<Item = HttpService, Error = ()>>>, default_fut: Option<Box<Future<Item = HttpService, Error = ()>>>,
} }
@ -454,6 +506,7 @@ impl Future for ScopeFactoryResponse {
router router
}); });
Ok(Async::Ready(ScopeService { Ok(Async::Ready(ScopeService {
data: self.data.clone(),
router: router.finish(), router: router.finish(),
default: self.default.take(), default: self.default.take(),
_ready: None, _ready: None,
@ -465,6 +518,7 @@ impl Future for ScopeFactoryResponse {
} }
pub struct ScopeService { pub struct ScopeService {
data: Option<Rc<Extensions>>,
router: Router<HttpService, Vec<Box<Guard>>>, router: Router<HttpService, Vec<Box<Guard>>>,
default: Option<HttpService>, default: Option<HttpService>,
_ready: Option<(ServiceRequest, ResourceInfo)>, _ready: Option<(ServiceRequest, ResourceInfo)>,
@ -493,6 +547,9 @@ impl Service for ScopeService {
}); });
if let Some((srv, _info)) = res { if let Some((srv, _info)) = res {
if let Some(ref data) = self.data {
req.set_data_container(data.clone());
}
Either::A(srv.call(req)) Either::A(srv.call(req))
} else if let Some(ref mut default) = self.default { } else if let Some(ref mut default) = self.default {
Either::A(default.call(req)) Either::A(default.call(req))
@ -515,6 +572,7 @@ impl ScopeEndpoint {
} }
impl NewService for ScopeEndpoint { impl NewService for ScopeEndpoint {
type Config = ();
type Request = ServiceRequest; type Request = ServiceRequest;
type Response = ServiceResponse; type Response = ServiceResponse;
type Error = Error; type Error = Error;
@ -946,4 +1004,22 @@ mod tests {
HeaderValue::from_static("0001") HeaderValue::from_static("0001")
); );
} }
#[test]
fn test_override_data() {
let mut srv = init_service(App::new().data(1usize).service(
web::scope("app").data(10usize).route(
"/t",
web::get().to(|data: web::Data<usize>| {
assert_eq!(*data, 10);
let _ = data.clone();
HttpResponse::Ok()
}),
),
));
let req = TestRequest::with_uri("/app/t").to_request();
let resp = call_service(&mut srv, req);
assert_eq!(resp.status(), StatusCode::OK);
}
} }

View File

@ -51,8 +51,8 @@ struct Config {
pub struct HttpServer<F, I, S, B> pub struct HttpServer<F, I, S, B>
where where
F: Fn() -> I + Send + Clone + 'static, F: Fn() -> I + Send + Clone + 'static,
I: IntoNewService<S, ServerConfig>, I: IntoNewService<S>,
S: NewService<ServerConfig, Request = Request>, S: NewService<Config = ServerConfig, Request = Request>,
S::Error: Into<Error>, S::Error: Into<Error>,
S::InitError: fmt::Debug, S::InitError: fmt::Debug,
S::Response: Into<Response<B>>, S::Response: Into<Response<B>>,
@ -71,8 +71,8 @@ where
impl<F, I, S, B> HttpServer<F, I, S, B> impl<F, I, S, B> HttpServer<F, I, S, B>
where where
F: Fn() -> I + Send + Clone + 'static, F: Fn() -> I + Send + Clone + 'static,
I: IntoNewService<S, ServerConfig>, I: IntoNewService<S>,
S: NewService<ServerConfig, Request = Request>, S: NewService<Config = ServerConfig, Request = Request>,
S::Error: Into<Error>, S::Error: Into<Error>,
S::InitError: fmt::Debug, S::InitError: fmt::Debug,
S::Response: Into<Response<B>>, S::Response: Into<Response<B>>,
@ -442,8 +442,8 @@ where
impl<F, I, S, B> HttpServer<F, I, S, B> impl<F, I, S, B> HttpServer<F, I, S, B>
where where
F: Fn() -> I + Send + Clone + 'static, F: Fn() -> I + Send + Clone + 'static,
I: IntoNewService<S, ServerConfig>, I: IntoNewService<S>,
S: NewService<ServerConfig, Request = Request>, S: NewService<Config = ServerConfig, Request = Request>,
S::Error: Into<Error>, S::Error: Into<Error>,
S::InitError: fmt::Debug, S::InitError: fmt::Debug,
S::Response: Into<Response<B>>, S::Response: Into<Response<B>>,

View File

@ -442,6 +442,7 @@ impl WebService {
where where
F: IntoNewService<T>, F: IntoNewService<T>,
T: NewService< T: NewService<
Config = (),
Request = ServiceRequest, Request = ServiceRequest,
Response = ServiceResponse, Response = ServiceResponse,
Error = Error, Error = Error,
@ -467,6 +468,7 @@ struct WebServiceImpl<T> {
impl<T> HttpServiceFactory for WebServiceImpl<T> impl<T> HttpServiceFactory for WebServiceImpl<T>
where where
T: NewService< T: NewService<
Config = (),
Request = ServiceRequest, Request = ServiceRequest,
Response = ServiceResponse, Response = ServiceResponse,
Error = Error, Error = Error,

View File

@ -9,7 +9,7 @@ use actix_http::{cookie::Cookie, Extensions, Request};
use actix_router::{Path, ResourceDef, Url}; use actix_router::{Path, ResourceDef, Url};
use actix_rt::Runtime; use actix_rt::Runtime;
use actix_server_config::ServerConfig; use actix_server_config::ServerConfig;
use actix_service::{FnService, IntoNewService, NewService, Service}; use actix_service::{IntoNewService, IntoService, NewService, Service};
use bytes::{Bytes, BytesMut}; use bytes::{Bytes, BytesMut};
use futures::future::{lazy, ok, Future, IntoFuture}; use futures::future::{lazy, ok, Future, IntoFuture};
use futures::Stream; use futures::Stream;
@ -110,9 +110,10 @@ pub fn default_service(
status_code: StatusCode, status_code: StatusCode,
) -> impl Service<Request = ServiceRequest, Response = ServiceResponse<Body>, Error = Error> ) -> impl Service<Request = ServiceRequest, Response = ServiceResponse<Body>, Error = Error>
{ {
FnService::new(move |req: ServiceRequest| { (move |req: ServiceRequest| {
req.into_response(HttpResponse::build(status_code).finish()) req.into_response(HttpResponse::build(status_code).finish())
}) })
.into_service()
} }
/// This method accepts application builder instance, and constructs /// This method accepts application builder instance, and constructs
@ -141,9 +142,9 @@ pub fn init_service<R, S, B, E>(
app: R, app: R,
) -> impl Service<Request = Request, Response = ServiceResponse<B>, Error = E> ) -> impl Service<Request = Request, Response = ServiceResponse<B>, Error = E>
where where
R: IntoNewService<S, ServerConfig>, R: IntoNewService<S>,
S: NewService< S: NewService<
ServerConfig, Config = ServerConfig,
Request = Request, Request = Request,
Response = ServiceResponse<B>, Response = ServiceResponse<B>,
Error = E, Error = E,

View File

@ -1,6 +1,6 @@
//! Json extractor/responder //! Json extractor/responder
use std::rc::Rc; use std::sync::Arc;
use std::{fmt, ops}; use std::{fmt, ops};
use bytes::BytesMut; use bytes::BytesMut;
@ -168,15 +168,15 @@ impl<T> FromRequest for Json<T>
where where
T: DeserializeOwned + 'static, T: DeserializeOwned + 'static,
{ {
type Config = JsonConfig;
type Error = Error; type Error = Error;
type Future = Box<Future<Item = Self, Error = Error>>; type Future = Box<Future<Item = Self, Error = Error>>;
type Config = JsonConfig;
#[inline] #[inline]
fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future { fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {
let req2 = req.clone(); let req2 = req.clone();
let (limit, err) = req let (limit, err) = req
.app_data::<JsonConfig>() .app_data::<Self::Config>()
.map(|c| (c.limit, c.ehandler.clone())) .map(|c| (c.limit, c.ehandler.clone()))
.unwrap_or((32768, None)); .unwrap_or((32768, None));
@ -236,7 +236,7 @@ where
#[derive(Clone)] #[derive(Clone)]
pub struct JsonConfig { pub struct JsonConfig {
limit: usize, limit: usize,
ehandler: Option<Rc<Fn(JsonPayloadError, &HttpRequest) -> Error>>, ehandler: Option<Arc<dyn Fn(JsonPayloadError, &HttpRequest) -> Error + Send + Sync>>,
} }
impl JsonConfig { impl JsonConfig {
@ -249,9 +249,9 @@ impl JsonConfig {
/// Set custom error handler /// Set custom error handler
pub fn error_handler<F>(mut self, f: F) -> Self pub fn error_handler<F>(mut self, f: F) -> Self
where where
F: Fn(JsonPayloadError, &HttpRequest) -> Error + 'static, F: Fn(JsonPayloadError, &HttpRequest) -> Error + Send + Sync + 'static,
{ {
self.ehandler = Some(Rc::new(f)); self.ehandler = Some(Arc::new(f));
self self
} }
} }

View File

@ -11,4 +11,4 @@ pub use self::form::{Form, FormConfig};
pub use self::json::{Json, JsonConfig}; pub use self::json::{Json, JsonConfig};
pub use self::path::Path; pub use self::path::Path;
pub use self::payload::{Payload, PayloadConfig}; pub use self::payload::{Payload, PayloadConfig};
pub use self::query::Query; pub use self::query::{Query, QueryConfig};

View File

@ -1,5 +1,6 @@
//! Query extractor //! Query extractor
use std::sync::Arc;
use std::{fmt, ops}; use std::{fmt, ops};
use actix_http::error::Error; use actix_http::error::Error;
@ -7,6 +8,7 @@ use serde::de;
use serde_urlencoded; use serde_urlencoded;
use crate::dev::Payload; use crate::dev::Payload;
use crate::error::QueryPayloadError;
use crate::extract::FromRequest; use crate::extract::FromRequest;
use crate::request::HttpRequest; use crate::request::HttpRequest;
@ -50,6 +52,16 @@ impl<T> Query<T> {
pub fn into_inner(self) -> T { pub fn into_inner(self) -> T {
self.0 self.0
} }
/// Get query parameters from the path
pub fn from_query(query_str: &str) -> Result<Self, QueryPayloadError>
where
T: de::DeserializeOwned,
{
serde_urlencoded::from_str::<T>(query_str)
.map(|val| Ok(Query(val)))
.unwrap_or_else(move |e| Err(QueryPayloadError::Deserialize(e)))
}
} }
impl<T> ops::Deref for Query<T> { impl<T> ops::Deref for Query<T> {
@ -115,38 +127,123 @@ impl<T> FromRequest for Query<T>
where where
T: de::DeserializeOwned, T: de::DeserializeOwned,
{ {
type Config = ();
type Error = Error; type Error = Error;
type Future = Result<Self, Error>; type Future = Result<Self, Error>;
type Config = QueryConfig;
#[inline] #[inline]
fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future { fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
let error_handler = req
.app_data::<Self::Config>()
.map(|c| c.ehandler.clone())
.unwrap_or(None);
serde_urlencoded::from_str::<T>(req.query_string()) serde_urlencoded::from_str::<T>(req.query_string())
.map(|val| Ok(Query(val))) .map(|val| Ok(Query(val)))
.unwrap_or_else(|e| { .unwrap_or_else(move |e| {
let e = QueryPayloadError::Deserialize(e);
log::debug!( log::debug!(
"Failed during Query extractor deserialization. \ "Failed during Query extractor deserialization. \
Request path: {:?}", Request path: {:?}",
req.path() req.path()
); );
Err(e.into())
let e = if let Some(error_handler) = error_handler {
(error_handler)(e, req)
} else {
e.into()
};
Err(e)
}) })
} }
} }
/// Query extractor configuration
///
/// ```rust
/// #[macro_use] extern crate serde_derive;
/// use actix_web::{error, web, App, FromRequest, HttpResponse};
///
/// #[derive(Deserialize)]
/// struct Info {
/// username: String,
/// }
///
/// /// deserialize `Info` from request's querystring
/// fn index(info: web::Query<Info>) -> String {
/// format!("Welcome {}!", info.username)
/// }
///
/// fn main() {
/// let app = App::new().service(
/// web::resource("/index.html").data(
/// // change query extractor configuration
/// web::Query::<Info>::configure(|cfg| {
/// cfg.error_handler(|err, req| { // <- create custom error response
/// error::InternalError::from_response(
/// err, HttpResponse::Conflict().finish()).into()
/// })
/// }))
/// .route(web::post().to(index))
/// );
/// }
/// ```
#[derive(Clone)]
pub struct QueryConfig {
ehandler: Option<Arc<Fn(QueryPayloadError, &HttpRequest) -> Error + Send + Sync>>,
}
impl QueryConfig {
/// Set custom error handler
pub fn error_handler<F>(mut self, f: F) -> Self
where
F: Fn(QueryPayloadError, &HttpRequest) -> Error + Send + Sync + 'static,
{
self.ehandler = Some(Arc::new(f));
self
}
}
impl Default for QueryConfig {
fn default() -> Self {
QueryConfig { ehandler: None }
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use actix_http::http::StatusCode;
use derive_more::Display; use derive_more::Display;
use serde_derive::Deserialize; use serde_derive::Deserialize;
use super::*; use super::*;
use crate::error::InternalError;
use crate::test::TestRequest; use crate::test::TestRequest;
use crate::HttpResponse;
#[derive(Deserialize, Debug, Display)] #[derive(Deserialize, Debug, Display)]
struct Id { struct Id {
id: String, id: String,
} }
#[test]
fn test_service_request_extract() {
let req = TestRequest::with_uri("/name/user1/").to_srv_request();
assert!(Query::<Id>::from_query(&req.query_string()).is_err());
let req = TestRequest::with_uri("/name/user1/?id=test").to_srv_request();
let mut s = Query::<Id>::from_query(&req.query_string()).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] #[test]
fn test_request_extract() { fn test_request_extract() {
let req = TestRequest::with_uri("/name/user1/").to_srv_request(); let req = TestRequest::with_uri("/name/user1/").to_srv_request();
@ -164,4 +261,27 @@ mod tests {
let s = s.into_inner(); let s = s.into_inner();
assert_eq!(s.id, "test1"); assert_eq!(s.id, "test1");
} }
#[test]
fn test_custom_error_responder() {
let req = TestRequest::with_uri("/name/user1/")
.data(QueryConfig::default().error_handler(|e, _| {
let resp = HttpResponse::UnprocessableEntity().finish();
InternalError::from_response(e, resp).into()
}))
.to_srv_request();
let (req, mut pl) = req.into_parts();
let query = Query::<Id>::from_request(&req, &mut pl);
assert!(query.is_err());
assert_eq!(
query
.unwrap_err()
.as_response_error()
.error_response()
.status(),
StatusCode::UNPROCESSABLE_ENTITY
);
}
} }

View File

@ -1,5 +1,9 @@
# Changes # Changes
## [0.2.0] - 2019-05-12
* Update awc and actix-http deps
## [0.1.1] - 2019-04-24 ## [0.1.1] - 2019-04-24
* Always make new connection for http client * Always make new connection for http client

View File

@ -1,6 +1,6 @@
[package] [package]
name = "actix-http-test" name = "actix-http-test"
version = "0.1.1" version = "0.2.0"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"] authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Actix http test server" description = "Actix http test server"
readme = "README.md" readme = "README.md"
@ -32,10 +32,10 @@ ssl = ["openssl", "actix-server/ssl", "awc/ssl"]
[dependencies] [dependencies]
actix-codec = "0.1.2" actix-codec = "0.1.2"
actix-rt = "0.2.2" actix-rt = "0.2.2"
actix-service = "0.3.6" actix-service = "0.4.0"
actix-server = "0.4.3" actix-server = "0.5.0"
actix-utils = "0.3.5" actix-utils = "0.4.0"
awc = "0.1.1" awc = "0.2.0"
base64 = "0.10" base64 = "0.10"
bytes = "0.4" bytes = "0.4"
@ -55,5 +55,5 @@ tokio-timer = "0.2"
openssl = { version="0.10", optional = true } openssl = { version="0.10", optional = true }
[dev-dependencies] [dev-dependencies]
actix-web = "1.0.0-beta.1" actix-web = "1.0.0-beta.4"
actix-http = "0.1.2" actix-http = "0.2.0"

View File

@ -9,16 +9,30 @@ use actix_server::{Server, StreamServiceFactory};
use awc::{error::PayloadError, ws, Client, ClientRequest, ClientResponse, Connector}; use awc::{error::PayloadError, ws, Client, ClientRequest, ClientResponse, Connector};
use bytes::Bytes; use bytes::Bytes;
use futures::future::lazy; use futures::future::lazy;
use futures::{Future, Stream}; use futures::{Future, IntoFuture, Stream};
use http::Method; use http::Method;
use net2::TcpBuilder; use net2::TcpBuilder;
thread_local! { thread_local! {
static RT: RefCell<Runtime> = { static RT: RefCell<Inner> = {
RefCell::new(Runtime::new().unwrap()) RefCell::new(Inner(Some(Runtime::new().unwrap())))
}; };
} }
struct Inner(Option<Runtime>);
impl Inner {
fn get_mut(&mut self) -> &mut Runtime {
self.0.as_mut().unwrap()
}
}
impl Drop for Inner {
fn drop(&mut self) {
std::mem::forget(self.0.take().unwrap())
}
}
/// Runs the provided future, blocking the current thread until the future /// Runs the provided future, blocking the current thread until the future
/// completes. /// completes.
/// ///
@ -31,21 +45,27 @@ thread_local! {
/// This function panics on nested call. /// This function panics on nested call.
pub fn block_on<F>(f: F) -> Result<F::Item, F::Error> pub fn block_on<F>(f: F) -> Result<F::Item, F::Error>
where where
F: Future, F: IntoFuture,
{ {
RT.with(move |rt| rt.borrow_mut().block_on(f)) RT.with(move |rt| rt.borrow_mut().get_mut().block_on(f.into_future()))
} }
/// Runs the provided function, with runtime enabled. /// Runs the provided function, blocking the current thread until the resul
/// future completes.
///
/// This function can be used to synchronously block the current thread
/// until the provided `future` has resolved either successfully or with an
/// error. The result of the future is then returned from this function
/// call.
/// ///
/// Note that this function is intended to be used only for testing purpose. /// Note that this function is intended to be used only for testing purpose.
/// This function panics on nested call. /// This function panics on nested call.
pub fn run_on<F, R>(f: F) -> R pub fn block_fn<F, R>(f: F) -> Result<R::Item, R::Error>
where where
F: Fn() -> R, F: FnOnce() -> R,
R: IntoFuture,
{ {
RT.with(move |rt| rt.borrow_mut().block_on(lazy(|| Ok::<_, ()>(f())))) RT.with(move |rt| rt.borrow_mut().get_mut().block_on(lazy(|| f())))
.unwrap()
} }
/// The `TestServer` type. /// The `TestServer` type.