1
0
mirror of https://github.com/fafhrd91/actix-web synced 2025-08-19 04:15:38 +02:00

Compare commits

..

16 Commits

Author SHA1 Message Date
Nikolay Kim
e7ec77aa81 update readme 2019-04-16 10:50:37 -07:00
Nikolay Kim
ddfd7523f7 prepare awc release 2019-04-16 10:49:38 -07:00
Nikolay Kim
2986077a28 no need for feature 2019-04-16 10:32:48 -07:00
Nikolay Kim
3744957804 actix_http::encoding always available 2019-04-16 10:27:58 -07:00
Nikolay Kim
420d3064c5 Add .peer_addr() #744 2019-04-16 10:11:38 -07:00
Nikolay Kim
eb4f6b74fb Merge branch 'master' of github.com:actix/actix-web 2019-04-16 09:58:07 -07:00
Nikolay Kim
a116c4c2c7 Expose peer addr via Request::peer_addr() and RequestHead::peer_addr 2019-04-16 09:54:02 -07:00
Travis Harmon
7f674febb1 add 422 to httpcodes.rs (#782) 2019-04-15 16:55:06 -07:00
Nikolay Kim
14252f5ef2 use test::call_service 2019-04-15 09:09:21 -07:00
Nikolay Kim
7a28b32f6d Rename test::call_success to test::call_service 2019-04-15 07:44:07 -07:00
Nikolay Kim
09cdf1e302 Rename RouterConfig to ServiceConfig 2019-04-15 07:32:49 -07:00
Nikolay Kim
1eebd47072 fix warnings 2019-04-14 21:00:16 -07:00
Nikolay Kim
002c41a7ca update trust-dns 2019-04-14 20:45:44 -07:00
Nikolay Kim
ab4fda6084 update tests 2019-04-14 20:20:33 -07:00
Nikolay Kim
f9078d41cd add test::read_response; fix TestRequest::app_data() 2019-04-14 19:52:12 -07:00
Darin
4cc2b38059 added read_response_json for testing (#776)
* added read_response_json for testing

* cleaned up

* modied docs for read_response_json

* typo in doc

* test code in doc should compile now

* use type coercion in doc

* removed generic R, replaced with Request
2019-04-14 16:25:45 -07:00
46 changed files with 741 additions and 278 deletions

View File

@@ -1,5 +1,25 @@
# Changes
## [1.0.0-beta.1] - 2019-04-xx
### Added
* Add helper functions for reading test response body,
`test::read_response()` and test::read_response_json()`
* Add `.peer_addr()` #744
### Changed
* Rename `RouterConfig` to `ServiceConfig`
* Rename `test::call_success` to `test::call_service`
### Fixed
* Fixed `TestRequest::app_data()`
## [1.0.0-alpha.6] - 2019-04-14
### Changed

View File

@@ -71,9 +71,9 @@ actix-utils = "0.3.4"
actix-router = "0.1.2"
actix-rt = "0.2.2"
actix-web-codegen = "0.1.0-alpha.6"
actix-http = { version = "0.1.0-alpha.5", features=["fail"] }
actix-server = "0.4.2"
actix-server-config = "0.1.0"
actix-http = { version = "0.1.0", features=["fail"] }
actix-server = "0.4.3"
actix-server-config = "0.1.1"
actix-threadpool = "0.1.0"
awc = { version = "0.1.0-alpha.6", optional = true }
@@ -81,7 +81,7 @@ bytes = "0.4"
derive_more = "0.14"
encoding = "0.2"
futures = "0.1"
hashbrown = "0.2.1"
hashbrown = "0.2.2"
log = "0.4"
mime = "0.3"
net2 = "0.2.33"
@@ -98,7 +98,7 @@ openssl = { version="0.10", optional = true }
rustls = { version = "^0.15", optional = true }
[dev-dependencies]
actix-http = { version = "0.1.0-alpha.5", features=["ssl", "brotli", "flate2-zlib"] }
actix-http = { version = "0.1.0", features=["ssl", "brotli", "flate2-zlib"] }
actix-http-test = { version = "0.1.0-alpha.3", features=["ssl"] }
actix-files = { version = "0.1.0-alpha.6" }
rand = "0.6"
@@ -121,4 +121,4 @@ actix-web-codegen = { path = "actix-web-codegen" }
actix-web-actors = { path = "actix-web-actors" }
actix-session = { path = "actix-session" }
actix-files = { path = "actix-files" }
awc = { path = "awc" }
awc = { path = "awc" }

View File

@@ -10,7 +10,7 @@ use std::{cmp, io};
use actix_service::boxed::{self, BoxedNewService, BoxedService};
use actix_service::{IntoNewService, NewService, Service};
use actix_web::dev::{
HttpServiceFactory, Payload, ResourceDef, ServiceConfig, ServiceRequest,
AppService, HttpServiceFactory, Payload, ResourceDef, ServiceRequest,
ServiceResponse,
};
use actix_web::error::{BlockingError, Error, ErrorInternalServerError};
@@ -349,7 +349,7 @@ impl Files {
}
impl HttpServiceFactory for Files {
fn register(self, config: &mut ServiceConfig) {
fn register(self, config: &mut AppService) {
if self.default.borrow().is_none() {
*self.default.borrow_mut() = Some(config.default_service());
}
@@ -775,7 +775,7 @@ mod tests {
);
let request = TestRequest::get().uri("/").to_request();
let response = test::call_success(&mut srv, request);
let response = test::call_service(&mut srv, request);
assert_eq!(response.status(), StatusCode::OK);
let content_disposition = response
@@ -799,7 +799,7 @@ mod tests {
.uri("/t%65st/Cargo.toml")
.header(header::RANGE, "bytes=10-20")
.to_request();
let response = test::call_success(&mut srv, request);
let response = test::call_service(&mut srv, request);
assert_eq!(response.status(), StatusCode::PARTIAL_CONTENT);
// Invalid range header
@@ -807,7 +807,7 @@ mod tests {
.uri("/t%65st/Cargo.toml")
.header(header::RANGE, "bytes=1-0")
.to_request();
let response = test::call_success(&mut srv, request);
let response = test::call_service(&mut srv, request);
assert_eq!(response.status(), StatusCode::RANGE_NOT_SATISFIABLE);
}
@@ -824,7 +824,7 @@ mod tests {
.header(header::RANGE, "bytes=10-20")
.to_request();
let response = test::call_success(&mut srv, request);
let response = test::call_service(&mut srv, request);
let contentrange = response
.headers()
.get(header::CONTENT_RANGE)
@@ -839,7 +839,7 @@ mod tests {
.uri("/t%65st/tests/test.binary")
.header(header::RANGE, "bytes=10-5")
.to_request();
let response = test::call_success(&mut srv, request);
let response = test::call_service(&mut srv, request);
let contentrange = response
.headers()
@@ -862,7 +862,7 @@ mod tests {
.uri("/t%65st/tests/test.binary")
.header(header::RANGE, "bytes=10-20")
.to_request();
let response = test::call_success(&mut srv, request);
let response = test::call_service(&mut srv, request);
let contentlength = response
.headers()
@@ -878,7 +878,7 @@ mod tests {
.uri("/t%65st/tests/test.binary")
.header(header::RANGE, "bytes=10-8")
.to_request();
let response = test::call_success(&mut srv, request);
let response = test::call_service(&mut srv, request);
assert_eq!(response.status(), StatusCode::RANGE_NOT_SATISFIABLE);
// Without range header
@@ -886,7 +886,7 @@ mod tests {
.uri("/t%65st/tests/test.binary")
// .no_default_headers()
.to_request();
let response = test::call_success(&mut srv, request);
let response = test::call_service(&mut srv, request);
let contentlength = response
.headers()
@@ -901,7 +901,7 @@ mod tests {
let request = TestRequest::get()
.uri("/t%65st/tests/test.binary")
.to_request();
let mut response = test::call_success(&mut srv, request);
let mut response = test::call_service(&mut srv, request);
// with enabled compression
// {
@@ -932,7 +932,7 @@ mod tests {
let request = TestRequest::get()
.uri("/tests/test%20space.binary")
.to_request();
let mut response = test::call_success(&mut srv, request);
let mut response = test::call_service(&mut srv, request);
assert_eq!(response.status(), StatusCode::OK);
let bytes =
@@ -975,7 +975,7 @@ mod tests {
.uri("/")
.header(header::ACCEPT_ENCODING, "gzip")
.to_request();
let res = test::call_success(&mut srv, request);
let res = test::call_service(&mut srv, request);
assert_eq!(res.status(), StatusCode::OK);
assert!(!res.headers().contains_key(header::CONTENT_ENCODING));
}
@@ -994,7 +994,7 @@ mod tests {
.uri("/")
.header(header::ACCEPT_ENCODING, "gzip")
.to_request();
let res = test::call_success(&mut srv, request);
let res = test::call_service(&mut srv, request);
assert_eq!(res.status(), StatusCode::OK);
assert_eq!(
res.headers()
@@ -1021,20 +1021,20 @@ mod tests {
);
let req = TestRequest::with_uri("/missing").to_request();
let resp = test::call_success(&mut srv, req);
let resp = test::call_service(&mut srv, req);
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
let mut srv = test::init_service(App::new().service(Files::new("/", ".")));
let req = TestRequest::default().to_request();
let resp = test::call_success(&mut srv, req);
let resp = test::call_service(&mut srv, req);
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
let mut srv = test::init_service(
App::new().service(Files::new("/", ".").show_files_listing()),
);
let req = TestRequest::with_uri("/tests").to_request();
let mut resp = test::call_success(&mut srv, req);
let mut resp = test::call_service(&mut srv, req);
assert_eq!(
resp.headers().get(header::CONTENT_TYPE).unwrap(),
"text/html; charset=utf-8"
@@ -1067,7 +1067,7 @@ mod tests {
.unwrap();
let req = TestRequest::with_uri("/missing").to_srv_request();
let mut resp = test::call_success(&mut st, req);
let mut resp = test::call_service(&mut st, req);
assert_eq!(resp.status(), StatusCode::OK);
let bytes =
test::block_on(resp.take_body().fold(BytesMut::new(), |mut b, c| {

View File

@@ -1,5 +1,18 @@
# Changes
## [0.1.0] - 2019-04-16
### Added
* Expose peer addr via `Request::peer_addr()` and `RequestHead::peer_addr`
### Changed
* `actix_http::encoding` always available
* use trust-dns-resolver 0.11.0
## [0.1.0-alpha.5] - 2019-04-12
### Added

View File

@@ -1,6 +1,6 @@
[package]
name = "actix-http"
version = "0.1.0-alpha.5"
version = "0.1.0"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Actix http primitives"
readme = "README.md"
@@ -48,7 +48,7 @@ actix-service = "0.3.6"
actix-codec = "0.1.2"
actix-connect = "0.1.4"
actix-utils = "0.3.5"
actix-server-config = "0.1.0"
actix-server-config = "0.1.1"
actix-threadpool = "0.1.0"
base64 = "0.10"
@@ -60,7 +60,7 @@ derive_more = "0.14"
either = "1.5.2"
encoding = "0.2"
futures = "0.1"
hashbrown = "0.2.0"
hashbrown = "0.2.2"
h2 = "0.1.16"
http = "0.1.17"
httparse = "1.3"
@@ -76,12 +76,12 @@ serde = "1.0"
serde_json = "1.0"
sha1 = "0.6"
slab = "0.4"
serde_urlencoded = "0.5.3"
serde_urlencoded = "0.5.5"
time = "0.1"
tokio-tcp = "0.1.3"
tokio-timer = "0.2"
tokio-timer = "0.2.8"
tokio-current-thread = "0.1"
trust-dns-resolver = { version="0.11.0-alpha.3", default-features = false }
trust-dns-resolver = { version="0.11.0", default-features = false }
# for secure cookie
ring = { version = "0.14.6", optional = true }
@@ -96,7 +96,7 @@ openssl = { version="0.10", optional = true }
[dev-dependencies]
actix-rt = "0.2.2"
actix-server = { version = "0.4.1", features=["ssl"] }
actix-server = { version = "0.4.3", features=["ssl"] }
actix-connect = { version = "0.1.4", features=["ssl"] }
actix-http-test = { version = "0.1.0-alpha.3", features=["ssl"] }
env_logger = "0.6"

View File

@@ -57,8 +57,7 @@ impl Connector<(), ()> {
let ssl = {
#[cfg(feature = "ssl")]
{
use log::error;
use openssl::ssl::{SslConnector, SslMethod};
use openssl::ssl::SslMethod;
let mut ssl = SslConnector::builder(SslMethod::tls()).unwrap();
let _ = ssl

View File

@@ -1,6 +1,6 @@
#![allow(unused_imports, unused_variables, dead_code)]
use std::fmt;
use std::io::{self, Write};
use std::io::Write;
use std::{fmt, io, net};
use actix_codec::{Decoder, Encoder};
use bitflags::bitflags;
@@ -40,7 +40,6 @@ pub struct Codec {
// encoder part
flags: Flags,
encoder: encoder::MessageEncoder<Response<()>>,
// headers_size: u32,
}
impl Default for Codec {
@@ -67,13 +66,11 @@ impl Codec {
};
Codec {
config,
flags,
decoder: decoder::MessageDecoder::default(),
payload: None,
version: Version::HTTP_11,
ctype: ConnectionType::Close,
flags,
// headers_size: 0,
encoder: encoder::MessageEncoder::default(),
}
}

View File

@@ -1,8 +1,9 @@
use std::collections::VecDeque;
use std::time::Instant;
use std::{fmt, io};
use std::{fmt, io, net};
use actix_codec::{AsyncRead, AsyncWrite, Decoder, Encoder, Framed, FramedParts};
use actix_codec::{Decoder, Encoder, Framed, FramedParts};
use actix_server_config::IoStream;
use actix_service::Service;
use actix_utils::cloneable::CloneableService;
use bitflags::bitflags;
@@ -81,6 +82,7 @@ where
expect: CloneableService<X>,
upgrade: Option<CloneableService<U>>,
flags: Flags,
peer_addr: Option<net::SocketAddr>,
error: Option<DispatchError>,
state: State<S, B, X>,
@@ -161,7 +163,7 @@ impl PartialEq for PollResponse {
impl<T, S, B, X, U> Dispatcher<T, S, B, X, U>
where
T: AsyncRead + AsyncWrite,
T: IoStream,
S: Service<Request = Request>,
S::Error: Into<Error>,
S::Response: Into<Response<B>>,
@@ -220,14 +222,15 @@ where
Dispatcher {
inner: DispatcherState::Normal(InnerDispatcher {
io,
codec,
read_buf,
write_buf: BytesMut::with_capacity(HW_BUFFER_SIZE),
payload: None,
state: State::None,
error: None,
peer_addr: io.peer_addr(),
messages: VecDeque::new(),
io,
codec,
read_buf,
service,
expect,
upgrade,
@@ -241,7 +244,7 @@ where
impl<T, S, B, X, U> InnerDispatcher<T, S, B, X, U>
where
T: AsyncRead + AsyncWrite,
T: IoStream,
S: Service<Request = Request>,
S::Error: Into<Error>,
S::Response: Into<Response<B>>,
@@ -490,6 +493,7 @@ where
match msg {
Message::Item(mut req) => {
let pl = self.codec.message_type();
req.head_mut().peer_addr = self.peer_addr;
if pl == MessageType::Stream && self.upgrade.is_some() {
self.messages.push_back(DispatcherMessage::Upgrade(req));
@@ -649,7 +653,7 @@ where
impl<T, S, B, X, U> Future for Dispatcher<T, S, B, X, U>
where
T: AsyncRead + AsyncWrite,
T: IoStream,
S: Service<Request = Request>,
S::Error: Into<Error>,
S::Response: Into<Response<B>>,

View File

@@ -1,8 +1,8 @@
use std::fmt;
use std::marker::PhantomData;
use actix_codec::{AsyncRead, AsyncWrite, Framed};
use actix_server_config::{Io, ServerConfig as SrvConfig};
use actix_codec::Framed;
use actix_server_config::{Io, IoStream, ServerConfig as SrvConfig};
use actix_service::{IntoNewService, NewService, Service};
use actix_utils::cloneable::CloneableService;
use futures::future::{ok, FutureResult};
@@ -104,7 +104,7 @@ where
impl<T, P, S, B, X, U> NewService<SrvConfig> for H1Service<T, P, S, B, X, U>
where
T: AsyncRead + AsyncWrite,
T: IoStream,
S: NewService<SrvConfig, Request = Request>,
S::Error: Into<Error>,
S::Response: Into<Response<B>>,
@@ -161,7 +161,7 @@ where
impl<T, P, S, B, X, U> Future for H1ServiceResponse<T, P, S, B, X, U>
where
T: AsyncRead + AsyncWrite,
T: IoStream,
S: NewService<SrvConfig, Request = Request>,
S::Error: Into<Error>,
S::Response: Into<Response<B>>,
@@ -245,7 +245,7 @@ where
impl<T, P, S, B, X, U> Service for H1ServiceHandler<T, P, S, B, X, U>
where
T: AsyncRead + AsyncWrite,
T: IoStream,
S: Service<Request = Request>,
S::Error: Into<Error>,
S::Response: Into<Response<B>>,
@@ -309,7 +309,7 @@ pub struct OneRequest<T, P> {
impl<T, P> OneRequest<T, P>
where
T: AsyncRead + AsyncWrite,
T: IoStream,
{
/// Create new `H1SimpleService` instance.
pub fn new() -> Self {
@@ -322,7 +322,7 @@ where
impl<T, P> NewService<SrvConfig> for OneRequest<T, P>
where
T: AsyncRead + AsyncWrite,
T: IoStream,
{
type Request = Io<T, P>;
type Response = (Request, Framed<T, Codec>);
@@ -348,7 +348,7 @@ pub struct OneRequestService<T, P> {
impl<T, P> Service for OneRequestService<T, P>
where
T: AsyncRead + AsyncWrite,
T: IoStream,
{
type Request = Io<T, P>;
type Response = (Request, Framed<T, Codec>);
@@ -372,14 +372,14 @@ where
#[doc(hidden)]
pub struct OneRequestServiceResponse<T>
where
T: AsyncRead + AsyncWrite,
T: IoStream,
{
framed: Option<Framed<T, Codec>>,
}
impl<T> Future for OneRequestServiceResponse<T>
where
T: AsyncRead + AsyncWrite,
T: IoStream,
{
type Item = (Request, Framed<T, Codec>);
type Error = ParseError;

View File

@@ -1,9 +1,10 @@
use std::collections::VecDeque;
use std::marker::PhantomData;
use std::time::Instant;
use std::{fmt, mem};
use std::{fmt, mem, net};
use actix_codec::{AsyncRead, AsyncWrite};
use actix_server_config::IoStream;
use actix_service::Service;
use actix_utils::cloneable::CloneableService;
use bitflags::bitflags;
@@ -29,14 +30,11 @@ use crate::response::Response;
const CHUNK_SIZE: usize = 16_384;
/// Dispatcher for HTTP/2 protocol
pub struct Dispatcher<
T: AsyncRead + AsyncWrite,
S: Service<Request = Request>,
B: MessageBody,
> {
pub struct Dispatcher<T: IoStream, S: Service<Request = Request>, B: MessageBody> {
service: CloneableService<S>,
connection: Connection<T, Bytes>,
config: ServiceConfig,
peer_addr: Option<net::SocketAddr>,
ka_expire: Instant,
ka_timer: Option<Delay>,
_t: PhantomData<B>,
@@ -44,7 +42,7 @@ pub struct Dispatcher<
impl<T, S, B> Dispatcher<T, S, B>
where
T: AsyncRead + AsyncWrite,
T: IoStream,
S: Service<Request = Request>,
S::Error: Into<Error>,
S::Future: 'static,
@@ -56,6 +54,7 @@ where
connection: Connection<T, Bytes>,
config: ServiceConfig,
timeout: Option<Delay>,
peer_addr: Option<net::SocketAddr>,
) -> Self {
// let keepalive = config.keep_alive_enabled();
// let flags = if keepalive {
@@ -76,9 +75,10 @@ where
Dispatcher {
service,
config,
peer_addr,
connection,
ka_expire,
ka_timer,
connection,
_t: PhantomData,
}
}
@@ -86,7 +86,7 @@ where
impl<T, S, B> Future for Dispatcher<T, S, B>
where
T: AsyncRead + AsyncWrite,
T: IoStream,
S: Service<Request = Request>,
S::Error: Into<Error>,
S::Future: 'static,
@@ -117,6 +117,7 @@ where
head.method = parts.method;
head.version = parts.version;
head.headers = parts.headers.into();
head.peer_addr = self.peer_addr;
tokio_current_thread::spawn(ServiceResponse::<S::Future, B> {
state: ServiceResponseState::ServiceCall(
self.service.call(req),

View File

@@ -3,7 +3,7 @@ use std::marker::PhantomData;
use std::{io, net};
use actix_codec::{AsyncRead, AsyncWrite, Framed};
use actix_server_config::{Io, ServerConfig as SrvConfig};
use actix_server_config::{Io, IoStream, ServerConfig as SrvConfig};
use actix_service::{IntoNewService, NewService, Service};
use actix_utils::cloneable::CloneableService;
use bytes::Bytes;
@@ -63,7 +63,7 @@ where
impl<T, P, S, B> NewService<SrvConfig> for H2Service<T, P, S, B>
where
T: AsyncRead + AsyncWrite,
T: IoStream,
S: NewService<SrvConfig, Request = Request>,
S::Error: Into<Error>,
S::Response: Into<Response<B>>,
@@ -95,7 +95,7 @@ pub struct H2ServiceResponse<T, P, S: NewService<SrvConfig, Request = Request>,
impl<T, P, S, B> Future for H2ServiceResponse<T, P, S, B>
where
T: AsyncRead + AsyncWrite,
T: IoStream,
S: NewService<SrvConfig, Request = Request>,
S::Error: Into<Error>,
S::Response: Into<Response<B>>,
@@ -140,7 +140,7 @@ where
impl<T, P, S, B> Service for H2ServiceHandler<T, P, S, B>
where
T: AsyncRead + AsyncWrite,
T: IoStream,
S: Service<Request = Request>,
S::Error: Into<Error>,
S::Future: 'static,
@@ -161,17 +161,20 @@ where
}
fn call(&mut self, req: Self::Request) -> Self::Future {
let io = req.into_parts().0;
let peer_addr = io.peer_addr();
H2ServiceHandlerResponse {
state: State::Handshake(
Some(self.srv.clone()),
Some(self.cfg.clone()),
server::handshake(req.into_parts().0),
peer_addr,
server::handshake(io),
),
}
}
}
enum State<T: AsyncRead + AsyncWrite, S: Service<Request = Request>, B: MessageBody>
enum State<T: IoStream, S: Service<Request = Request>, B: MessageBody>
where
S::Future: 'static,
{
@@ -179,13 +182,14 @@ where
Handshake(
Option<CloneableService<S>>,
Option<ServiceConfig>,
Option<net::SocketAddr>,
Handshake<T, Bytes>,
),
}
pub struct H2ServiceHandlerResponse<T, S, B>
where
T: AsyncRead + AsyncWrite,
T: IoStream,
S: Service<Request = Request>,
S::Error: Into<Error>,
S::Future: 'static,
@@ -197,7 +201,7 @@ where
impl<T, S, B> Future for H2ServiceHandlerResponse<T, S, B>
where
T: AsyncRead + AsyncWrite,
T: IoStream,
S: Service<Request = Request>,
S::Error: Into<Error>,
S::Future: 'static,
@@ -210,24 +214,28 @@ where
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
match self.state {
State::Incoming(ref mut disp) => disp.poll(),
State::Handshake(ref mut srv, ref mut config, ref mut handshake) => {
match handshake.poll() {
Ok(Async::Ready(conn)) => {
self.state = State::Incoming(Dispatcher::new(
srv.take().unwrap(),
conn,
config.take().unwrap(),
None,
));
self.poll()
}
Ok(Async::NotReady) => Ok(Async::NotReady),
Err(err) => {
trace!("H2 handshake error: {}", err);
Err(err.into())
}
State::Handshake(
ref mut srv,
ref mut config,
ref peer_addr,
ref mut handshake,
) => match handshake.poll() {
Ok(Async::Ready(conn)) => {
self.state = State::Incoming(Dispatcher::new(
srv.take().unwrap(),
conn,
config.take().unwrap(),
None,
peer_addr.clone(),
));
self.poll()
}
}
Ok(Async::NotReady) => Ok(Async::NotReady),
Err(err) => {
trace!("H2 handshake error: {}", err);
Err(err.into())
}
},
}
}
}

View File

@@ -60,6 +60,7 @@ impl Response {
STATIC_RESP!(UnsupportedMediaType, StatusCode::UNSUPPORTED_MEDIA_TYPE);
STATIC_RESP!(RangeNotSatisfiable, StatusCode::RANGE_NOT_SATISFIABLE);
STATIC_RESP!(ExpectationFailed, StatusCode::EXPECTATION_FAILED);
STATIC_RESP!(UnprocessableEntity, StatusCode::UNPROCESSABLE_ENTITY);
STATIC_RESP!(InternalServerError, StatusCode::INTERNAL_SERVER_ERROR);
STATIC_RESP!(NotImplemented, StatusCode::NOT_IMPLEMENTED);

View File

@@ -12,7 +12,6 @@ pub mod body;
mod builder;
pub mod client;
mod config;
#[cfg(any(feature = "flate2-zlib", feature = "flate2-rust", feature = "brotli"))]
pub mod encoding;
mod extensions;
mod header;

View File

@@ -1,4 +1,5 @@
use std::cell::{Ref, RefCell, RefMut};
use std::net;
use std::rc::Rc;
use bitflags::bitflags;
@@ -43,6 +44,7 @@ pub struct RequestHead {
pub version: Version,
pub headers: HeaderMap,
pub extensions: RefCell<Extensions>,
pub peer_addr: Option<net::SocketAddr>,
flags: Flags,
}
@@ -54,6 +56,7 @@ impl Default for RequestHead {
version: Version::HTTP_11,
headers: HeaderMap::with_capacity(16),
flags: Flags::empty(),
peer_addr: None,
extensions: RefCell::new(Extensions::new()),
}
}

View File

@@ -1,5 +1,5 @@
use std::cell::{Ref, RefMut};
use std::fmt;
use std::{fmt, net};
use http::{header, Method, Uri, Version};
@@ -139,6 +139,7 @@ impl<P> Request<P> {
}
/// Check if request requires connection upgrade
#[inline]
pub fn upgrade(&self) -> bool {
if let Some(conn) = self.head().headers.get(header::CONNECTION) {
if let Ok(s) = conn.to_str() {
@@ -147,6 +148,15 @@ impl<P> Request<P> {
}
self.head().method == Method::CONNECT
}
/// Peer socket address
///
/// Peer address is actual socket address, if proxy is used in front of
/// actix http server, then peer address would be address of this proxy.
#[inline]
pub fn peer_addr(&self) -> Option<net::SocketAddr> {
self.head().peer_addr
}
}
impl<P> fmt::Debug for Request<P> {

View File

@@ -1,8 +1,10 @@
use std::marker::PhantomData;
use std::{fmt, io};
use std::{fmt, io, net};
use actix_codec::{AsyncRead, AsyncWrite, Framed};
use actix_server_config::{Io as ServerIo, Protocol, ServerConfig as SrvConfig};
use actix_server_config::{
Io as ServerIo, IoStream, Protocol, ServerConfig as SrvConfig,
};
use actix_service::{IntoNewService, NewService, Service};
use actix_utils::cloneable::CloneableService;
use bytes::{Buf, BufMut, Bytes, BytesMut};
@@ -128,7 +130,7 @@ where
impl<T, P, S, B, X, U> NewService<SrvConfig> for HttpService<T, P, S, B, X, U>
where
T: AsyncRead + AsyncWrite,
T: IoStream,
S: NewService<SrvConfig, Request = Request>,
S::Error: Into<Error>,
S::InitError: fmt::Debug,
@@ -182,7 +184,7 @@ pub struct HttpServiceResponse<
impl<T, P, S, B, X, U> Future for HttpServiceResponse<T, P, S, B, X, U>
where
T: AsyncRead + AsyncWrite,
T: IoStream,
S: NewService<SrvConfig, Request = Request>,
S::Error: Into<Error>,
S::InitError: fmt::Debug,
@@ -268,7 +270,7 @@ where
impl<T, P, S, B, X, U> Service for HttpServiceHandler<T, P, S, B, X, U>
where
T: AsyncRead + AsyncWrite,
T: IoStream,
S: Service<Request = Request>,
S::Error: Into<Error>,
S::Future: 'static,
@@ -317,6 +319,7 @@ where
let (io, _, proto) = req.into_parts();
match proto {
Protocol::Http2 => {
let peer_addr = io.peer_addr();
let io = Io {
inner: io,
unread: None,
@@ -326,6 +329,7 @@ where
server::handshake(io),
self.cfg.clone(),
self.srv.clone(),
peer_addr,
))),
}
}
@@ -357,7 +361,7 @@ where
S: Service<Request = Request>,
S::Future: 'static,
S::Error: Into<Error>,
T: AsyncRead + AsyncWrite,
T: IoStream,
B: MessageBody,
X: Service<Request = Request, Response = Request>,
X::Error: Into<Error>,
@@ -376,12 +380,19 @@ where
Option<CloneableService<U>>,
)>,
),
Handshake(Option<(Handshake<Io<T>, Bytes>, ServiceConfig, CloneableService<S>)>),
Handshake(
Option<(
Handshake<Io<T>, Bytes>,
ServiceConfig,
CloneableService<S>,
Option<net::SocketAddr>,
)>,
),
}
pub struct HttpServiceHandlerResponse<T, S, B, X, U>
where
T: AsyncRead + AsyncWrite,
T: IoStream,
S: Service<Request = Request>,
S::Error: Into<Error>,
S::Future: 'static,
@@ -399,7 +410,7 @@ const HTTP2_PREFACE: [u8; 14] = *b"PRI * HTTP/2.0";
impl<T, S, B, X, U> Future for HttpServiceHandlerResponse<T, S, B, X, U>
where
T: AsyncRead + AsyncWrite,
T: IoStream,
S: Service<Request = Request>,
S::Error: Into<Error>,
S::Future: 'static,
@@ -437,12 +448,17 @@ where
}
let (io, buf, cfg, srv, expect, upgrade) = data.take().unwrap();
if buf[..14] == HTTP2_PREFACE[..] {
let peer_addr = io.peer_addr();
let io = Io {
inner: io,
unread: Some(buf),
};
self.state =
State::Handshake(Some((server::handshake(io), cfg, srv)));
self.state = State::Handshake(Some((
server::handshake(io),
cfg,
srv,
peer_addr,
)));
} else {
self.state = State::H1(h1::Dispatcher::with_timeout(
io,
@@ -470,8 +486,8 @@ where
} else {
panic!()
};
let (_, cfg, srv) = data.take().unwrap();
self.state = State::H2(Dispatcher::new(srv, conn, cfg, None));
let (_, cfg, srv, peer_addr) = data.take().unwrap();
self.state = State::H2(Dispatcher::new(srv, conn, cfg, None, peer_addr));
self.poll()
}
}
@@ -523,3 +539,25 @@ impl<T: AsyncWrite> AsyncWrite for Io<T> {
self.inner.write_buf(buf)
}
}
impl<T: IoStream> IoStream for Io<T> {
#[inline]
fn peer_addr(&self) -> Option<net::SocketAddr> {
self.inner.peer_addr()
}
#[inline]
fn set_nodelay(&mut self, nodelay: bool) -> io::Result<()> {
self.inner.set_nodelay(nodelay)
}
#[inline]
fn set_linger(&mut self, dur: Option<std::time::Duration>) -> io::Result<()> {
self.inner.set_linger(dur)
}
#[inline]
fn set_keepalive(&mut self, dur: Option<std::time::Duration>) -> io::Result<()> {
self.inner.set_keepalive(dur)
}
}

View File

@@ -4,6 +4,7 @@ use std::io;
use std::str::FromStr;
use actix_codec::{AsyncRead, AsyncWrite};
use actix_server_config::IoStream;
use bytes::{Buf, Bytes, BytesMut};
use futures::{Async, Poll};
use http::header::{self, HeaderName, HeaderValue};
@@ -253,3 +254,17 @@ impl AsyncWrite for TestBuffer {
Ok(Async::NotReady)
}
}
impl IoStream for TestBuffer {
fn set_nodelay(&mut self, _nodelay: bool) -> io::Result<()> {
Ok(())
}
fn set_linger(&mut self, _dur: Option<std::time::Duration>) -> io::Result<()> {
Ok(())
}
fn set_keepalive(&mut self, _dur: Option<std::time::Duration>) -> io::Result<()> {
Ok(())
}
}

View File

@@ -35,7 +35,10 @@ fn test_h1() {
.keep_alive(KeepAlive::Disabled)
.client_timeout(1000)
.client_disconnect(1000)
.h1(|_| future::ok::<_, ()>(Response::Ok().finish()))
.h1(|req: Request| {
assert!(req.peer_addr().is_some());
future::ok::<_, ()>(Response::Ok().finish())
})
});
let response = srv.block_on(srv.get("/").send()).unwrap();
@@ -50,6 +53,7 @@ fn test_h1_2() {
.client_timeout(1000)
.client_disconnect(1000)
.finish(|req: Request| {
assert!(req.peer_addr().is_some());
assert_eq!(req.version(), http::Version::HTTP_11);
future::ok::<_, ()>(Response::Ok().finish())
})
@@ -115,6 +119,7 @@ fn test_h2_1() -> std::io::Result<()> {
.and_then(
HttpService::build()
.finish(|req: Request| {
assert!(req.peer_addr().is_some());
assert_eq!(req.version(), http::Version::HTTP_2);
future::ok::<_, Error>(Response::Ok().finish())
})

View File

@@ -18,7 +18,7 @@ name = "actix_web_actors"
path = "src/lib.rs"
[dependencies]
actix = "0.8.0-alpha.3"
actix = "0.8.0"
actix-web = "1.0.0-alpha.5"
actix-http = "0.1.0-alpha.5"
actix-codec = "0.1.2"

View File

@@ -199,7 +199,7 @@ mod tests {
use actix::Actor;
use actix_web::http::StatusCode;
use actix_web::test::{block_on, call_success, init_service, TestRequest};
use actix_web::test::{block_on, call_service, init_service, TestRequest};
use actix_web::{web, App, HttpResponse};
use bytes::{Bytes, BytesMut};
@@ -237,7 +237,7 @@ mod tests {
})));
let req = TestRequest::with_uri("/test").to_request();
let mut resp = call_success(&mut srv, req);
let mut resp = call_service(&mut srv, req);
assert_eq!(resp.status(), StatusCode::OK);
let body = block_on(resp.take_body().fold(

View File

@@ -62,7 +62,7 @@ impl fmt::Display for Args {
pub struct {name};
impl actix_web::dev::HttpServiceFactory for {name} {{
fn register(self, config: &mut actix_web::dev::ServiceConfig) {{
fn register(self, config: &mut actix_web::dev::AppService) {{
{ast}
let resource = actix_web::Resource::new(\"{path}\"){guards}.{to}({name});

View File

@@ -1,5 +1,10 @@
# Changes
## [0.1.0] - 2019-04-16
* No changes
## [0.1.0-alpha.6] - 2019-04-14
### Changed

View File

@@ -1,6 +1,6 @@
[package]
name = "awc"
version = "0.1.0-alpha.6"
version = "0.1.0"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Actix http client."
readme = "README.md"
@@ -38,7 +38,7 @@ flate2-rust = ["actix-http/flate2-rust"]
[dependencies]
actix-codec = "0.1.2"
actix-service = "0.3.6"
actix-http = "0.1.0-alpha.5"
actix-http = "0.1.0"
base64 = "0.10.1"
bytes = "0.4"
derive_more = "0.14"
@@ -56,10 +56,10 @@ openssl = { version="0.10", optional = true }
[dev-dependencies]
actix-rt = "0.2.2"
actix-web = { version = "1.0.0-alpha.6", features=["ssl"] }
actix-http = { version = "0.1.0-alpha.5", features=["ssl"] }
actix-http = { version = "0.1.0", features=["ssl"] }
actix-http-test = { version = "0.1.0-alpha.3", features=["ssl"] }
actix-utils = "0.3.4"
actix-server = { version = "0.4.1", features=["ssl"] }
actix-server = { version = "0.4.3", features=["ssl"] }
brotli2 = { version="0.3.2" }
flate2 = { version="1.0.2" }
env_logger = "0.6"

View File

@@ -1 +1,33 @@
# Actix http client [![Build Status](https://travis-ci.org/actix/actix-web.svg?branch=master)](https://travis-ci.org/actix/actix-web) [![codecov](https://codecov.io/gh/actix/actix-web/branch/master/graph/badge.svg)](https://codecov.io/gh/actix/actix-web) [![crates.io](https://meritbadge.herokuapp.com/awc)](https://crates.io/crates/awc) [![Join the chat at https://gitter.im/actix/actix](https://badges.gitter.im/actix/actix.svg)](https://gitter.im/actix/actix?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
An HTTP Client
## Documentation & community resources
* [User Guide](https://actix.rs/docs/)
* [API Documentation](https://docs.rs/awc/)
* [Chat on gitter](https://gitter.im/actix/actix)
* Cargo package: [awc](https://crates.io/crates/awc)
* Minimum supported Rust version: 1.33 or later
## Example
```rust
use actix_rt::System;
use awc::Client;
use futures::future::{Future, lazy};
fn main() {
System::new("test").block_on(lazy(|| {
let mut client = Client::default();
client.get("http://www.rust-lang.org") // <- Create request builder
.header("User-Agent", "Actix-web")
.send() // <- Send http request
.and_then(|response| { // <- server http response
println!("Response: {:?}", response);
Ok(())
})
}));
}
```

View File

@@ -544,6 +544,8 @@ impl fmt::Debug for ClientRequest {
#[cfg(test)]
mod tests {
use std::time::SystemTime;
use super::*;
use crate::Client;
@@ -555,6 +557,21 @@ mod tests {
assert!(repr.contains("x-test"));
}
#[test]
fn test_basics() {
let mut req = Client::new()
.put("/")
.version(Version::HTTP_2)
.set(header::Date(SystemTime::now().into()))
.content_type("plain/text")
.content_length(100);
assert!(req.headers().contains_key(header::CONTENT_TYPE));
assert!(req.headers().contains_key(header::DATE));
assert_eq!(req.head.version, Version::HTTP_2);
let _ = req.headers_mut();
let _ = req.send_body("");
}
#[test]
fn test_client_header() {
let req = Client::build()

View File

@@ -134,3 +134,23 @@ impl TestResponse {
}
}
}
#[cfg(test)]
mod tests {
use std::time::SystemTime;
use super::*;
use crate::{cookie, http::header};
#[test]
fn test_basics() {
let res = TestResponse::default()
.version(Version::HTTP_2)
.set(header::Date(SystemTime::now().into()))
.cookie(cookie::Cookie::build("name", "value").finish())
.finish();
assert!(res.headers().contains_key(header::SET_COOKIE));
assert!(res.headers().contains_key(header::DATE));
assert_eq!(res.version(), Version::HTTP_2);
}
}

View File

@@ -455,10 +455,20 @@ mod tests {
.max_frame_size(100)
.server_mode()
.protocols(&["v1", "v2"])
.set_header_if_none(header::CONTENT_TYPE, "json")
.set_header_if_none(header::CONTENT_TYPE, "text")
.cookie(Cookie::build("cookie1", "value1").finish());
assert_eq!(req.origin.unwrap().to_str().unwrap(), "test-origin");
assert_eq!(
req.origin.as_ref().unwrap().to_str().unwrap(),
"test-origin"
);
assert_eq!(req.max_size, 100);
assert_eq!(req.server_mode, true);
assert_eq!(req.protocols, Some("v1,v2".to_string()));
assert_eq!(
req.head.headers.get(header::CONTENT_TYPE).unwrap(),
header::HeaderValue::from_static("json")
);
let _ = req.connect();
}
}

View File

@@ -1,8 +1,9 @@
use std::io::Write;
use std::io::{Read, Write};
use std::time::Duration;
use brotli2::write::BrotliEncoder;
use bytes::Bytes;
use flate2::read::GzDecoder;
use flate2::write::GzEncoder;
use flate2::Compression;
use futures::future::Future;
@@ -11,6 +12,7 @@ use rand::Rng;
use actix_http::HttpService;
use actix_http_test::TestServer;
use actix_web::http::Cookie;
use actix_web::middleware::{BodyEncoding, Compress};
use actix_web::{http::header, web, App, Error, HttpMessage, HttpRequest, HttpResponse};
use awc::error::SendRequestError;
@@ -95,11 +97,9 @@ fn test_timeout_override() {
))))
});
let client = srv.execute(|| {
awc::Client::build()
.timeout(Duration::from_millis(50000))
.finish()
});
let client = awc::Client::build()
.timeout(Duration::from_millis(50000))
.finish();
let request = client
.get(srv.url("/"))
.timeout(Duration::from_millis(50))
@@ -110,58 +110,77 @@ fn test_timeout_override() {
}
}
// #[test]
// fn test_connection_close() {
// let mut srv =
// test::TestServer::new(|app| app.handler(|_| HttpResponse::Ok().body(STR)));
#[test]
fn test_connection_close() {
let mut srv = TestServer::new(|| {
HttpService::new(
App::new().service(web::resource("/").to(|| HttpResponse::Ok())),
)
});
// let request = srv.get("/").header("Connection", "close").finish().unwrap();
// let response = srv.execute(request.send()).unwrap();
// assert!(response.status().is_success());
// }
let res = srv
.block_on(awc::Client::new().get(srv.url("/")).force_close().send())
.unwrap();
assert!(res.status().is_success());
}
// #[test]
// fn test_with_query_parameter() {
// let mut srv = test::TestServer::new(|app| {
// app.handler(|req: &HttpRequest| match req.query().get("qp") {
// Some(_) => HttpResponse::Ok().finish(),
// None => HttpResponse::BadRequest().finish(),
// })
// });
#[test]
fn test_with_query_parameter() {
let mut srv = TestServer::new(|| {
HttpService::new(App::new().service(web::resource("/").to(
|req: HttpRequest| {
if req.query_string().contains("qp") {
HttpResponse::Ok()
} else {
HttpResponse::BadRequest()
}
},
)))
});
// let request = srv.get("/").uri(srv.url("/?qp=5").as_str()).finish().unwrap();
let res = srv
.block_on(awc::Client::new().get(srv.url("/?qp=5")).send())
.unwrap();
assert!(res.status().is_success());
}
// let response = srv.execute(request.send()).unwrap();
// assert!(response.status().is_success());
// }
#[test]
fn test_no_decompress() {
let mut srv = TestServer::new(|| {
HttpService::new(App::new().wrap(Compress::default()).service(
web::resource("/").route(web::to(|| {
let mut res = HttpResponse::Ok().body(STR);
res.encoding(header::ContentEncoding::Gzip);
res
})),
))
});
// #[test]
// fn test_no_decompress() {
// let mut srv =
// test::TestServer::new(|app| app.handler(|_| HttpResponse::Ok().body(STR)));
let mut res = srv
.block_on(awc::Client::new().get(srv.url("/")).no_decompress().send())
.unwrap();
assert!(res.status().is_success());
// let request = srv.get("/").disable_decompress().finish().unwrap();
// let response = srv.execute(request.send()).unwrap();
// assert!(response.status().is_success());
// read response
let bytes = srv.block_on(res.body()).unwrap();
// // read response
// let bytes = srv.execute(response.body()).unwrap();
let mut e = GzDecoder::new(&bytes[..]);
let mut dec = Vec::new();
e.read_to_end(&mut dec).unwrap();
assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref()));
// let mut e = GzDecoder::new(&bytes[..]);
// let mut dec = Vec::new();
// e.read_to_end(&mut dec).unwrap();
// assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref()));
// POST
let mut res = srv
.block_on(awc::Client::new().post(srv.url("/")).no_decompress().send())
.unwrap();
assert!(res.status().is_success());
// // POST
// let request = srv.post().disable_decompress().finish().unwrap();
// let response = srv.execute(request.send()).unwrap();
// let bytes = srv.execute(response.body()).unwrap();
// let mut e = GzDecoder::new(&bytes[..]);
// let mut dec = Vec::new();
// e.read_to_end(&mut dec).unwrap();
// assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref()));
// }
let bytes = srv.block_on(res.body()).unwrap();
let mut e = GzDecoder::new(&bytes[..]);
let mut dec = Vec::new();
e.read_to_end(&mut dec).unwrap();
assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref()));
}
#[test]
fn test_client_gzip_encoding() {

View File

@@ -12,7 +12,7 @@ use actix_service::{
use futures::IntoFuture;
use crate::app_service::{AppEntry, AppInit, AppRoutingFactory};
use crate::config::{AppConfig, AppConfigInner, RouterConfig};
use crate::config::{AppConfig, AppConfigInner, ServiceConfig};
use crate::data::{Data, DataFactory};
use crate::dev::ResourceDef;
use crate::error::Error;
@@ -125,7 +125,7 @@ where
/// use actix_web::{web, middleware, App, HttpResponse};
///
/// // this function could be located in different module
/// fn config(cfg: &mut web::RouterConfig) {
/// fn config(cfg: &mut web::ServiceConfig) {
/// cfg.service(web::resource("/test")
/// .route(web::get().to(|| HttpResponse::Ok()))
/// .route(web::head().to(|| HttpResponse::MethodNotAllowed()))
@@ -141,9 +141,9 @@ where
/// ```
pub fn configure<F>(mut self, f: F) -> Self
where
F: Fn(&mut RouterConfig),
F: Fn(&mut ServiceConfig),
{
let mut cfg = RouterConfig::new();
let mut cfg = ServiceConfig::new();
f(&mut cfg);
self.data.extend(cfg.data);
self.services.extend(cfg.services);
@@ -436,7 +436,7 @@ mod tests {
use super::*;
use crate::http::{header, HeaderValue, Method, StatusCode};
use crate::service::{ServiceRequest, ServiceResponse};
use crate::test::{block_on, call_success, init_service, TestRequest};
use crate::test::{block_on, call_service, init_service, TestRequest};
use crate::{web, Error, HttpResponse};
#[test]
@@ -527,7 +527,7 @@ mod tests {
.route("/test", web::get().to(|| HttpResponse::Ok())),
);
let req = TestRequest::with_uri("/test").to_request();
let resp = call_success(&mut srv, req);
let resp = call_service(&mut srv, req);
assert_eq!(resp.status(), StatusCode::OK);
assert_eq!(
resp.headers().get(header::CONTENT_TYPE).unwrap(),
@@ -543,7 +543,7 @@ mod tests {
.wrap(md),
);
let req = TestRequest::with_uri("/test").to_request();
let resp = call_success(&mut srv, req);
let resp = call_service(&mut srv, req);
assert_eq!(resp.status(), StatusCode::OK);
assert_eq!(
resp.headers().get(header::CONTENT_TYPE).unwrap(),
@@ -567,7 +567,7 @@ mod tests {
.service(web::resource("/test").to(|| HttpResponse::Ok())),
);
let req = TestRequest::with_uri("/test").to_request();
let resp = call_success(&mut srv, req);
let resp = call_service(&mut srv, req);
assert_eq!(resp.status(), StatusCode::OK);
assert_eq!(
resp.headers().get(header::CONTENT_TYPE).unwrap(),
@@ -591,7 +591,7 @@ mod tests {
}),
);
let req = TestRequest::with_uri("/test").to_request();
let resp = call_success(&mut srv, req);
let resp = call_service(&mut srv, req);
assert_eq!(resp.status(), StatusCode::OK);
assert_eq!(
resp.headers().get(header::CONTENT_TYPE).unwrap(),

View File

@@ -10,7 +10,7 @@ use actix_service::{fn_service, NewService, Service};
use futures::future::{ok, Either, FutureResult};
use futures::{Async, Future, Poll};
use crate::config::{AppConfig, ServiceConfig};
use crate::config::{AppConfig, AppService};
use crate::data::{DataFactory, DataFactoryResult};
use crate::error::Error;
use crate::guard::Guard;
@@ -77,8 +77,7 @@ where
loc_cfg.addr = cfg.local_addr();
}
let mut config =
ServiceConfig::new(self.config.borrow().clone(), default.clone());
let mut config = AppService::new(self.config.borrow().clone(), default.clone());
// register services
std::mem::replace(&mut *self.services.borrow_mut(), Vec::new())

View File

@@ -23,7 +23,7 @@ type HttpNewService =
boxed::BoxedNewService<(), ServiceRequest, ServiceResponse, Error, ()>;
/// Application configuration
pub struct ServiceConfig {
pub struct AppService {
config: AppConfig,
root: bool,
default: Rc<HttpNewService>,
@@ -35,10 +35,10 @@ pub struct ServiceConfig {
)>,
}
impl ServiceConfig {
impl AppService {
/// Crate server settings instance
pub(crate) fn new(config: AppConfig, default: Rc<HttpNewService>) -> Self {
ServiceConfig {
AppService {
config,
default,
root: true,
@@ -63,7 +63,7 @@ impl ServiceConfig {
}
pub(crate) fn clone_config(&self) -> Self {
ServiceConfig {
AppService {
config: self.config.clone(),
default: self.default.clone(),
services: Vec::new(),
@@ -165,17 +165,17 @@ impl Default for AppConfigInner {
}
}
/// Router config. It is used for external configuration.
/// Service config is used for external configuration.
/// Part of application configuration could be offloaded
/// to set of external methods. This could help with
/// modularization of big application configuration.
pub struct RouterConfig {
pub struct ServiceConfig {
pub(crate) services: Vec<Box<ServiceFactory>>,
pub(crate) data: Vec<Box<DataFactory>>,
pub(crate) external: Vec<ResourceDef>,
}
impl RouterConfig {
impl ServiceConfig {
pub(crate) fn new() -> Self {
Self {
services: Vec::new(),
@@ -255,13 +255,13 @@ mod tests {
use actix_service::Service;
use super::*;
use crate::http::StatusCode;
use crate::test::{block_on, init_service, TestRequest};
use crate::http::{Method, StatusCode};
use crate::test::{block_on, call_service, init_service, TestRequest};
use crate::{web, App, HttpResponse};
#[test]
fn test_data() {
let cfg = |cfg: &mut RouterConfig| {
let cfg = |cfg: &mut ServiceConfig| {
cfg.data(10usize);
};
@@ -276,7 +276,7 @@ mod tests {
#[test]
fn test_data_factory() {
let cfg = |cfg: &mut RouterConfig| {
let cfg = |cfg: &mut ServiceConfig| {
cfg.data_factory(|| Ok::<_, ()>(10usize));
};
@@ -288,7 +288,7 @@ mod tests {
let resp = block_on(srv.call(req)).unwrap();
assert_eq!(resp.status(), StatusCode::OK);
let cfg2 = |cfg: &mut RouterConfig| {
let cfg2 = |cfg: &mut ServiceConfig| {
cfg.data_factory(|| Ok::<_, ()>(10u32));
};
let mut srv = init_service(
@@ -300,4 +300,26 @@ mod tests {
let resp = block_on(srv.call(req)).unwrap();
assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
}
#[test]
fn test_service() {
let mut srv = init_service(App::new().configure(|cfg| {
cfg.service(
web::resource("/test").route(web::get().to(|| HttpResponse::Created())),
)
.route("/index.html", web::get().to(|| HttpResponse::Ok()));
}));
let req = TestRequest::with_uri("/test")
.method(Method::GET)
.to_request();
let resp = call_service(&mut srv, req);
assert_eq!(resp.status(), StatusCode::CREATED);
let req = TestRequest::with_uri("/index.html")
.method(Method::GET)
.to_request();
let resp = call_service(&mut srv, req);
assert_eq!(resp.status(), StatusCode::OK);
}
}

View File

@@ -61,6 +61,7 @@ pub(crate) trait DataFactoryResult {
/// web::get().to(index)));
/// }
/// ```
#[derive(Debug)]
pub struct Data<T>(Arc<T>);
impl<T> Data<T> {

View File

@@ -30,7 +30,7 @@ impl ConnectionInfo {
let mut host = None;
let mut scheme = None;
let mut remote = None;
let peer = None;
let mut peer = None;
// load forwarded header
for hdr in req.headers.get_all(&header::FORWARDED) {
@@ -116,10 +116,10 @@ impl ConnectionInfo {
remote = h.split(',').next().map(|v| v.trim());
}
}
// if remote.is_none() {
// get peeraddr from socketaddr
// peer = req.peer_addr().map(|addr| format!("{}", addr));
// }
if remote.is_none() {
// get peeraddr from socketaddr
peer = req.peer_addr.map(|addr| format!("{}", addr));
}
}
ConnectionInfo {

View File

@@ -133,7 +133,7 @@ pub mod dev {
//! use actix_web::dev::*;
//! ```
pub use crate::config::{AppConfig, ServiceConfig};
pub use crate::config::{AppConfig, AppService};
pub use crate::info::ConnectionInfo;
pub use crate::rmap::ResourceMap;
pub use crate::service::{HttpServiceFactory, ServiceRequest, ServiceResponse};

View File

@@ -848,7 +848,18 @@ mod tests {
let req = TestRequest::with_header("Origin", "https://www.example.com")
.to_srv_request();
let resp = test::call_success(&mut cors, req);
let resp = test::call_service(&mut cors, req);
assert_eq!(resp.status(), StatusCode::OK);
}
#[test]
fn default() {
let mut cors =
block_on(Cors::default().new_transform(test::ok_service())).unwrap();
let req = TestRequest::with_header("Origin", "https://www.example.com")
.to_srv_request();
let resp = test::call_service(&mut cors, req);
assert_eq!(resp.status(), StatusCode::OK);
}
@@ -868,7 +879,7 @@ mod tests {
assert!(cors.inner.validate_allowed_method(req.head()).is_err());
assert!(cors.inner.validate_allowed_headers(req.head()).is_err());
let resp = test::call_success(&mut cors, req);
let resp = test::call_service(&mut cors, req);
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
let req = TestRequest::with_header("Origin", "https://www.example.com")
@@ -888,7 +899,7 @@ mod tests {
.method(Method::OPTIONS)
.to_srv_request();
let resp = test::call_success(&mut cors, req);
let resp = test::call_service(&mut cors, req);
assert_eq!(
&b"*"[..],
resp.headers()
@@ -934,7 +945,7 @@ mod tests {
.method(Method::OPTIONS)
.to_srv_request();
let resp = test::call_success(&mut cors, req);
let resp = test::call_service(&mut cors, req);
assert_eq!(resp.status(), StatusCode::OK);
}
@@ -973,7 +984,7 @@ mod tests {
.method(Method::GET)
.to_srv_request();
let resp = test::call_success(&mut cors, req);
let resp = test::call_service(&mut cors, req);
assert_eq!(resp.status(), StatusCode::OK);
}
@@ -982,7 +993,7 @@ mod tests {
let mut cors = Cors::new().disable_preflight().finish(test::ok_service());
let req = TestRequest::default().method(Method::GET).to_srv_request();
let resp = test::call_success(&mut cors, req);
let resp = test::call_service(&mut cors, req);
assert!(resp
.headers()
.get(header::ACCESS_CONTROL_ALLOW_ORIGIN)
@@ -991,7 +1002,7 @@ mod tests {
let req = TestRequest::with_header("Origin", "https://www.example.com")
.method(Method::OPTIONS)
.to_srv_request();
let resp = test::call_success(&mut cors, req);
let resp = test::call_service(&mut cors, req);
assert_eq!(
&b"https://www.example.com"[..],
resp.headers()
@@ -1018,7 +1029,7 @@ mod tests {
.method(Method::OPTIONS)
.to_srv_request();
let resp = test::call_success(&mut cors, req);
let resp = test::call_service(&mut cors, req);
assert_eq!(
&b"*"[..],
resp.headers()
@@ -1064,7 +1075,7 @@ mod tests {
let req = TestRequest::with_header("Origin", "https://www.example.com")
.method(Method::OPTIONS)
.to_srv_request();
let resp = test::call_success(&mut cors, req);
let resp = test::call_service(&mut cors, req);
assert_eq!(
&b"Accept, Origin"[..],
resp.headers().get(header::VARY).unwrap().as_bytes()
@@ -1080,7 +1091,7 @@ mod tests {
.method(Method::OPTIONS)
.header(header::ACCESS_CONTROL_REQUEST_METHOD, "POST")
.to_srv_request();
let resp = test::call_success(&mut cors, req);
let resp = test::call_service(&mut cors, req);
let origins_str = resp
.headers()
@@ -1104,7 +1115,7 @@ mod tests {
.method(Method::GET)
.to_srv_request();
let resp = test::call_success(&mut cors, req);
let resp = test::call_service(&mut cors, req);
assert_eq!(
&b"https://example.com"[..],
resp.headers()
@@ -1117,7 +1128,7 @@ mod tests {
.method(Method::GET)
.to_srv_request();
let resp = test::call_success(&mut cors, req);
let resp = test::call_service(&mut cors, req);
assert_eq!(
&b"https://example.org"[..],
resp.headers()
@@ -1140,7 +1151,7 @@ mod tests {
.method(Method::OPTIONS)
.to_srv_request();
let resp = test::call_success(&mut cors, req);
let resp = test::call_service(&mut cors, req);
assert_eq!(
&b"https://example.com"[..],
resp.headers()
@@ -1154,7 +1165,7 @@ mod tests {
.method(Method::OPTIONS)
.to_srv_request();
let resp = test::call_success(&mut cors, req);
let resp = test::call_service(&mut cors, req);
assert_eq!(
&b"https://example.org"[..],
resp.headers()

View File

@@ -172,7 +172,7 @@ mod tests {
)
.unwrap();
let resp = test::call_success(&mut mw, TestRequest::default().to_srv_request());
let resp = test::call_service(&mut mw, TestRequest::default().to_srv_request());
assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), "0001");
}
@@ -198,7 +198,7 @@ mod tests {
)
.unwrap();
let resp = test::call_success(&mut mw, TestRequest::default().to_srv_request());
let resp = test::call_service(&mut mw, TestRequest::default().to_srv_request());
assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), "0001");
}
}

View File

@@ -500,15 +500,15 @@ mod tests {
})),
);
let resp =
test::call_success(&mut srv, TestRequest::with_uri("/index").to_request());
test::call_service(&mut srv, TestRequest::with_uri("/index").to_request());
assert_eq!(resp.status(), StatusCode::OK);
let resp =
test::call_success(&mut srv, TestRequest::with_uri("/login").to_request());
test::call_service(&mut srv, TestRequest::with_uri("/login").to_request());
assert_eq!(resp.status(), StatusCode::OK);
let c = resp.response().cookies().next().unwrap().to_owned();
let resp = test::call_success(
let resp = test::call_service(
&mut srv,
TestRequest::with_uri("/index")
.cookie(c.clone())
@@ -516,7 +516,7 @@ mod tests {
);
assert_eq!(resp.status(), StatusCode::CREATED);
let resp = test::call_success(
let resp = test::call_service(
&mut srv,
TestRequest::with_uri("/logout")
.cookie(c.clone())

View File

@@ -363,13 +363,6 @@ impl FormatText {
let rt = (rt.num_nanoseconds().unwrap_or(0) as f64) / 1_000_000.0;
fmt.write_fmt(format_args!("{:.6}", rt))
}
// FormatText::RemoteAddr => {
// if let Some(remote) = req.connection_info().remote() {
// return remote.fmt(fmt);
// } else {
// "-".fmt(fmt)
// }
// }
FormatText::EnvironHeader(ref name) => {
if let Ok(val) = env::var(name) {
fmt.write_fmt(format_args!("{}", val))
@@ -441,6 +434,14 @@ impl FormatText {
};
*self = FormatText::Str(s.to_string());
}
FormatText::RemoteAddr => {
let s = if let Some(remote) = req.connection_info().remote() {
FormatText::Str(remote.to_string())
} else {
FormatText::Str("-".to_string())
};
*self = s;
}
_ => (),
}
}

View File

@@ -1,6 +1,6 @@
use std::cell::{Ref, RefCell, RefMut};
use std::fmt;
use std::rc::Rc;
use std::{fmt, net};
use actix_http::http::{HeaderMap, Method, Uri, Version};
use actix_http::{Error, Extensions, HttpMessage, Message, Payload, RequestHead};
@@ -170,6 +170,17 @@ impl HttpRequest {
self.url_for(name, &NO_PARAMS)
}
/// Peer socket address
///
/// Peer address is actual socket address, if proxy is used in front of
/// actix http server, then peer address would be address of this proxy.
///
/// To get client connection information `.connection_info()` should be used.
#[inline]
pub fn peer_addr(&self) -> Option<net::SocketAddr> {
self.head().peer_addr
}
/// Get *ConnectionInfo* for the current request.
#[inline]
pub fn connection_info(&self) -> Ref<ConnectionInfo> {
@@ -324,7 +335,7 @@ mod tests {
use super::*;
use crate::dev::{ResourceDef, ResourceMap};
use crate::http::{header, StatusCode};
use crate::test::{call_success, init_service, TestRequest};
use crate::test::{call_service, init_service, TestRequest};
use crate::{web, App, HttpResponse};
#[test]
@@ -453,7 +464,7 @@ mod tests {
));
let req = TestRequest::default().to_request();
let resp = call_success(&mut srv, req);
let resp = call_service(&mut srv, req);
assert_eq!(resp.status(), StatusCode::OK);
let mut srv = init_service(App::new().data(10u32).service(
@@ -467,7 +478,7 @@ mod tests {
));
let req = TestRequest::default().to_request();
let resp = call_success(&mut srv, req);
let resp = call_service(&mut srv, req);
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
}
}

View File

@@ -10,7 +10,7 @@ use actix_service::{
use futures::future::{ok, Either, FutureResult};
use futures::{Async, Future, IntoFuture, Poll};
use crate::dev::{insert_slash, HttpServiceFactory, ResourceDef, ServiceConfig};
use crate::dev::{insert_slash, AppService, HttpServiceFactory, ResourceDef};
use crate::extract::FromRequest;
use crate::guard::Guard;
use crate::handler::{AsyncFactory, Factory};
@@ -347,7 +347,7 @@ where
InitError = (),
> + 'static,
{
fn register(mut self, config: &mut ServiceConfig) {
fn register(mut self, config: &mut AppService) {
let guards = if self.guards.is_empty() {
None
} else {
@@ -545,7 +545,7 @@ mod tests {
use crate::http::{header, HeaderValue, Method, StatusCode};
use crate::service::{ServiceRequest, ServiceResponse};
use crate::test::{call_success, init_service, TestRequest};
use crate::test::{call_service, init_service, TestRequest};
use crate::{web, App, Error, HttpResponse};
fn md<S, B>(
@@ -577,7 +577,7 @@ mod tests {
),
);
let req = TestRequest::with_uri("/test").to_request();
let resp = call_success(&mut srv, req);
let resp = call_service(&mut srv, req);
assert_eq!(resp.status(), StatusCode::OK);
assert_eq!(
resp.headers().get(header::CONTENT_TYPE).unwrap(),
@@ -603,7 +603,7 @@ mod tests {
),
);
let req = TestRequest::with_uri("/test").to_request();
let resp = call_success(&mut srv, req);
let resp = call_service(&mut srv, req);
assert_eq!(resp.status(), StatusCode::OK);
assert_eq!(
resp.headers().get(header::CONTENT_TYPE).unwrap(),
@@ -618,7 +618,7 @@ mod tests {
sleep(Duration::from_millis(100)).then(|_| HttpResponse::Ok())
})));
let req = TestRequest::with_uri("/test").to_request();
let resp = call_success(&mut srv, req);
let resp = call_service(&mut srv, req);
assert_eq!(resp.status(), StatusCode::OK);
}
@@ -634,13 +634,13 @@ mod tests {
}),
);
let req = TestRequest::with_uri("/test").to_request();
let resp = call_success(&mut srv, req);
let resp = call_service(&mut srv, req);
assert_eq!(resp.status(), StatusCode::OK);
let req = TestRequest::with_uri("/test")
.method(Method::POST)
.to_request();
let resp = call_success(&mut srv, req);
let resp = call_service(&mut srv, req);
assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED);
let mut srv = init_service(
@@ -654,13 +654,13 @@ mod tests {
);
let req = TestRequest::with_uri("/test").to_request();
let resp = call_success(&mut srv, req);
let resp = call_service(&mut srv, req);
assert_eq!(resp.status(), StatusCode::OK);
let req = TestRequest::with_uri("/test")
.method(Method::POST)
.to_request();
let resp = call_success(&mut srv, req);
let resp = call_service(&mut srv, req);
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
}
}

View File

@@ -422,7 +422,7 @@ mod tests {
use tokio_timer::sleep;
use crate::http::{Method, StatusCode};
use crate::test::{call_success, init_service, TestRequest};
use crate::test::{call_service, init_service, TestRequest};
use crate::{error, web, App, HttpResponse};
#[test]
@@ -450,31 +450,31 @@ mod tests {
let req = TestRequest::with_uri("/test")
.method(Method::GET)
.to_request();
let resp = call_success(&mut srv, req);
let resp = call_service(&mut srv, req);
assert_eq!(resp.status(), StatusCode::OK);
let req = TestRequest::with_uri("/test")
.method(Method::POST)
.to_request();
let resp = call_success(&mut srv, req);
let resp = call_service(&mut srv, req);
assert_eq!(resp.status(), StatusCode::CREATED);
let req = TestRequest::with_uri("/test")
.method(Method::PUT)
.to_request();
let resp = call_success(&mut srv, req);
let resp = call_service(&mut srv, req);
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
let req = TestRequest::with_uri("/test")
.method(Method::DELETE)
.to_request();
let resp = call_success(&mut srv, req);
let resp = call_service(&mut srv, req);
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
let req = TestRequest::with_uri("/test")
.method(Method::HEAD)
.to_request();
let resp = call_success(&mut srv, req);
let resp = call_service(&mut srv, req);
assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED);
}
}

View File

@@ -11,7 +11,7 @@ use actix_service::{
use futures::future::{ok, Either, Future, FutureResult};
use futures::{Async, IntoFuture, Poll};
use crate::dev::{HttpServiceFactory, ServiceConfig};
use crate::dev::{AppService, HttpServiceFactory};
use crate::error::Error;
use crate::guard::Guard;
use crate::resource::Resource;
@@ -303,7 +303,7 @@ where
InitError = (),
> + 'static,
{
fn register(self, config: &mut ServiceConfig) {
fn register(self, config: &mut AppService) {
// update default resource if needed
if self.default.borrow().is_none() {
*self.default.borrow_mut() = Some(config.default_service());
@@ -535,7 +535,7 @@ mod tests {
use crate::dev::{Body, ResponseBody};
use crate::http::{header, HeaderValue, Method, StatusCode};
use crate::service::{ServiceRequest, ServiceResponse};
use crate::test::{block_on, call_success, init_service, TestRequest};
use crate::test::{block_on, call_service, init_service, TestRequest};
use crate::{guard, web, App, Error, HttpRequest, HttpResponse};
#[test]
@@ -912,7 +912,7 @@ mod tests {
web::resource("/test").route(web::get().to(|| HttpResponse::Ok())),
)));
let req = TestRequest::with_uri("/app/test").to_request();
let resp = call_success(&mut srv, req);
let resp = call_service(&mut srv, req);
assert_eq!(resp.status(), StatusCode::OK);
assert_eq!(
resp.headers().get(header::CONTENT_TYPE).unwrap(),
@@ -938,7 +938,7 @@ mod tests {
),
);
let req = TestRequest::with_uri("/app/test").to_request();
let resp = call_success(&mut srv, req);
let resp = call_service(&mut srv, req);
assert_eq!(resp.status(), StatusCode::OK);
assert_eq!(
resp.headers().get(header::CONTENT_TYPE).unwrap(),

View File

@@ -1,5 +1,5 @@
use std::cell::{Ref, RefMut};
use std::fmt;
use std::{fmt, net};
use actix_http::body::{Body, MessageBody, ResponseBody};
use actix_http::http::{HeaderMap, Method, StatusCode, Uri, Version};
@@ -10,16 +10,17 @@ use actix_http::{
use actix_router::{Path, Resource, Url};
use futures::future::{ok, FutureResult, IntoFuture};
use crate::config::{AppConfig, ServiceConfig};
use crate::config::{AppConfig, AppService};
use crate::data::Data;
use crate::info::ConnectionInfo;
use crate::request::HttpRequest;
pub trait HttpServiceFactory {
fn register(self, config: &mut ServiceConfig);
fn register(self, config: &mut AppService);
}
pub(crate) trait ServiceFactory {
fn register(&mut self, config: &mut ServiceConfig);
fn register(&mut self, config: &mut AppService);
}
pub(crate) struct ServiceFactoryWrapper<T> {
@@ -38,7 +39,7 @@ impl<T> ServiceFactory for ServiceFactoryWrapper<T>
where
T: HttpServiceFactory,
{
fn register(&mut self, config: &mut ServiceConfig) {
fn register(&mut self, config: &mut AppService) {
if let Some(item) = self.factory.take() {
item.register(config)
}
@@ -134,6 +135,23 @@ impl ServiceRequest {
}
}
/// Peer socket address
///
/// Peer address is actual socket address, if proxy is used in front of
/// actix http server, then peer address would be address of this proxy.
///
/// To get client connection information `ConnectionInfo` should be used.
#[inline]
pub fn peer_addr(&self) -> Option<net::SocketAddr> {
self.head().peer_addr
}
/// Get *ConnectionInfo* for the current request.
#[inline]
pub fn connection_info(&self) -> Ref<ConnectionInfo> {
ConnectionInfo::get(self.head(), &*self.app_config())
}
/// Get a reference to the Path parameters.
///
/// Params is a container for url parameters.

View File

@@ -4,21 +4,26 @@ use std::rc::Rc;
use actix_http::cookie::Cookie;
use actix_http::http::header::{Header, HeaderName, IntoHeaderValue};
use actix_http::http::{HttpTryFrom, Method, StatusCode, Version};
use actix_http::http::{HttpTryFrom, Method, StatusCode, Uri, Version};
use actix_http::test::TestRequest as HttpTestRequest;
use actix_http::{Extensions, Request};
use actix_router::{Path, ResourceDef, Url};
use actix_rt::Runtime;
use actix_server_config::ServerConfig;
use actix_service::{FnService, IntoNewService, NewService, Service};
use bytes::Bytes;
use futures::future::{lazy, Future};
use bytes::{Bytes, BytesMut};
use futures::{
future::{lazy, ok, Future},
stream::Stream,
};
use serde::de::DeserializeOwned;
use serde_json;
pub use actix_http::test::TestBuffer;
use crate::config::{AppConfig, AppConfigInner};
use crate::data::RouteData;
use crate::dev::{Body, Payload};
use crate::data::{Data, RouteData};
use crate::dev::{Body, MessageBody, Payload};
use crate::request::HttpRequestPool;
use crate::rmap::ResourceMap;
use crate::service::{ServiceRequest, ServiceResponse};
@@ -79,11 +84,12 @@ pub fn default_service(
/// This method accepts application builder instance, and constructs
/// service.
///
/// ```rust,ignore
/// ```rust
/// use actix_service::Service;
/// use actix_web::{test, web, App, HttpResponse, http::StatusCode};
///
/// fn main() {
/// #[test]
/// fn test_init_service() {
/// let mut app = test::init_service(
/// App::new()
/// .service(web::resource("/test").to(|| HttpResponse::Ok()))
@@ -116,11 +122,12 @@ where
/// Calls service and waits for response future completion.
///
/// ```rust,ignore
/// ```rust
/// use actix_web::{test, App, HttpResponse, http::StatusCode};
/// use actix_service::Service;
///
/// fn main() {
/// #[test]
/// fn test_response() {
/// let mut app = test::init_service(
/// App::new()
/// .service(web::resource("/test").to(|| HttpResponse::Ok()))
@@ -130,11 +137,11 @@ where
/// let req = test::TestRequest::with_uri("/test").to_request();
///
/// // Call application
/// let resp = test::call_success(&mut app, req);
/// let resp = test::call_service(&mut app, req);
/// assert_eq!(resp.status(), StatusCode::OK);
/// }
/// ```
pub fn call_success<S, R, B, E>(app: &mut S, req: R) -> S::Response
pub fn call_service<S, R, B, E>(app: &mut S, req: R) -> S::Response
where
S: Service<Request = R, Response = ServiceResponse<B>, Error = E>,
E: std::fmt::Debug,
@@ -142,6 +149,101 @@ where
block_on(app.call(req)).unwrap()
}
/// Helper function that returns a response body of a TestRequest
/// This function blocks the current thread until futures complete.
///
/// ```rust
/// use actix_web::{test, web, App, HttpResponse, http::header};
/// use bytes::Bytes;
///
/// #[test]
/// fn test_index() {
/// let mut app = test::init_service(
/// App::new().service(
/// web::resource("/index.html")
/// .route(web::post().to(
/// || HttpResponse::Ok().body("welcome!")))));
///
/// let req = test::TestRequest::post()
/// .uri("/index.html")
/// .header(header::CONTENT_TYPE, "application/json")
/// .to_request();
///
/// let result = test::read_response(&mut app, req);
/// assert_eq!(result, Bytes::from_static(b"welcome!"));
/// }
/// ```
pub fn read_response<S, B>(app: &mut S, req: Request) -> Bytes
where
S: Service<Request = Request, Response = ServiceResponse<B>, Error = Error>,
B: MessageBody,
{
block_on(app.call(req).and_then(|mut resp: ServiceResponse<B>| {
resp.take_body()
.fold(BytesMut::new(), move |mut body, chunk| {
body.extend_from_slice(&chunk);
Ok::<_, Error>(body)
})
.map(|body: BytesMut| body.freeze())
}))
.unwrap_or_else(|_| panic!("read_response failed at block_on unwrap"))
}
/// Helper function that returns a deserialized response body of a TestRequest
/// This function blocks the current thread until futures complete.
///
/// ```rust
/// use actix_web::{App, test, web, HttpResponse, http::header};
/// use serde::{Serialize, Deserialize};
///
/// #[derive(Serialize, Deserialize)]
/// pub struct Person {
/// id: String,
/// name: String
/// }
///
/// #[test]
/// fn test_add_person() {
/// let mut app = test::init_service(
/// App::new().service(
/// web::resource("/people")
/// .route(web::post().to(|person: web::Json<Person>| {
/// HttpResponse::Ok()
/// .json(person.into_inner())})
/// )));
///
/// let payload = r#"{"id":"12345","name":"User name"}"#.as_bytes();
///
/// let req = test::TestRequest::post()
/// .uri("/people")
/// .header(header::CONTENT_TYPE, "application/json")
/// .set_payload(payload)
/// .to_request();
///
/// let result: Person = test::read_response_json(&mut app, req);
/// }
/// ```
pub fn read_response_json<S, B, T>(app: &mut S, req: Request) -> T
where
S: Service<Request = Request, Response = ServiceResponse<B>, Error = Error>,
B: MessageBody,
T: DeserializeOwned,
{
block_on(app.call(req).and_then(|mut resp: ServiceResponse<B>| {
resp.take_body()
.fold(BytesMut::new(), move |mut body, chunk| {
body.extend_from_slice(&chunk);
Ok::<_, Error>(body)
})
.and_then(|body: BytesMut| {
ok(serde_json::from_slice(&body).unwrap_or_else(|_| {
panic!("read_response_json failed during deserialization")
}))
})
}))
.unwrap_or_else(|_| panic!("read_response_json failed at block_on unwrap"))
}
/// Test `Request` builder.
///
/// For unit testing, actix provides a request builder type and a simple handler runner. TestRequest implements a builder-like pattern.
@@ -151,7 +253,7 @@ where
/// * `TestRequest::to_from` creates `ServiceFromRequest` instance, which is used for testing extractors.
/// * `TestRequest::to_http_request` creates `HttpRequest` instance, which is used for testing handlers.
///
/// ```rust,ignore
/// ```rust
/// # use futures::IntoFuture;
/// use actix_web::{test, HttpRequest, HttpResponse, HttpMessage};
/// use actix_web::http::{header, StatusCode};
@@ -164,7 +266,8 @@ where
/// }
/// }
///
/// fn main() {
/// #[test]
/// fn test_index() {
/// let req = test::TestRequest::with_header("content-type", "text/plain")
/// .to_http_request();
///
@@ -181,6 +284,7 @@ pub struct TestRequest {
rmap: ResourceMap,
config: AppConfigInner,
route_data: Extensions,
path: Path<Url>,
}
impl Default for TestRequest {
@@ -190,6 +294,7 @@ impl Default for TestRequest {
rmap: ResourceMap::new(ResourceDef::new("")),
config: AppConfigInner::default(),
route_data: Extensions::new(),
path: Path::new(Url::new(Uri::default())),
}
}
}
@@ -265,6 +370,12 @@ impl TestRequest {
self
}
/// Set request path pattern parameter
pub fn param(mut self, name: &'static str, value: &'static str) -> Self {
self.path.add_static(name, value);
self
}
/// Set request payload
pub fn set_payload<B: Into<Bytes>>(mut self, data: B) -> Self {
self.req.set_payload(data);
@@ -274,7 +385,7 @@ impl TestRequest {
/// Set application data. This is equivalent of `App::data()` method
/// for testing purpose.
pub fn app_data<T: 'static>(self, data: T) -> Self {
self.config.extensions.borrow_mut().insert(data);
self.config.extensions.borrow_mut().insert(Data::new(data));
self
}
@@ -300,9 +411,10 @@ impl TestRequest {
/// Complete request creation and generate `ServiceRequest` instance
pub fn to_srv_request(mut self) -> ServiceRequest {
let (head, payload) = self.req.finish().into_parts();
self.path.get_mut().update(&head.uri);
let req = HttpRequest::new(
Path::new(Url::new(head.uri.clone())),
self.path,
head,
Rc::new(self.rmap),
AppConfig::new(self.config),
@@ -320,9 +432,10 @@ impl TestRequest {
/// Complete request creation and generate `HttpRequest` instance
pub fn to_http_request(mut self) -> HttpRequest {
let (head, _) = self.req.finish().into_parts();
self.path.get_mut().update(&head.uri);
let mut req = HttpRequest::new(
Path::new(Url::new(head.uri.clone())),
self.path,
head,
Rc::new(self.rmap),
AppConfig::new(self.config),
@@ -335,9 +448,10 @@ impl TestRequest {
/// Complete request creation and generate `HttpRequest` and `Payload` instances
pub fn to_http_parts(mut self) -> (HttpRequest, Payload) {
let (head, payload) = self.req.finish().into_parts();
self.path.get_mut().update(&head.uri);
let mut req = HttpRequest::new(
Path::new(Url::new(head.uri.clone())),
self.path,
head,
Rc::new(self.rmap),
AppConfig::new(self.config),
@@ -364,3 +478,73 @@ impl TestRequest {
block_on(f)
}
}
#[cfg(test)]
mod tests {
use serde::{Deserialize, Serialize};
use std::time::SystemTime;
use super::*;
use crate::{http::header, web, App, HttpResponse};
#[test]
fn test_basics() {
let req = TestRequest::with_hdr(header::ContentType::json())
.version(Version::HTTP_2)
.set(header::Date(SystemTime::now().into()))
.param("test", "123")
.app_data(10u32)
.to_http_request();
assert!(req.headers().contains_key(header::CONTENT_TYPE));
assert!(req.headers().contains_key(header::DATE));
assert_eq!(&req.match_info()["test"], "123");
assert_eq!(req.version(), Version::HTTP_2);
let data = req.app_data::<u32>().unwrap();
assert_eq!(*data, 10);
assert_eq!(*data.get_ref(), 10);
}
#[test]
fn test_response() {
let mut app = init_service(
App::new().service(
web::resource("/index.html")
.route(web::post().to(|| HttpResponse::Ok().body("welcome!"))),
),
);
let req = TestRequest::post()
.uri("/index.html")
.header(header::CONTENT_TYPE, "application/json")
.to_request();
let result = read_response(&mut app, req);
assert_eq!(result, Bytes::from_static(b"welcome!"));
}
#[derive(Serialize, Deserialize)]
pub struct Person {
id: String,
name: String,
}
#[test]
fn test_response_json() {
let mut app = init_service(App::new().service(web::resource("/people").route(
web::post().to(|person: web::Json<Person>| {
HttpResponse::Ok().json(person.into_inner())
}),
)));
let payload = r#"{"id":"12345","name":"User name"}"#.as_bytes();
let req = TestRequest::post()
.uri("/people")
.header(header::CONTENT_TYPE, "application/json")
.set_payload(payload)
.to_request();
let result: Person = read_response_json(&mut app, req);
assert_eq!(&result.id, "12345");
}
}

View File

@@ -13,7 +13,7 @@ use crate::responder::Responder;
use crate::route::Route;
use crate::scope::Scope;
pub use crate::config::RouterConfig;
pub use crate::config::ServiceConfig;
pub use crate::data::{Data, RouteData};
pub use crate::request::HttpRequest;
pub use crate::types::*;
@@ -103,7 +103,7 @@ pub fn route() -> Route {
/// * /{project_id}
///
pub fn get() -> Route {
Route::new().method(Method::GET)
method(Method::GET)
}
/// Create *route* with `POST` method guard.
@@ -123,7 +123,7 @@ pub fn get() -> Route {
/// * /{project_id}
///
pub fn post() -> Route {
Route::new().method(Method::POST)
method(Method::POST)
}
/// Create *route* with `PUT` method guard.
@@ -143,7 +143,7 @@ pub fn post() -> Route {
/// * /{project_id}
///
pub fn put() -> Route {
Route::new().method(Method::PUT)
method(Method::PUT)
}
/// Create *route* with `PATCH` method guard.
@@ -163,7 +163,7 @@ pub fn put() -> Route {
/// * /{project_id}
///
pub fn patch() -> Route {
Route::new().method(Method::PATCH)
method(Method::PATCH)
}
/// Create *route* with `DELETE` method guard.
@@ -183,7 +183,7 @@ pub fn patch() -> Route {
/// * /{project_id}
///
pub fn delete() -> Route {
Route::new().method(Method::DELETE)
method(Method::DELETE)
}
/// Create *route* with `HEAD` method guard.
@@ -203,7 +203,7 @@ pub fn delete() -> Route {
/// * /{project_id}
///
pub fn head() -> Route {
Route::new().method(Method::HEAD)
method(Method::HEAD)
}
/// Create *route* and add method guard.

View File

@@ -33,7 +33,7 @@ ssl = ["openssl", "actix-server/ssl", "awc/ssl"]
actix-codec = "0.1.2"
actix-rt = "0.2.2"
actix-service = "0.3.6"
actix-server = "0.4.1"
actix-server = "0.4.3"
actix-utils = "0.3.5"
awc = "0.1.0-alpha.5"
@@ -56,4 +56,4 @@ openssl = { version="0.10", optional = true }
[dev-dependencies]
actix-web = "1.0.0-alpha.5"
actix-http = "0.1.0-alpha.5"
actix-http = "0.1.0"