mirror of
https://github.com/fafhrd91/actix-web
synced 2025-07-03 17:41:30 +02:00
Compare commits
37 Commits
files-v0.1
...
http-test-
Author | SHA1 | Date | |
---|---|---|---|
c45728ac01 | |||
6f71409355 | |||
8d17c8651f | |||
b1143168e5 | |||
69456991f6 | |||
f410f3330f | |||
e1fcd203f8 | |||
0d8a4304a9 | |||
14cc5a5d6b | |||
287c2b1d18 | |||
7596ab69e0 | |||
1fdd77bffa | |||
2d424957fb | |||
dabc4fe00b | |||
5bf5b0acd2 | |||
099a8ff7d8 | |||
a28b7139e6 | |||
a0a469fe85 | |||
dbab55dd6b | |||
d2eb1edac3 | |||
5901dfee1a | |||
0e05b37082 | |||
37f4ce8604 | |||
12b5174850 | |||
b77ed193f7 | |||
d286ccb4f5 | |||
cac162aed7 | |||
50a9d9e2c5 | |||
93855b889a | |||
fa7e0fe6df | |||
382d4ca216 | |||
32a66a99bf | |||
73ae801a13 | |||
ca4ed0932e | |||
9fc7c8b1af | |||
65732197b8 | |||
959eef05ae |
@ -3,7 +3,7 @@ sudo: required
|
|||||||
dist: trusty
|
dist: trusty
|
||||||
|
|
||||||
cache:
|
cache:
|
||||||
cargo: true
|
# cargo: true
|
||||||
apt: true
|
apt: true
|
||||||
|
|
||||||
matrix:
|
matrix:
|
||||||
@ -26,7 +26,7 @@ before_install:
|
|||||||
|
|
||||||
before_cache: |
|
before_cache: |
|
||||||
if [[ "$TRAVIS_RUST_VERSION" == "nightly-2019-04-02" ]]; then
|
if [[ "$TRAVIS_RUST_VERSION" == "nightly-2019-04-02" ]]; then
|
||||||
RUSTFLAGS="--cfg procmacro2_semver_exempt" cargo install cargo-tarpaulin
|
RUSTFLAGS="--cfg procmacro2_semver_exempt" cargo install --version 0.6.11 cargo-tarpaulin
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Add clippy
|
# Add clippy
|
||||||
|
12
CHANGES.md
12
CHANGES.md
@ -1,6 +1,16 @@
|
|||||||
# Changes
|
# Changes
|
||||||
|
|
||||||
## [1.0.3] - unreleased
|
## [1.0.4] - TBD
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* Add `Responder` impl for `(T, StatusCode) where T: Responder`
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
* Upgrade `rand` dependency version to 0.7
|
||||||
|
|
||||||
|
## [1.0.3] - 2019-06-28
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
12
Cargo.toml
12
Cargo.toml
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "actix-web"
|
name = "actix-web"
|
||||||
version = "1.0.2"
|
version = "1.0.3"
|
||||||
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"
|
||||||
@ -71,11 +71,11 @@ rust-tls = ["rustls", "actix-server/rust-tls"]
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
actix-codec = "0.1.2"
|
actix-codec = "0.1.2"
|
||||||
actix-service = "0.4.1"
|
actix-service = "0.4.1"
|
||||||
actix-utils = "0.4.1"
|
actix-utils = "0.4.2"
|
||||||
actix-router = "0.1.5"
|
actix-router = "0.1.5"
|
||||||
actix-rt = "0.2.2"
|
actix-rt = "0.2.3"
|
||||||
actix-web-codegen = "0.1.2"
|
actix-web-codegen = "0.1.2"
|
||||||
actix-http = "0.2.4"
|
actix-http = "0.2.5"
|
||||||
actix-server = "0.5.1"
|
actix-server = "0.5.1"
|
||||||
actix-server-config = "0.1.1"
|
actix-server-config = "0.1.1"
|
||||||
actix-threadpool = "0.1.1"
|
actix-threadpool = "0.1.1"
|
||||||
@ -103,9 +103,9 @@ rustls = { version = "0.15", optional = true }
|
|||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
actix = { version = "0.8.3" }
|
actix = { version = "0.8.3" }
|
||||||
actix-http = { version = "0.2.4", features=["ssl", "brotli", "flate2-zlib"] }
|
actix-http = { version = "0.2.5", features=["ssl", "brotli", "flate2-zlib"] }
|
||||||
actix-http-test = { version = "0.2.2", features=["ssl"] }
|
actix-http-test = { version = "0.2.2", features=["ssl"] }
|
||||||
rand = "0.6"
|
rand = "0.7"
|
||||||
env_logger = "0.6"
|
env_logger = "0.6"
|
||||||
serde_derive = "1.0"
|
serde_derive = "1.0"
|
||||||
tokio-timer = "0.2.8"
|
tokio-timer = "0.2.8"
|
||||||
|
@ -61,7 +61,7 @@ You may consider checking out
|
|||||||
|
|
||||||
## Benchmarks
|
## Benchmarks
|
||||||
|
|
||||||
* [TechEmpower Framework Benchmark](https://www.techempower.com/benchmarks/#section=data-r16&hw=ph&test=plaintext)
|
* [TechEmpower Framework Benchmark](https://www.techempower.com/benchmarks/#section=data-r18)
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
## Documentation & community resources
|
## Documentation & community resources
|
||||||
|
|
||||||
* [User Guide](https://actix.rs/docs/)
|
* [User Guide](https://actix.rs/docs/)
|
||||||
* [API Documentation](https://docs.rs/actix-identity/)
|
* [API Documentation](https://docs.rs/actix-cors/)
|
||||||
* [Chat on gitter](https://gitter.im/actix/actix)
|
* [Chat on gitter](https://gitter.im/actix/actix)
|
||||||
* Cargo package: [actix-session](https://crates.io/crates/actix-identity)
|
* Cargo package: [actix-cors](https://crates.io/crates/actix-cors)
|
||||||
* Minimum supported Rust version: 1.34 or later
|
* Minimum supported Rust version: 1.34 or later
|
||||||
|
@ -1,10 +1,23 @@
|
|||||||
# Changes
|
# Changes
|
||||||
|
|
||||||
## [0.2.5] - unreleased
|
## [0.2.6] - TBD
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
* Use `encoding_rs` crate instead of unmaintained `encoding` crate
|
* Upgrade `rand` dependency version to 0.7
|
||||||
|
|
||||||
|
## [0.2.5] - 2019-06-28
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* Add `on-connect` callback, `HttpServiceBuilder::on_connect()` #946
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
* Use `encoding_rs` crate instead of unmaintained `encoding` crate
|
||||||
|
|
||||||
|
* Add `Copy` and `Clone` impls for `ws::Codec`
|
||||||
|
|
||||||
|
|
||||||
## [0.2.4] - 2019-06-16
|
## [0.2.4] - 2019-06-16
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "actix-http"
|
name = "actix-http"
|
||||||
version = "0.2.4"
|
version = "0.2.5"
|
||||||
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.4.0"
|
actix-service = "0.4.1"
|
||||||
actix-codec = "0.1.2"
|
actix-codec = "0.1.2"
|
||||||
actix-connect = "0.2.0"
|
actix-connect = "0.2.0"
|
||||||
actix-utils = "0.4.1"
|
actix-utils = "0.4.2"
|
||||||
actix-server-config = "0.1.1"
|
actix-server-config = "0.1.1"
|
||||||
actix-threadpool = "0.1.0"
|
actix-threadpool = "0.1.0"
|
||||||
|
|
||||||
@ -55,7 +55,7 @@ base64 = "0.10"
|
|||||||
bitflags = "1.0"
|
bitflags = "1.0"
|
||||||
bytes = "0.4"
|
bytes = "0.4"
|
||||||
byteorder = "1.2"
|
byteorder = "1.2"
|
||||||
copyless = "0.1.2"
|
copyless = "0.1.4"
|
||||||
derive_more = "0.15.0"
|
derive_more = "0.15.0"
|
||||||
either = "1.5.2"
|
either = "1.5.2"
|
||||||
encoding_rs = "0.8"
|
encoding_rs = "0.8"
|
||||||
@ -70,7 +70,7 @@ language-tags = "0.2"
|
|||||||
log = "0.4"
|
log = "0.4"
|
||||||
mime = "0.3"
|
mime = "0.3"
|
||||||
percent-encoding = "1.0"
|
percent-encoding = "1.0"
|
||||||
rand = "0.6"
|
rand = "0.7"
|
||||||
regex = "1.0"
|
regex = "1.0"
|
||||||
serde = "1.0"
|
serde = "1.0"
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
use actix_codec::Framed;
|
use actix_codec::Framed;
|
||||||
use actix_server_config::ServerConfig as SrvConfig;
|
use actix_server_config::ServerConfig as SrvConfig;
|
||||||
@ -10,6 +11,7 @@ use crate::config::{KeepAlive, ServiceConfig};
|
|||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
use crate::h1::{Codec, ExpectHandler, H1Service, UpgradeHandler};
|
use crate::h1::{Codec, ExpectHandler, H1Service, UpgradeHandler};
|
||||||
use crate::h2::H2Service;
|
use crate::h2::H2Service;
|
||||||
|
use crate::helpers::{Data, DataFactory};
|
||||||
use crate::request::Request;
|
use crate::request::Request;
|
||||||
use crate::response::Response;
|
use crate::response::Response;
|
||||||
use crate::service::HttpService;
|
use crate::service::HttpService;
|
||||||
@ -24,6 +26,7 @@ pub struct HttpServiceBuilder<T, S, X = ExpectHandler, U = UpgradeHandler<T>> {
|
|||||||
client_disconnect: u64,
|
client_disconnect: u64,
|
||||||
expect: X,
|
expect: X,
|
||||||
upgrade: Option<U>,
|
upgrade: Option<U>,
|
||||||
|
on_connect: Option<Rc<Fn(&T) -> Box<dyn DataFactory>>>,
|
||||||
_t: PhantomData<(T, S)>,
|
_t: PhantomData<(T, S)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,6 +44,7 @@ where
|
|||||||
client_disconnect: 0,
|
client_disconnect: 0,
|
||||||
expect: ExpectHandler,
|
expect: ExpectHandler,
|
||||||
upgrade: None,
|
upgrade: None,
|
||||||
|
on_connect: None,
|
||||||
_t: PhantomData,
|
_t: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -115,6 +119,7 @@ where
|
|||||||
client_disconnect: self.client_disconnect,
|
client_disconnect: self.client_disconnect,
|
||||||
expect: expect.into_new_service(),
|
expect: expect.into_new_service(),
|
||||||
upgrade: self.upgrade,
|
upgrade: self.upgrade,
|
||||||
|
on_connect: self.on_connect,
|
||||||
_t: PhantomData,
|
_t: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -140,10 +145,24 @@ where
|
|||||||
client_disconnect: self.client_disconnect,
|
client_disconnect: self.client_disconnect,
|
||||||
expect: self.expect,
|
expect: self.expect,
|
||||||
upgrade: Some(upgrade.into_new_service()),
|
upgrade: Some(upgrade.into_new_service()),
|
||||||
|
on_connect: self.on_connect,
|
||||||
_t: PhantomData,
|
_t: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set on-connect callback.
|
||||||
|
///
|
||||||
|
/// It get called once per connection and result of the call
|
||||||
|
/// get stored to the request's extensions.
|
||||||
|
pub fn on_connect<F, I>(mut self, f: F) -> Self
|
||||||
|
where
|
||||||
|
F: Fn(&T) -> I + 'static,
|
||||||
|
I: Clone + 'static,
|
||||||
|
{
|
||||||
|
self.on_connect = Some(Rc::new(move |io| Box::new(Data(f(io)))));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Finish service configuration and create *http service* for HTTP/1 protocol.
|
/// Finish service configuration and create *http service* for HTTP/1 protocol.
|
||||||
pub fn h1<F, P, B>(self, service: F) -> H1Service<T, P, S, B, X, U>
|
pub fn h1<F, P, B>(self, service: F) -> H1Service<T, P, S, B, X, U>
|
||||||
where
|
where
|
||||||
@ -161,6 +180,7 @@ where
|
|||||||
H1Service::with_config(cfg, service.into_new_service())
|
H1Service::with_config(cfg, service.into_new_service())
|
||||||
.expect(self.expect)
|
.expect(self.expect)
|
||||||
.upgrade(self.upgrade)
|
.upgrade(self.upgrade)
|
||||||
|
.on_connect(self.on_connect)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Finish service configuration and create *http service* for HTTP/2 protocol.
|
/// Finish service configuration and create *http service* for HTTP/2 protocol.
|
||||||
@ -199,5 +219,6 @@ where
|
|||||||
HttpService::with_config(cfg, service.into_new_service())
|
HttpService::with_config(cfg, service.into_new_service())
|
||||||
.expect(self.expect)
|
.expect(self.expect)
|
||||||
.upgrade(self.upgrade)
|
.upgrade(self.upgrade)
|
||||||
|
.on_connect(self.on_connect)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,8 @@ use crate::body::{Body, BodySize, MessageBody, ResponseBody};
|
|||||||
use crate::config::ServiceConfig;
|
use crate::config::ServiceConfig;
|
||||||
use crate::error::{DispatchError, Error};
|
use crate::error::{DispatchError, Error};
|
||||||
use crate::error::{ParseError, PayloadError};
|
use crate::error::{ParseError, PayloadError};
|
||||||
|
use crate::helpers::DataFactory;
|
||||||
|
use crate::httpmessage::HttpMessage;
|
||||||
use crate::request::Request;
|
use crate::request::Request;
|
||||||
use crate::response::Response;
|
use crate::response::Response;
|
||||||
|
|
||||||
@ -81,6 +83,7 @@ where
|
|||||||
service: CloneableService<S>,
|
service: CloneableService<S>,
|
||||||
expect: CloneableService<X>,
|
expect: CloneableService<X>,
|
||||||
upgrade: Option<CloneableService<U>>,
|
upgrade: Option<CloneableService<U>>,
|
||||||
|
on_connect: Option<Box<dyn DataFactory>>,
|
||||||
flags: Flags,
|
flags: Flags,
|
||||||
peer_addr: Option<net::SocketAddr>,
|
peer_addr: Option<net::SocketAddr>,
|
||||||
error: Option<DispatchError>,
|
error: Option<DispatchError>,
|
||||||
@ -174,12 +177,13 @@ where
|
|||||||
U::Error: fmt::Display,
|
U::Error: fmt::Display,
|
||||||
{
|
{
|
||||||
/// Create http/1 dispatcher.
|
/// Create http/1 dispatcher.
|
||||||
pub fn new(
|
pub(crate) fn new(
|
||||||
stream: T,
|
stream: T,
|
||||||
config: ServiceConfig,
|
config: ServiceConfig,
|
||||||
service: CloneableService<S>,
|
service: CloneableService<S>,
|
||||||
expect: CloneableService<X>,
|
expect: CloneableService<X>,
|
||||||
upgrade: Option<CloneableService<U>>,
|
upgrade: Option<CloneableService<U>>,
|
||||||
|
on_connect: Option<Box<dyn DataFactory>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Dispatcher::with_timeout(
|
Dispatcher::with_timeout(
|
||||||
stream,
|
stream,
|
||||||
@ -190,11 +194,12 @@ where
|
|||||||
service,
|
service,
|
||||||
expect,
|
expect,
|
||||||
upgrade,
|
upgrade,
|
||||||
|
on_connect,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create http/1 dispatcher with slow request timeout.
|
/// Create http/1 dispatcher with slow request timeout.
|
||||||
pub fn with_timeout(
|
pub(crate) fn with_timeout(
|
||||||
io: T,
|
io: T,
|
||||||
codec: Codec,
|
codec: Codec,
|
||||||
config: ServiceConfig,
|
config: ServiceConfig,
|
||||||
@ -203,6 +208,7 @@ where
|
|||||||
service: CloneableService<S>,
|
service: CloneableService<S>,
|
||||||
expect: CloneableService<X>,
|
expect: CloneableService<X>,
|
||||||
upgrade: Option<CloneableService<U>>,
|
upgrade: Option<CloneableService<U>>,
|
||||||
|
on_connect: Option<Box<dyn DataFactory>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let keepalive = config.keep_alive_enabled();
|
let keepalive = config.keep_alive_enabled();
|
||||||
let flags = if keepalive {
|
let flags = if keepalive {
|
||||||
@ -234,6 +240,7 @@ where
|
|||||||
service,
|
service,
|
||||||
expect,
|
expect,
|
||||||
upgrade,
|
upgrade,
|
||||||
|
on_connect,
|
||||||
flags,
|
flags,
|
||||||
ka_expire,
|
ka_expire,
|
||||||
ka_timer,
|
ka_timer,
|
||||||
@ -495,6 +502,11 @@ where
|
|||||||
let pl = self.codec.message_type();
|
let pl = self.codec.message_type();
|
||||||
req.head_mut().peer_addr = self.peer_addr;
|
req.head_mut().peer_addr = self.peer_addr;
|
||||||
|
|
||||||
|
// on_connect data
|
||||||
|
if let Some(ref on_connect) = self.on_connect {
|
||||||
|
on_connect.set(&mut req.extensions_mut());
|
||||||
|
}
|
||||||
|
|
||||||
if pl == MessageType::Stream && self.upgrade.is_some() {
|
if pl == MessageType::Stream && self.upgrade.is_some() {
|
||||||
self.messages.push_back(DispatcherMessage::Upgrade(req));
|
self.messages.push_back(DispatcherMessage::Upgrade(req));
|
||||||
break;
|
break;
|
||||||
@ -851,6 +863,7 @@ mod tests {
|
|||||||
),
|
),
|
||||||
CloneableService::new(ExpectHandler),
|
CloneableService::new(ExpectHandler),
|
||||||
None,
|
None,
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
assert!(h1.poll().is_err());
|
assert!(h1.poll().is_err());
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
use actix_codec::Framed;
|
use actix_codec::Framed;
|
||||||
use actix_server_config::{Io, IoStream, ServerConfig as SrvConfig};
|
use actix_server_config::{Io, IoStream, ServerConfig as SrvConfig};
|
||||||
@ -11,6 +12,7 @@ use futures::{try_ready, Async, Future, IntoFuture, Poll, Stream};
|
|||||||
use crate::body::MessageBody;
|
use crate::body::MessageBody;
|
||||||
use crate::config::{KeepAlive, ServiceConfig};
|
use crate::config::{KeepAlive, ServiceConfig};
|
||||||
use crate::error::{DispatchError, Error, ParseError};
|
use crate::error::{DispatchError, Error, ParseError};
|
||||||
|
use crate::helpers::DataFactory;
|
||||||
use crate::request::Request;
|
use crate::request::Request;
|
||||||
use crate::response::Response;
|
use crate::response::Response;
|
||||||
|
|
||||||
@ -24,6 +26,7 @@ pub struct H1Service<T, P, S, B, X = ExpectHandler, U = UpgradeHandler<T>> {
|
|||||||
cfg: ServiceConfig,
|
cfg: ServiceConfig,
|
||||||
expect: X,
|
expect: X,
|
||||||
upgrade: Option<U>,
|
upgrade: Option<U>,
|
||||||
|
on_connect: Option<Rc<Fn(&T) -> Box<dyn DataFactory>>>,
|
||||||
_t: PhantomData<(T, P, B)>,
|
_t: PhantomData<(T, P, B)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,6 +47,7 @@ where
|
|||||||
srv: service.into_new_service(),
|
srv: service.into_new_service(),
|
||||||
expect: ExpectHandler,
|
expect: ExpectHandler,
|
||||||
upgrade: None,
|
upgrade: None,
|
||||||
|
on_connect: None,
|
||||||
_t: PhantomData,
|
_t: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -55,6 +59,7 @@ where
|
|||||||
srv: service.into_new_service(),
|
srv: service.into_new_service(),
|
||||||
expect: ExpectHandler,
|
expect: ExpectHandler,
|
||||||
upgrade: None,
|
upgrade: None,
|
||||||
|
on_connect: None,
|
||||||
_t: PhantomData,
|
_t: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -79,6 +84,7 @@ where
|
|||||||
cfg: self.cfg,
|
cfg: self.cfg,
|
||||||
srv: self.srv,
|
srv: self.srv,
|
||||||
upgrade: self.upgrade,
|
upgrade: self.upgrade,
|
||||||
|
on_connect: self.on_connect,
|
||||||
_t: PhantomData,
|
_t: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -94,9 +100,19 @@ where
|
|||||||
cfg: self.cfg,
|
cfg: self.cfg,
|
||||||
srv: self.srv,
|
srv: self.srv,
|
||||||
expect: self.expect,
|
expect: self.expect,
|
||||||
|
on_connect: self.on_connect,
|
||||||
_t: PhantomData,
|
_t: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set on connect callback.
|
||||||
|
pub(crate) fn on_connect(
|
||||||
|
mut self,
|
||||||
|
f: Option<Rc<Fn(&T) -> Box<dyn DataFactory>>>,
|
||||||
|
) -> Self {
|
||||||
|
self.on_connect = f;
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, P, S, B, X, U> NewService for H1Service<T, P, S, B, X, U>
|
impl<T, P, S, B, X, U> NewService for H1Service<T, P, S, B, X, U>
|
||||||
@ -133,6 +149,7 @@ where
|
|||||||
fut_upg: self.upgrade.as_ref().map(|f| f.new_service(cfg)),
|
fut_upg: self.upgrade.as_ref().map(|f| f.new_service(cfg)),
|
||||||
expect: None,
|
expect: None,
|
||||||
upgrade: None,
|
upgrade: None,
|
||||||
|
on_connect: self.on_connect.clone(),
|
||||||
cfg: Some(self.cfg.clone()),
|
cfg: Some(self.cfg.clone()),
|
||||||
_t: PhantomData,
|
_t: PhantomData,
|
||||||
}
|
}
|
||||||
@ -157,6 +174,7 @@ where
|
|||||||
fut_upg: Option<U::Future>,
|
fut_upg: Option<U::Future>,
|
||||||
expect: Option<X::Service>,
|
expect: Option<X::Service>,
|
||||||
upgrade: Option<U::Service>,
|
upgrade: Option<U::Service>,
|
||||||
|
on_connect: Option<Rc<Fn(&T) -> Box<dyn DataFactory>>>,
|
||||||
cfg: Option<ServiceConfig>,
|
cfg: Option<ServiceConfig>,
|
||||||
_t: PhantomData<(T, P, B)>,
|
_t: PhantomData<(T, P, B)>,
|
||||||
}
|
}
|
||||||
@ -205,6 +223,7 @@ where
|
|||||||
service,
|
service,
|
||||||
self.expect.take().unwrap(),
|
self.expect.take().unwrap(),
|
||||||
self.upgrade.take(),
|
self.upgrade.take(),
|
||||||
|
self.on_connect.clone(),
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -214,6 +233,7 @@ pub struct H1ServiceHandler<T, P, S, B, X, U> {
|
|||||||
srv: CloneableService<S>,
|
srv: CloneableService<S>,
|
||||||
expect: CloneableService<X>,
|
expect: CloneableService<X>,
|
||||||
upgrade: Option<CloneableService<U>>,
|
upgrade: Option<CloneableService<U>>,
|
||||||
|
on_connect: Option<Rc<Fn(&T) -> Box<dyn DataFactory>>>,
|
||||||
cfg: ServiceConfig,
|
cfg: ServiceConfig,
|
||||||
_t: PhantomData<(T, P, B)>,
|
_t: PhantomData<(T, P, B)>,
|
||||||
}
|
}
|
||||||
@ -234,12 +254,14 @@ where
|
|||||||
srv: S,
|
srv: S,
|
||||||
expect: X,
|
expect: X,
|
||||||
upgrade: Option<U>,
|
upgrade: Option<U>,
|
||||||
|
on_connect: Option<Rc<Fn(&T) -> Box<dyn DataFactory>>>,
|
||||||
) -> H1ServiceHandler<T, P, S, B, X, U> {
|
) -> H1ServiceHandler<T, P, S, B, X, U> {
|
||||||
H1ServiceHandler {
|
H1ServiceHandler {
|
||||||
srv: CloneableService::new(srv),
|
srv: CloneableService::new(srv),
|
||||||
expect: CloneableService::new(expect),
|
expect: CloneableService::new(expect),
|
||||||
upgrade: upgrade.map(|s| CloneableService::new(s)),
|
upgrade: upgrade.map(|s| CloneableService::new(s)),
|
||||||
cfg,
|
cfg,
|
||||||
|
on_connect,
|
||||||
_t: PhantomData,
|
_t: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -292,12 +314,21 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn call(&mut self, req: Self::Request) -> Self::Future {
|
fn call(&mut self, req: Self::Request) -> Self::Future {
|
||||||
|
let io = req.into_parts().0;
|
||||||
|
|
||||||
|
let on_connect = if let Some(ref on_connect) = self.on_connect {
|
||||||
|
Some(on_connect(&io))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
Dispatcher::new(
|
Dispatcher::new(
|
||||||
req.into_parts().0,
|
io,
|
||||||
self.cfg.clone(),
|
self.cfg.clone(),
|
||||||
self.srv.clone(),
|
self.srv.clone(),
|
||||||
self.expect.clone(),
|
self.expect.clone(),
|
||||||
self.upgrade.clone(),
|
self.upgrade.clone(),
|
||||||
|
on_connect,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ use tokio_timer::Delay;
|
|||||||
use crate::body::{Body, BodySize, MessageBody, ResponseBody};
|
use crate::body::{Body, BodySize, MessageBody, ResponseBody};
|
||||||
use crate::config::ServiceConfig;
|
use crate::config::ServiceConfig;
|
||||||
use crate::error::{DispatchError, Error, ParseError, PayloadError, ResponseError};
|
use crate::error::{DispatchError, Error, ParseError, PayloadError, ResponseError};
|
||||||
|
use crate::helpers::DataFactory;
|
||||||
use crate::message::ResponseHead;
|
use crate::message::ResponseHead;
|
||||||
use crate::payload::Payload;
|
use crate::payload::Payload;
|
||||||
use crate::request::Request;
|
use crate::request::Request;
|
||||||
@ -33,6 +34,7 @@ const CHUNK_SIZE: usize = 16_384;
|
|||||||
pub struct Dispatcher<T: IoStream, S: Service<Request = Request>, B: MessageBody> {
|
pub struct Dispatcher<T: IoStream, S: Service<Request = Request>, B: MessageBody> {
|
||||||
service: CloneableService<S>,
|
service: CloneableService<S>,
|
||||||
connection: Connection<T, Bytes>,
|
connection: Connection<T, Bytes>,
|
||||||
|
on_connect: Option<Box<dyn DataFactory>>,
|
||||||
config: ServiceConfig,
|
config: ServiceConfig,
|
||||||
peer_addr: Option<net::SocketAddr>,
|
peer_addr: Option<net::SocketAddr>,
|
||||||
ka_expire: Instant,
|
ka_expire: Instant,
|
||||||
@ -49,9 +51,10 @@ where
|
|||||||
S::Response: Into<Response<B>>,
|
S::Response: Into<Response<B>>,
|
||||||
B: MessageBody + 'static,
|
B: MessageBody + 'static,
|
||||||
{
|
{
|
||||||
pub fn new(
|
pub(crate) fn new(
|
||||||
service: CloneableService<S>,
|
service: CloneableService<S>,
|
||||||
connection: Connection<T, Bytes>,
|
connection: Connection<T, Bytes>,
|
||||||
|
on_connect: Option<Box<dyn DataFactory>>,
|
||||||
config: ServiceConfig,
|
config: ServiceConfig,
|
||||||
timeout: Option<Delay>,
|
timeout: Option<Delay>,
|
||||||
peer_addr: Option<net::SocketAddr>,
|
peer_addr: Option<net::SocketAddr>,
|
||||||
@ -77,6 +80,7 @@ where
|
|||||||
config,
|
config,
|
||||||
peer_addr,
|
peer_addr,
|
||||||
connection,
|
connection,
|
||||||
|
on_connect,
|
||||||
ka_expire,
|
ka_expire,
|
||||||
ka_timer,
|
ka_timer,
|
||||||
_t: PhantomData,
|
_t: PhantomData,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::{io, net};
|
use std::{io, net, rc};
|
||||||
|
|
||||||
use actix_codec::{AsyncRead, AsyncWrite, Framed};
|
use actix_codec::{AsyncRead, AsyncWrite, Framed};
|
||||||
use actix_server_config::{Io, IoStream, ServerConfig as SrvConfig};
|
use actix_server_config::{Io, IoStream, ServerConfig as SrvConfig};
|
||||||
@ -16,6 +16,7 @@ use log::error;
|
|||||||
use crate::body::MessageBody;
|
use crate::body::MessageBody;
|
||||||
use crate::config::{KeepAlive, ServiceConfig};
|
use crate::config::{KeepAlive, ServiceConfig};
|
||||||
use crate::error::{DispatchError, Error, ParseError, ResponseError};
|
use crate::error::{DispatchError, Error, ParseError, ResponseError};
|
||||||
|
use crate::helpers::DataFactory;
|
||||||
use crate::payload::Payload;
|
use crate::payload::Payload;
|
||||||
use crate::request::Request;
|
use crate::request::Request;
|
||||||
use crate::response::Response;
|
use crate::response::Response;
|
||||||
@ -26,6 +27,7 @@ use super::dispatcher::Dispatcher;
|
|||||||
pub struct H2Service<T, P, S, B> {
|
pub struct H2Service<T, P, S, B> {
|
||||||
srv: S,
|
srv: S,
|
||||||
cfg: ServiceConfig,
|
cfg: ServiceConfig,
|
||||||
|
on_connect: Option<rc::Rc<Fn(&T) -> Box<dyn DataFactory>>>,
|
||||||
_t: PhantomData<(T, P, B)>,
|
_t: PhantomData<(T, P, B)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,6 +45,7 @@ where
|
|||||||
|
|
||||||
H2Service {
|
H2Service {
|
||||||
cfg,
|
cfg,
|
||||||
|
on_connect: None,
|
||||||
srv: service.into_new_service(),
|
srv: service.into_new_service(),
|
||||||
_t: PhantomData,
|
_t: PhantomData,
|
||||||
}
|
}
|
||||||
@ -52,10 +55,20 @@ where
|
|||||||
pub fn with_config<F: IntoNewService<S>>(cfg: ServiceConfig, service: F) -> Self {
|
pub fn with_config<F: IntoNewService<S>>(cfg: ServiceConfig, service: F) -> Self {
|
||||||
H2Service {
|
H2Service {
|
||||||
cfg,
|
cfg,
|
||||||
|
on_connect: None,
|
||||||
srv: service.into_new_service(),
|
srv: service.into_new_service(),
|
||||||
_t: PhantomData,
|
_t: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set on connect callback.
|
||||||
|
pub(crate) fn on_connect(
|
||||||
|
mut self,
|
||||||
|
f: Option<rc::Rc<Fn(&T) -> Box<dyn DataFactory>>>,
|
||||||
|
) -> Self {
|
||||||
|
self.on_connect = f;
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, P, S, B> NewService for H2Service<T, P, S, B>
|
impl<T, P, S, B> NewService for H2Service<T, P, S, B>
|
||||||
@ -79,6 +92,7 @@ where
|
|||||||
H2ServiceResponse {
|
H2ServiceResponse {
|
||||||
fut: self.srv.new_service(cfg).into_future(),
|
fut: self.srv.new_service(cfg).into_future(),
|
||||||
cfg: Some(self.cfg.clone()),
|
cfg: Some(self.cfg.clone()),
|
||||||
|
on_connect: self.on_connect.clone(),
|
||||||
_t: PhantomData,
|
_t: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -88,6 +102,7 @@ where
|
|||||||
pub struct H2ServiceResponse<T, P, S: NewService, 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>,
|
||||||
|
on_connect: Option<rc::Rc<Fn(&T) -> Box<dyn DataFactory>>>,
|
||||||
_t: PhantomData<(T, P, B)>,
|
_t: PhantomData<(T, P, B)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,6 +122,7 @@ where
|
|||||||
let service = try_ready!(self.fut.poll());
|
let service = try_ready!(self.fut.poll());
|
||||||
Ok(Async::Ready(H2ServiceHandler::new(
|
Ok(Async::Ready(H2ServiceHandler::new(
|
||||||
self.cfg.take().unwrap(),
|
self.cfg.take().unwrap(),
|
||||||
|
self.on_connect.clone(),
|
||||||
service,
|
service,
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
@ -116,6 +132,7 @@ where
|
|||||||
pub struct H2ServiceHandler<T, P, S, B> {
|
pub struct H2ServiceHandler<T, P, S, B> {
|
||||||
srv: CloneableService<S>,
|
srv: CloneableService<S>,
|
||||||
cfg: ServiceConfig,
|
cfg: ServiceConfig,
|
||||||
|
on_connect: Option<rc::Rc<Fn(&T) -> Box<dyn DataFactory>>>,
|
||||||
_t: PhantomData<(T, P, B)>,
|
_t: PhantomData<(T, P, B)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,9 +144,14 @@ where
|
|||||||
S::Response: Into<Response<B>>,
|
S::Response: Into<Response<B>>,
|
||||||
B: MessageBody + 'static,
|
B: MessageBody + 'static,
|
||||||
{
|
{
|
||||||
fn new(cfg: ServiceConfig, srv: S) -> H2ServiceHandler<T, P, S, B> {
|
fn new(
|
||||||
|
cfg: ServiceConfig,
|
||||||
|
on_connect: Option<rc::Rc<Fn(&T) -> Box<dyn DataFactory>>>,
|
||||||
|
srv: S,
|
||||||
|
) -> H2ServiceHandler<T, P, S, B> {
|
||||||
H2ServiceHandler {
|
H2ServiceHandler {
|
||||||
cfg,
|
cfg,
|
||||||
|
on_connect,
|
||||||
srv: CloneableService::new(srv),
|
srv: CloneableService::new(srv),
|
||||||
_t: PhantomData,
|
_t: PhantomData,
|
||||||
}
|
}
|
||||||
@ -161,11 +183,18 @@ where
|
|||||||
fn call(&mut self, req: Self::Request) -> Self::Future {
|
fn call(&mut self, req: Self::Request) -> Self::Future {
|
||||||
let io = req.into_parts().0;
|
let io = req.into_parts().0;
|
||||||
let peer_addr = io.peer_addr();
|
let peer_addr = io.peer_addr();
|
||||||
|
let on_connect = if let Some(ref on_connect) = self.on_connect {
|
||||||
|
Some(on_connect(&io))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
H2ServiceHandlerResponse {
|
H2ServiceHandlerResponse {
|
||||||
state: State::Handshake(
|
state: State::Handshake(
|
||||||
Some(self.srv.clone()),
|
Some(self.srv.clone()),
|
||||||
Some(self.cfg.clone()),
|
Some(self.cfg.clone()),
|
||||||
peer_addr,
|
peer_addr,
|
||||||
|
on_connect,
|
||||||
server::handshake(io),
|
server::handshake(io),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
@ -181,6 +210,7 @@ where
|
|||||||
Option<CloneableService<S>>,
|
Option<CloneableService<S>>,
|
||||||
Option<ServiceConfig>,
|
Option<ServiceConfig>,
|
||||||
Option<net::SocketAddr>,
|
Option<net::SocketAddr>,
|
||||||
|
Option<Box<dyn DataFactory>>,
|
||||||
Handshake<T, Bytes>,
|
Handshake<T, Bytes>,
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
@ -216,12 +246,14 @@ where
|
|||||||
ref mut srv,
|
ref mut srv,
|
||||||
ref mut config,
|
ref mut config,
|
||||||
ref peer_addr,
|
ref peer_addr,
|
||||||
|
ref mut on_connect,
|
||||||
ref mut handshake,
|
ref mut handshake,
|
||||||
) => match handshake.poll() {
|
) => match handshake.poll() {
|
||||||
Ok(Async::Ready(conn)) => {
|
Ok(Async::Ready(conn)) => {
|
||||||
self.state = State::Incoming(Dispatcher::new(
|
self.state = State::Incoming(Dispatcher::new(
|
||||||
srv.take().unwrap(),
|
srv.take().unwrap(),
|
||||||
conn,
|
conn,
|
||||||
|
on_connect.take(),
|
||||||
config.take().unwrap(),
|
config.take().unwrap(),
|
||||||
None,
|
None,
|
||||||
peer_addr.clone(),
|
peer_addr.clone(),
|
||||||
|
@ -3,6 +3,8 @@ use std::{io, mem, ptr, slice};
|
|||||||
use bytes::{BufMut, BytesMut};
|
use bytes::{BufMut, BytesMut};
|
||||||
use http::Version;
|
use http::Version;
|
||||||
|
|
||||||
|
use crate::extensions::Extensions;
|
||||||
|
|
||||||
const DEC_DIGITS_LUT: &[u8] = b"0001020304050607080910111213141516171819\
|
const DEC_DIGITS_LUT: &[u8] = b"0001020304050607080910111213141516171819\
|
||||||
2021222324252627282930313233343536373839\
|
2021222324252627282930313233343536373839\
|
||||||
4041424344454647484950515253545556575859\
|
4041424344454647484950515253545556575859\
|
||||||
@ -180,6 +182,18 @@ impl<'a> io::Write for Writer<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) trait DataFactory {
|
||||||
|
fn set(&self, ext: &mut Extensions);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct Data<T>(pub(crate) T);
|
||||||
|
|
||||||
|
impl<T: Clone + 'static> DataFactory for Data<T> {
|
||||||
|
fn set(&self, ext: &mut Extensions) {
|
||||||
|
ext.insert(self.0.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::{fmt, io, net};
|
use std::{fmt, io, net, rc};
|
||||||
|
|
||||||
use actix_codec::{AsyncRead, AsyncWrite, Framed};
|
use actix_codec::{AsyncRead, AsyncWrite, Framed};
|
||||||
use actix_server_config::{
|
use actix_server_config::{
|
||||||
@ -15,6 +15,7 @@ use crate::body::MessageBody;
|
|||||||
use crate::builder::HttpServiceBuilder;
|
use crate::builder::HttpServiceBuilder;
|
||||||
use crate::config::{KeepAlive, ServiceConfig};
|
use crate::config::{KeepAlive, ServiceConfig};
|
||||||
use crate::error::{DispatchError, Error};
|
use crate::error::{DispatchError, Error};
|
||||||
|
use crate::helpers::DataFactory;
|
||||||
use crate::request::Request;
|
use crate::request::Request;
|
||||||
use crate::response::Response;
|
use crate::response::Response;
|
||||||
use crate::{h1, h2::Dispatcher};
|
use crate::{h1, h2::Dispatcher};
|
||||||
@ -25,6 +26,7 @@ pub struct HttpService<T, P, S, B, X = h1::ExpectHandler, U = h1::UpgradeHandler
|
|||||||
cfg: ServiceConfig,
|
cfg: ServiceConfig,
|
||||||
expect: X,
|
expect: X,
|
||||||
upgrade: Option<U>,
|
upgrade: Option<U>,
|
||||||
|
on_connect: Option<rc::Rc<Fn(&T) -> Box<dyn DataFactory>>>,
|
||||||
_t: PhantomData<(T, P, B)>,
|
_t: PhantomData<(T, P, B)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,6 +63,7 @@ where
|
|||||||
srv: service.into_new_service(),
|
srv: service.into_new_service(),
|
||||||
expect: h1::ExpectHandler,
|
expect: h1::ExpectHandler,
|
||||||
upgrade: None,
|
upgrade: None,
|
||||||
|
on_connect: None,
|
||||||
_t: PhantomData,
|
_t: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -75,6 +78,7 @@ where
|
|||||||
srv: service.into_new_service(),
|
srv: service.into_new_service(),
|
||||||
expect: h1::ExpectHandler,
|
expect: h1::ExpectHandler,
|
||||||
upgrade: None,
|
upgrade: None,
|
||||||
|
on_connect: None,
|
||||||
_t: PhantomData,
|
_t: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -104,6 +108,7 @@ where
|
|||||||
cfg: self.cfg,
|
cfg: self.cfg,
|
||||||
srv: self.srv,
|
srv: self.srv,
|
||||||
upgrade: self.upgrade,
|
upgrade: self.upgrade,
|
||||||
|
on_connect: self.on_connect,
|
||||||
_t: PhantomData,
|
_t: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -127,9 +132,19 @@ where
|
|||||||
cfg: self.cfg,
|
cfg: self.cfg,
|
||||||
srv: self.srv,
|
srv: self.srv,
|
||||||
expect: self.expect,
|
expect: self.expect,
|
||||||
|
on_connect: self.on_connect,
|
||||||
_t: PhantomData,
|
_t: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set on connect callback.
|
||||||
|
pub(crate) fn on_connect(
|
||||||
|
mut self,
|
||||||
|
f: Option<rc::Rc<Fn(&T) -> Box<dyn DataFactory>>>,
|
||||||
|
) -> Self {
|
||||||
|
self.on_connect = f;
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, P, S, B, X, U> NewService for HttpService<T, P, S, B, X, U>
|
impl<T, P, S, B, X, U> NewService for HttpService<T, P, S, B, X, U>
|
||||||
@ -167,6 +182,7 @@ where
|
|||||||
fut_upg: self.upgrade.as_ref().map(|f| f.new_service(cfg)),
|
fut_upg: self.upgrade.as_ref().map(|f| f.new_service(cfg)),
|
||||||
expect: None,
|
expect: None,
|
||||||
upgrade: None,
|
upgrade: None,
|
||||||
|
on_connect: self.on_connect.clone(),
|
||||||
cfg: Some(self.cfg.clone()),
|
cfg: Some(self.cfg.clone()),
|
||||||
_t: PhantomData,
|
_t: PhantomData,
|
||||||
}
|
}
|
||||||
@ -180,6 +196,7 @@ pub struct HttpServiceResponse<T, P, S: NewService, B, X: NewService, U: NewServ
|
|||||||
fut_upg: Option<U::Future>,
|
fut_upg: Option<U::Future>,
|
||||||
expect: Option<X::Service>,
|
expect: Option<X::Service>,
|
||||||
upgrade: Option<U::Service>,
|
upgrade: Option<U::Service>,
|
||||||
|
on_connect: Option<rc::Rc<Fn(&T) -> Box<dyn DataFactory>>>,
|
||||||
cfg: Option<ServiceConfig>,
|
cfg: Option<ServiceConfig>,
|
||||||
_t: PhantomData<(T, P, B)>,
|
_t: PhantomData<(T, P, B)>,
|
||||||
}
|
}
|
||||||
@ -229,6 +246,7 @@ where
|
|||||||
service,
|
service,
|
||||||
self.expect.take().unwrap(),
|
self.expect.take().unwrap(),
|
||||||
self.upgrade.take(),
|
self.upgrade.take(),
|
||||||
|
self.on_connect.clone(),
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -239,6 +257,7 @@ pub struct HttpServiceHandler<T, P, S, B, X, U> {
|
|||||||
expect: CloneableService<X>,
|
expect: CloneableService<X>,
|
||||||
upgrade: Option<CloneableService<U>>,
|
upgrade: Option<CloneableService<U>>,
|
||||||
cfg: ServiceConfig,
|
cfg: ServiceConfig,
|
||||||
|
on_connect: Option<rc::Rc<Fn(&T) -> Box<dyn DataFactory>>>,
|
||||||
_t: PhantomData<(T, P, B, X)>,
|
_t: PhantomData<(T, P, B, X)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -259,9 +278,11 @@ where
|
|||||||
srv: S,
|
srv: S,
|
||||||
expect: X,
|
expect: X,
|
||||||
upgrade: Option<U>,
|
upgrade: Option<U>,
|
||||||
|
on_connect: Option<rc::Rc<Fn(&T) -> Box<dyn DataFactory>>>,
|
||||||
) -> HttpServiceHandler<T, P, S, B, X, U> {
|
) -> HttpServiceHandler<T, P, S, B, X, U> {
|
||||||
HttpServiceHandler {
|
HttpServiceHandler {
|
||||||
cfg,
|
cfg,
|
||||||
|
on_connect,
|
||||||
srv: CloneableService::new(srv),
|
srv: CloneableService::new(srv),
|
||||||
expect: CloneableService::new(expect),
|
expect: CloneableService::new(expect),
|
||||||
upgrade: upgrade.map(|s| CloneableService::new(s)),
|
upgrade: upgrade.map(|s| CloneableService::new(s)),
|
||||||
@ -319,6 +340,13 @@ where
|
|||||||
|
|
||||||
fn call(&mut self, req: Self::Request) -> Self::Future {
|
fn call(&mut self, req: Self::Request) -> Self::Future {
|
||||||
let (io, _, proto) = req.into_parts();
|
let (io, _, proto) = req.into_parts();
|
||||||
|
|
||||||
|
let on_connect = if let Some(ref on_connect) = self.on_connect {
|
||||||
|
Some(on_connect(&io))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
match proto {
|
match proto {
|
||||||
Protocol::Http2 => {
|
Protocol::Http2 => {
|
||||||
let peer_addr = io.peer_addr();
|
let peer_addr = io.peer_addr();
|
||||||
@ -332,6 +360,7 @@ where
|
|||||||
self.cfg.clone(),
|
self.cfg.clone(),
|
||||||
self.srv.clone(),
|
self.srv.clone(),
|
||||||
peer_addr,
|
peer_addr,
|
||||||
|
on_connect,
|
||||||
))),
|
))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -342,6 +371,7 @@ where
|
|||||||
self.srv.clone(),
|
self.srv.clone(),
|
||||||
self.expect.clone(),
|
self.expect.clone(),
|
||||||
self.upgrade.clone(),
|
self.upgrade.clone(),
|
||||||
|
on_connect,
|
||||||
)),
|
)),
|
||||||
},
|
},
|
||||||
_ => HttpServiceHandlerResponse {
|
_ => HttpServiceHandlerResponse {
|
||||||
@ -352,6 +382,7 @@ where
|
|||||||
self.srv.clone(),
|
self.srv.clone(),
|
||||||
self.expect.clone(),
|
self.expect.clone(),
|
||||||
self.upgrade.clone(),
|
self.upgrade.clone(),
|
||||||
|
on_connect,
|
||||||
))),
|
))),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -380,6 +411,7 @@ where
|
|||||||
CloneableService<S>,
|
CloneableService<S>,
|
||||||
CloneableService<X>,
|
CloneableService<X>,
|
||||||
Option<CloneableService<U>>,
|
Option<CloneableService<U>>,
|
||||||
|
Option<Box<dyn DataFactory>>,
|
||||||
)>,
|
)>,
|
||||||
),
|
),
|
||||||
Handshake(
|
Handshake(
|
||||||
@ -388,6 +420,7 @@ where
|
|||||||
ServiceConfig,
|
ServiceConfig,
|
||||||
CloneableService<S>,
|
CloneableService<S>,
|
||||||
Option<net::SocketAddr>,
|
Option<net::SocketAddr>,
|
||||||
|
Option<Box<dyn DataFactory>>,
|
||||||
)>,
|
)>,
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
@ -448,7 +481,8 @@ where
|
|||||||
} else {
|
} else {
|
||||||
panic!()
|
panic!()
|
||||||
}
|
}
|
||||||
let (io, buf, cfg, srv, expect, upgrade) = data.take().unwrap();
|
let (io, buf, cfg, srv, expect, upgrade, on_connect) =
|
||||||
|
data.take().unwrap();
|
||||||
if buf[..14] == HTTP2_PREFACE[..] {
|
if buf[..14] == HTTP2_PREFACE[..] {
|
||||||
let peer_addr = io.peer_addr();
|
let peer_addr = io.peer_addr();
|
||||||
let io = Io {
|
let io = Io {
|
||||||
@ -460,6 +494,7 @@ where
|
|||||||
cfg,
|
cfg,
|
||||||
srv,
|
srv,
|
||||||
peer_addr,
|
peer_addr,
|
||||||
|
on_connect,
|
||||||
)));
|
)));
|
||||||
} else {
|
} else {
|
||||||
self.state = State::H1(h1::Dispatcher::with_timeout(
|
self.state = State::H1(h1::Dispatcher::with_timeout(
|
||||||
@ -471,6 +506,7 @@ where
|
|||||||
srv,
|
srv,
|
||||||
expect,
|
expect,
|
||||||
upgrade,
|
upgrade,
|
||||||
|
on_connect,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
self.poll()
|
self.poll()
|
||||||
@ -488,8 +524,10 @@ where
|
|||||||
} else {
|
} else {
|
||||||
panic!()
|
panic!()
|
||||||
};
|
};
|
||||||
let (_, cfg, srv, peer_addr) = data.take().unwrap();
|
let (_, cfg, srv, peer_addr, on_connect) = data.take().unwrap();
|
||||||
self.state = State::H2(Dispatcher::new(srv, conn, cfg, None, peer_addr));
|
self.state = State::H2(Dispatcher::new(
|
||||||
|
srv, conn, on_connect, cfg, None, peer_addr,
|
||||||
|
));
|
||||||
self.poll()
|
self.poll()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,12 @@
|
|||||||
# Changes
|
# Changes
|
||||||
|
|
||||||
|
## [0.2.0] - 2019-07-08
|
||||||
|
|
||||||
|
* Enhanced ``actix-session`` to facilitate state changes. Use ``Session.renew()``
|
||||||
|
at successful login to cycle a session (new key/cookie but keeps state).
|
||||||
|
Use ``Session.purge()`` at logout to invalid a session cookie (and remove
|
||||||
|
from redis cache, if applicable).
|
||||||
|
|
||||||
## [0.1.1] - 2019-06-03
|
## [0.1.1] - 2019-06-03
|
||||||
|
|
||||||
* Fix optional cookie session support
|
* Fix optional cookie session support
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "actix-session"
|
name = "actix-session"
|
||||||
version = "0.1.1"
|
version = "0.2.0"
|
||||||
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"
|
||||||
|
@ -6,4 +6,4 @@
|
|||||||
* [API Documentation](https://docs.rs/actix-session/)
|
* [API Documentation](https://docs.rs/actix-session/)
|
||||||
* [Chat on gitter](https://gitter.im/actix/actix)
|
* [Chat on gitter](https://gitter.im/actix/actix)
|
||||||
* Cargo package: [actix-session](https://crates.io/crates/actix-session)
|
* Cargo package: [actix-session](https://crates.io/crates/actix-session)
|
||||||
* Minimum supported Rust version: 1.33 or later
|
* Minimum supported Rust version: 1.34 or later
|
||||||
|
@ -28,7 +28,7 @@ use futures::future::{ok, Future, FutureResult};
|
|||||||
use futures::Poll;
|
use futures::Poll;
|
||||||
use serde_json::error::Error as JsonError;
|
use serde_json::error::Error as JsonError;
|
||||||
|
|
||||||
use crate::Session;
|
use crate::{Session, SessionStatus};
|
||||||
|
|
||||||
/// Errors that can occur during handling cookie session
|
/// Errors that can occur during handling cookie session
|
||||||
#[derive(Debug, From, Display)]
|
#[derive(Debug, From, Display)]
|
||||||
@ -119,7 +119,20 @@ impl CookieSessionInner {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load(&self, req: &ServiceRequest) -> HashMap<String, String> {
|
/// invalidates session cookie
|
||||||
|
fn remove_cookie<B>(&self, res: &mut ServiceResponse<B>) -> Result<(), Error> {
|
||||||
|
let mut cookie = Cookie::named(self.name.clone());
|
||||||
|
cookie.set_value("");
|
||||||
|
cookie.set_max_age(time::Duration::seconds(0));
|
||||||
|
cookie.set_expires(time::now() - time::Duration::days(365));
|
||||||
|
|
||||||
|
let val = HeaderValue::from_str(&cookie.to_string())?;
|
||||||
|
res.headers_mut().append(SET_COOKIE, val);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load(&self, req: &ServiceRequest) -> (bool, HashMap<String, String>) {
|
||||||
if let Ok(cookies) = req.cookies() {
|
if let Ok(cookies) = req.cookies() {
|
||||||
for cookie in cookies.iter() {
|
for cookie in cookies.iter() {
|
||||||
if cookie.name() == self.name {
|
if cookie.name() == self.name {
|
||||||
@ -134,13 +147,13 @@ impl CookieSessionInner {
|
|||||||
};
|
};
|
||||||
if let Some(cookie) = cookie_opt {
|
if let Some(cookie) = cookie_opt {
|
||||||
if let Ok(val) = serde_json::from_str(cookie.value()) {
|
if let Ok(val) = serde_json::from_str(cookie.value()) {
|
||||||
return val;
|
return (false, val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
HashMap::new()
|
(true, HashMap::new())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -302,16 +315,37 @@ where
|
|||||||
self.service.poll_ready()
|
self.service.poll_ready()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// On first request, a new session cookie is returned in response, regardless
|
||||||
|
/// of whether any session state is set. With subsequent requests, if the
|
||||||
|
/// session state changes, then set-cookie is returned in response. As
|
||||||
|
/// a user logs out, call session.purge() to set SessionStatus accordingly
|
||||||
|
/// and this will trigger removal of the session cookie in the response.
|
||||||
fn call(&mut self, mut req: ServiceRequest) -> Self::Future {
|
fn call(&mut self, mut req: ServiceRequest) -> Self::Future {
|
||||||
let inner = self.inner.clone();
|
let inner = self.inner.clone();
|
||||||
let state = self.inner.load(&req);
|
let (is_new, state) = self.inner.load(&req);
|
||||||
Session::set_session(state.into_iter(), &mut req);
|
Session::set_session(state.into_iter(), &mut req);
|
||||||
|
|
||||||
Box::new(self.service.call(req).map(move |mut res| {
|
Box::new(self.service.call(req).map(move |mut res| {
|
||||||
if let Some(state) = Session::get_changes(&mut res) {
|
match Session::get_changes(&mut res) {
|
||||||
res.checked_expr(|res| inner.set_cookie(res, state))
|
(SessionStatus::Changed, Some(state))
|
||||||
} else {
|
| (SessionStatus::Renewed, Some(state)) => {
|
||||||
res
|
res.checked_expr(|res| inner.set_cookie(res, state))
|
||||||
|
}
|
||||||
|
(SessionStatus::Unchanged, _) =>
|
||||||
|
// set a new session cookie upon first request (new client)
|
||||||
|
{
|
||||||
|
if is_new {
|
||||||
|
let state: HashMap<String, String> = HashMap::new();
|
||||||
|
res.checked_expr(|res| inner.set_cookie(res, state.into_iter()))
|
||||||
|
} else {
|
||||||
|
res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(SessionStatus::Purged, _) => {
|
||||||
|
inner.remove_cookie(&mut res);
|
||||||
|
res
|
||||||
|
}
|
||||||
|
_ => res,
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
@ -98,10 +98,23 @@ impl UserSession for ServiceRequest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Clone, Debug)]
|
||||||
|
pub enum SessionStatus {
|
||||||
|
Changed,
|
||||||
|
Purged,
|
||||||
|
Renewed,
|
||||||
|
Unchanged,
|
||||||
|
}
|
||||||
|
impl Default for SessionStatus {
|
||||||
|
fn default() -> SessionStatus {
|
||||||
|
SessionStatus::Unchanged
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct SessionInner {
|
struct SessionInner {
|
||||||
state: HashMap<String, String>,
|
state: HashMap<String, String>,
|
||||||
changed: bool,
|
pub status: SessionStatus,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Session {
|
impl Session {
|
||||||
@ -117,25 +130,46 @@ impl Session {
|
|||||||
/// Set a `value` from the session.
|
/// Set a `value` from the session.
|
||||||
pub fn set<T: Serialize>(&self, key: &str, value: T) -> Result<(), Error> {
|
pub fn set<T: Serialize>(&self, key: &str, value: T) -> Result<(), Error> {
|
||||||
let mut inner = self.0.borrow_mut();
|
let mut inner = self.0.borrow_mut();
|
||||||
inner.changed = true;
|
if inner.status != SessionStatus::Purged {
|
||||||
inner
|
inner.status = SessionStatus::Changed;
|
||||||
.state
|
inner
|
||||||
.insert(key.to_owned(), serde_json::to_string(&value)?);
|
.state
|
||||||
|
.insert(key.to_owned(), serde_json::to_string(&value)?);
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Remove value from the session.
|
/// Remove value from the session.
|
||||||
pub fn remove(&self, key: &str) {
|
pub fn remove(&self, key: &str) {
|
||||||
let mut inner = self.0.borrow_mut();
|
let mut inner = self.0.borrow_mut();
|
||||||
inner.changed = true;
|
if inner.status != SessionStatus::Purged {
|
||||||
inner.state.remove(key);
|
inner.status = SessionStatus::Changed;
|
||||||
|
inner.state.remove(key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clear the session.
|
/// Clear the session.
|
||||||
pub fn clear(&self) {
|
pub fn clear(&self) {
|
||||||
let mut inner = self.0.borrow_mut();
|
let mut inner = self.0.borrow_mut();
|
||||||
inner.changed = true;
|
if inner.status != SessionStatus::Purged {
|
||||||
inner.state.clear()
|
inner.status = SessionStatus::Changed;
|
||||||
|
inner.state.clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes session, both client and server side.
|
||||||
|
pub fn purge(&self) {
|
||||||
|
let mut inner = self.0.borrow_mut();
|
||||||
|
inner.status = SessionStatus::Purged;
|
||||||
|
inner.state.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Renews the session key, assigning existing session state to new key.
|
||||||
|
pub fn renew(&self) {
|
||||||
|
let mut inner = self.0.borrow_mut();
|
||||||
|
if inner.status != SessionStatus::Purged {
|
||||||
|
inner.status = SessionStatus::Renewed;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_session(
|
pub fn set_session(
|
||||||
@ -149,7 +183,10 @@ impl Session {
|
|||||||
|
|
||||||
pub fn get_changes<B>(
|
pub fn get_changes<B>(
|
||||||
res: &mut ServiceResponse<B>,
|
res: &mut ServiceResponse<B>,
|
||||||
) -> Option<impl Iterator<Item = (String, String)>> {
|
) -> (
|
||||||
|
SessionStatus,
|
||||||
|
Option<impl Iterator<Item = (String, String)>>,
|
||||||
|
) {
|
||||||
if let Some(s_impl) = res
|
if let Some(s_impl) = res
|
||||||
.request()
|
.request()
|
||||||
.extensions()
|
.extensions()
|
||||||
@ -157,9 +194,9 @@ impl Session {
|
|||||||
{
|
{
|
||||||
let state =
|
let state =
|
||||||
std::mem::replace(&mut s_impl.borrow_mut().state, HashMap::new());
|
std::mem::replace(&mut s_impl.borrow_mut().state, HashMap::new());
|
||||||
Some(state.into_iter())
|
(s_impl.borrow().status.clone(), Some(state.into_iter()))
|
||||||
} else {
|
} else {
|
||||||
None
|
(SessionStatus::Unchanged, None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -224,7 +261,8 @@ mod tests {
|
|||||||
session.remove("key");
|
session.remove("key");
|
||||||
|
|
||||||
let mut res = req.into_response(HttpResponse::Ok().finish());
|
let mut res = req.into_response(HttpResponse::Ok().finish());
|
||||||
let changes: Vec<_> = Session::get_changes(&mut res).unwrap().collect();
|
let (_status, state) = Session::get_changes(&mut res);
|
||||||
|
let changes: Vec<_> = state.unwrap().collect();
|
||||||
assert_eq!(changes, [("key2".to_string(), "\"value2\"".to_string())]);
|
assert_eq!(changes, [("key2".to_string(), "\"value2\"".to_string())]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -241,4 +279,22 @@ mod tests {
|
|||||||
let res = session.get::<String>("key").unwrap();
|
let res = session.get::<String>("key").unwrap();
|
||||||
assert_eq!(res, Some("value".to_string()));
|
assert_eq!(res, Some("value".to_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn purge_session() {
|
||||||
|
let mut req = test::TestRequest::default().to_srv_request();
|
||||||
|
let session = Session::get_session(&mut *req.extensions_mut());
|
||||||
|
assert_eq!(session.0.borrow().status, SessionStatus::Unchanged);
|
||||||
|
session.purge();
|
||||||
|
assert_eq!(session.0.borrow().status, SessionStatus::Purged);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn renew_session() {
|
||||||
|
let mut req = test::TestRequest::default().to_srv_request();
|
||||||
|
let session = Session::get_session(&mut *req.extensions_mut());
|
||||||
|
assert_eq!(session.0.borrow().status, SessionStatus::Unchanged);
|
||||||
|
session.renew();
|
||||||
|
assert_eq!(session.0.borrow().status, SessionStatus::Renewed);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,8 +19,8 @@ path = "src/lib.rs"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix = "0.8.3"
|
actix = "0.8.3"
|
||||||
actix-web = "1.0.2"
|
actix-web = "1.0.3"
|
||||||
actix-http = "0.2.4"
|
actix-http = "0.2.5"
|
||||||
actix-codec = "0.1.2"
|
actix-codec = "0.1.2"
|
||||||
bytes = "0.4"
|
bytes = "0.4"
|
||||||
futures = "0.1.25"
|
futures = "0.1.25"
|
||||||
|
@ -1,5 +1,14 @@
|
|||||||
# Changes
|
# Changes
|
||||||
|
|
||||||
|
## [0.2.2] - 2019-07-01
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
* Always append a colon after username in basic auth
|
||||||
|
|
||||||
|
* Upgrade `rand` dependency version to 0.7
|
||||||
|
|
||||||
|
|
||||||
## [0.2.1] - 2019-06-05
|
## [0.2.1] - 2019-06-05
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "awc"
|
name = "awc"
|
||||||
version = "0.2.1"
|
version = "0.2.2"
|
||||||
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"
|
||||||
@ -49,7 +49,7 @@ futures = "0.1.25"
|
|||||||
log =" 0.4"
|
log =" 0.4"
|
||||||
mime = "0.3"
|
mime = "0.3"
|
||||||
percent-encoding = "1.0"
|
percent-encoding = "1.0"
|
||||||
rand = "0.6"
|
rand = "0.7"
|
||||||
serde = "1.0"
|
serde = "1.0"
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
serde_urlencoded = "0.5.3"
|
serde_urlencoded = "0.5.3"
|
||||||
@ -66,5 +66,5 @@ actix-server = { version = "0.5.1", 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"
|
||||||
rand = "0.6"
|
rand = "0.7"
|
||||||
tokio-tcp = "0.1"
|
tokio-tcp = "0.1"
|
||||||
|
@ -115,7 +115,7 @@ impl ClientBuilder {
|
|||||||
{
|
{
|
||||||
let auth = match password {
|
let auth = match password {
|
||||||
Some(password) => format!("{}:{}", username, password),
|
Some(password) => format!("{}:{}", username, password),
|
||||||
None => format!("{}", username),
|
None => format!("{}:", username),
|
||||||
};
|
};
|
||||||
self.header(
|
self.header(
|
||||||
header::AUTHORIZATION,
|
header::AUTHORIZATION,
|
||||||
@ -164,7 +164,7 @@ mod tests {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.to_str()
|
.to_str()
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
"Basic dXNlcm5hbWU="
|
"Basic dXNlcm5hbWU6"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
//! An HTTP Client
|
//! An HTTP Client
|
||||||
//!
|
//!
|
||||||
//! ```rust
|
//! ```rust
|
||||||
//! # use futures::future::{Future, lazy};
|
//! use futures::future::{lazy, Future};
|
||||||
//! use actix_rt::System;
|
//! use actix_rt::System;
|
||||||
//! use awc::Client;
|
//! use awc::Client;
|
||||||
//!
|
//!
|
||||||
|
@ -280,7 +280,7 @@ impl ClientRequest {
|
|||||||
{
|
{
|
||||||
let auth = match password {
|
let auth = match password {
|
||||||
Some(password) => format!("{}:{}", username, password),
|
Some(password) => format!("{}:{}", username, password),
|
||||||
None => format!("{}", username),
|
None => format!("{}:", username),
|
||||||
};
|
};
|
||||||
self.header(
|
self.header(
|
||||||
header::AUTHORIZATION,
|
header::AUTHORIZATION,
|
||||||
@ -664,7 +664,7 @@ mod tests {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.to_str()
|
.to_str()
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
"Basic dXNlcm5hbWU="
|
"Basic dXNlcm5hbWU6"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -195,7 +195,7 @@ impl WebsocketsRequest {
|
|||||||
{
|
{
|
||||||
let auth = match password {
|
let auth = match password {
|
||||||
Some(password) => format!("{}:{}", username, password),
|
Some(password) => format!("{}:{}", username, password),
|
||||||
None => format!("{}", username),
|
None => format!("{}:", username),
|
||||||
};
|
};
|
||||||
self.header(AUTHORIZATION, format!("Basic {}", base64::encode(&auth)))
|
self.header(AUTHORIZATION, format!("Basic {}", base64::encode(&auth)))
|
||||||
}
|
}
|
||||||
@ -443,7 +443,7 @@ mod tests {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.to_str()
|
.to_str()
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
"Basic dXNlcm5hbWU="
|
"Basic dXNlcm5hbWU6"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,6 +186,9 @@ impl HttpRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get *ConnectionInfo* for the current request.
|
/// Get *ConnectionInfo* for the current request.
|
||||||
|
///
|
||||||
|
/// This method panics if request's extensions container is already
|
||||||
|
/// borrowed.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn connection_info(&self) -> Ref<ConnectionInfo> {
|
pub fn connection_info(&self) -> Ref<ConnectionInfo> {
|
||||||
ConnectionInfo::get(self.head(), &*self.app_config())
|
ConnectionInfo::get(self.head(), &*self.app_config())
|
||||||
|
@ -137,6 +137,22 @@ impl Responder for () {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T> Responder for (T, StatusCode)
|
||||||
|
where
|
||||||
|
T: Responder,
|
||||||
|
{
|
||||||
|
type Error = T::Error;
|
||||||
|
type Future = CustomResponderFut<T>;
|
||||||
|
|
||||||
|
fn respond_to(self, req: &HttpRequest) -> Self::Future {
|
||||||
|
CustomResponderFut {
|
||||||
|
fut: self.0.respond_to(req).into_future(),
|
||||||
|
status: Some(self.1),
|
||||||
|
headers: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Responder for &'static str {
|
impl Responder for &'static str {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Future = FutureResult<Response, Error>;
|
type Future = FutureResult<Response, Error>;
|
||||||
@ -624,4 +640,28 @@ pub(crate) mod tests {
|
|||||||
HeaderValue::from_static("json")
|
HeaderValue::from_static("json")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_tuple_responder_with_status_code() {
|
||||||
|
let req = TestRequest::default().to_http_request();
|
||||||
|
let res =
|
||||||
|
block_on(("test".to_string(), StatusCode::BAD_REQUEST).respond_to(&req))
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(res.status(), StatusCode::BAD_REQUEST);
|
||||||
|
assert_eq!(res.body().bin_ref(), b"test");
|
||||||
|
|
||||||
|
let req = TestRequest::default().to_http_request();
|
||||||
|
let res = block_on(
|
||||||
|
("test".to_string(), StatusCode::OK)
|
||||||
|
.with_header("content-type", "json")
|
||||||
|
.respond_to(&req),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(res.status(), StatusCode::OK);
|
||||||
|
assert_eq!(res.body().bin_ref(), b"test");
|
||||||
|
assert_eq!(
|
||||||
|
res.headers().get(CONTENT_TYPE).unwrap(),
|
||||||
|
HeaderValue::from_static("json")
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -64,7 +64,7 @@ where
|
|||||||
RT.with(move |rt| rt.borrow_mut().get_mut().block_on(f.into_future()))
|
RT.with(move |rt| rt.borrow_mut().get_mut().block_on(f.into_future()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Runs the provided function, blocking the current thread until the resul
|
/// Runs the provided function, blocking the current thread until the result
|
||||||
/// future completes.
|
/// future completes.
|
||||||
///
|
///
|
||||||
/// This function can be used to synchronously block the current thread
|
/// This function can be used to synchronously block the current thread
|
||||||
|
@ -13,7 +13,7 @@ use crate::extract::FromRequest;
|
|||||||
use crate::request::HttpRequest;
|
use crate::request::HttpRequest;
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(PartialEq, Eq, PartialOrd, Ord)]
|
||||||
/// Extract typed information from from the request's query.
|
/// Extract typed information from the request's query.
|
||||||
///
|
///
|
||||||
/// ## Example
|
/// ## Example
|
||||||
///
|
///
|
||||||
@ -90,7 +90,7 @@ impl<T: fmt::Display> fmt::Display for Query<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extract typed information from from the request's query.
|
/// Extract typed information from the request's query.
|
||||||
///
|
///
|
||||||
/// ## Example
|
/// ## Example
|
||||||
///
|
///
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
# Changes
|
# Changes
|
||||||
|
|
||||||
|
## [0.2.3] - 2019-07-16
|
||||||
|
|
||||||
|
* Add `delete`, `options`, `patch` methods to `TestServerRunner`
|
||||||
|
|
||||||
## [0.2.2] - 2019-06-16
|
## [0.2.2] - 2019-06-16
|
||||||
|
|
||||||
* Add .put() and .sput() methods
|
* Add .put() and .sput() methods
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "actix-http-test"
|
name = "actix-http-test"
|
||||||
version = "0.2.2"
|
version = "0.2.3"
|
||||||
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"
|
||||||
@ -35,7 +35,7 @@ actix-rt = "0.2.2"
|
|||||||
actix-service = "0.4.1"
|
actix-service = "0.4.1"
|
||||||
actix-server = "0.5.1"
|
actix-server = "0.5.1"
|
||||||
actix-utils = "0.4.1"
|
actix-utils = "0.4.1"
|
||||||
awc = "0.2.1"
|
awc = "0.2.2"
|
||||||
|
|
||||||
base64 = "0.10"
|
base64 = "0.10"
|
||||||
bytes = "0.4"
|
bytes = "0.4"
|
||||||
|
@ -265,6 +265,36 @@ impl TestServerRuntime {
|
|||||||
self.client.put(self.surl(path.as_ref()).as_str())
|
self.client.put(self.surl(path.as_ref()).as_str())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create `PATCH` request
|
||||||
|
pub fn patch<S: AsRef<str>>(&self, path: S) -> ClientRequest {
|
||||||
|
self.client.patch(self.url(path.as_ref()).as_str())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create https `PATCH` request
|
||||||
|
pub fn spatch<S: AsRef<str>>(&self, path: S) -> ClientRequest {
|
||||||
|
self.client.patch(self.surl(path.as_ref()).as_str())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create `DELETE` request
|
||||||
|
pub fn delete<S: AsRef<str>>(&self, path: S) -> ClientRequest {
|
||||||
|
self.client.delete(self.url(path.as_ref()).as_str())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create https `DELETE` request
|
||||||
|
pub fn sdelete<S: AsRef<str>>(&self, path: S) -> ClientRequest {
|
||||||
|
self.client.delete(self.surl(path.as_ref()).as_str())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create `OPTIONS` request
|
||||||
|
pub fn options<S: AsRef<str>>(&self, path: S) -> ClientRequest {
|
||||||
|
self.client.options(self.url(path.as_ref()).as_str())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create https `OPTIONS` request
|
||||||
|
pub fn soptions<S: AsRef<str>>(&self, path: S) -> ClientRequest {
|
||||||
|
self.client.options(self.surl(path.as_ref()).as_str())
|
||||||
|
}
|
||||||
|
|
||||||
/// Connect to test http server
|
/// Connect to test http server
|
||||||
pub fn request<S: AsRef<str>>(&self, method: Method, path: S) -> ClientRequest {
|
pub fn request<S: AsRef<str>>(&self, method: Method, path: S) -> ClientRequest {
|
||||||
self.client.request(method, path.as_ref())
|
self.client.request(method, path.as_ref())
|
||||||
|
Reference in New Issue
Block a user