mirror of
https://github.com/actix/actix-web.git
synced 2024-12-22 16:16:40 +00:00
add rustls support for actix-http and awc (#998)
* add rustls support for actix-http and awc * fix features conflict * remove unnecessary duplication * test server with rust-tls * fix * test rustls * awc rustls test * format * tests * fix dependencies * fixes and add changes * remove test-server and Cargo.toml dev-dependencies changes * cargo fmt
This commit is contained in:
parent
0d9ea41047
commit
cb19ebfe0c
21 changed files with 1317 additions and 674 deletions
|
@ -105,9 +105,9 @@ openssl = { version="0.10", optional = true }
|
|||
rustls = { version = "0.15", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
actix = { version = "0.8.3" }
|
||||
actix-http = { version = "0.2.5", features=["ssl", "brotli", "flate2-zlib"] }
|
||||
actix-http-test = { version = "0.2.4", features=["ssl"] }
|
||||
actix = "0.8.3"
|
||||
actix-connect = "0.2.2"
|
||||
actix-http-test = "0.2.4"
|
||||
rand = "0.7"
|
||||
env_logger = "0.6"
|
||||
serde_derive = "1.0"
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
### Changed
|
||||
|
||||
* Add `Clone` impl for `HeaderMap`
|
||||
* Add `rustls` support
|
||||
|
||||
### Fixed
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "actix-http"
|
||||
version = "0.2.7"
|
||||
version = "0.2.8"
|
||||
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
||||
description = "Actix http primitives"
|
||||
readme = "README.md"
|
||||
|
@ -27,6 +27,7 @@ default = []
|
|||
|
||||
# openssl
|
||||
ssl = ["openssl", "actix-connect/ssl"]
|
||||
rust-tls = ["rustls", "webpki-roots", "actix-connect/rust-tls"]
|
||||
|
||||
# brotli encoding, requires c compiler
|
||||
brotli = ["brotli2"]
|
||||
|
@ -46,7 +47,7 @@ secure-cookies = ["ring"]
|
|||
[dependencies]
|
||||
actix-service = "0.4.1"
|
||||
actix-codec = "0.1.2"
|
||||
actix-connect = "0.2.1"
|
||||
actix-connect = "0.2.2"
|
||||
actix-utils = "0.4.4"
|
||||
actix-server-config = "0.1.2"
|
||||
actix-threadpool = "0.1.1"
|
||||
|
@ -93,6 +94,8 @@ flate2 = { version="1.0.7", optional = true, default-features = false }
|
|||
# optional deps
|
||||
failure = { version = "0.1.5", optional = true }
|
||||
openssl = { version="0.10", optional = true }
|
||||
rustls = { version = "0.15.2", optional = true }
|
||||
webpki-roots = { version = "0.16", optional = true }
|
||||
chrono = "0.4.6"
|
||||
|
||||
[dev-dependencies]
|
||||
|
|
|
@ -17,9 +17,21 @@ use super::pool::{ConnectionPool, Protocol};
|
|||
use super::Connect;
|
||||
|
||||
#[cfg(feature = "ssl")]
|
||||
use openssl::ssl::SslConnector;
|
||||
use openssl::ssl::SslConnector as OpensslConnector;
|
||||
|
||||
#[cfg(not(feature = "ssl"))]
|
||||
#[cfg(feature = "rust-tls")]
|
||||
use rustls::ClientConfig;
|
||||
#[cfg(feature = "rust-tls")]
|
||||
use std::sync::Arc;
|
||||
|
||||
#[cfg(any(feature = "ssl", feature = "rust-tls"))]
|
||||
enum SslConnector {
|
||||
#[cfg(feature = "ssl")]
|
||||
Openssl(OpensslConnector),
|
||||
#[cfg(feature = "rust-tls")]
|
||||
Rustls(Arc<ClientConfig>),
|
||||
}
|
||||
#[cfg(not(any(feature = "ssl", feature = "rust-tls")))]
|
||||
type SslConnector = ();
|
||||
|
||||
/// Manages http client network connectivity
|
||||
|
@ -46,6 +58,9 @@ pub struct Connector<T, U> {
|
|||
_t: PhantomData<U>,
|
||||
}
|
||||
|
||||
trait Io: AsyncRead + AsyncWrite {}
|
||||
impl<T: AsyncRead + AsyncWrite> Io for T {}
|
||||
|
||||
impl Connector<(), ()> {
|
||||
#[allow(clippy::new_ret_no_self)]
|
||||
pub fn new() -> Connector<
|
||||
|
@ -61,13 +76,23 @@ impl Connector<(), ()> {
|
|||
{
|
||||
use openssl::ssl::SslMethod;
|
||||
|
||||
let mut ssl = SslConnector::builder(SslMethod::tls()).unwrap();
|
||||
let mut ssl = OpensslConnector::builder(SslMethod::tls()).unwrap();
|
||||
let _ = ssl
|
||||
.set_alpn_protos(b"\x02h2\x08http/1.1")
|
||||
.map_err(|e| error!("Can not set alpn protocol: {:?}", e));
|
||||
ssl.build()
|
||||
SslConnector::Openssl(ssl.build())
|
||||
}
|
||||
#[cfg(not(feature = "ssl"))]
|
||||
#[cfg(all(not(feature = "ssl"), feature = "rust-tls"))]
|
||||
{
|
||||
let protos = vec![b"h2".to_vec(), b"http/1.1".to_vec()];
|
||||
let mut config = ClientConfig::new();
|
||||
config.set_protocols(&protos);
|
||||
config
|
||||
.root_store
|
||||
.add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS);
|
||||
SslConnector::Rustls(Arc::new(config))
|
||||
}
|
||||
#[cfg(not(any(feature = "ssl", feature = "rust-tls")))]
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -127,8 +152,14 @@ where
|
|||
|
||||
#[cfg(feature = "ssl")]
|
||||
/// Use custom `SslConnector` instance.
|
||||
pub fn ssl(mut self, connector: SslConnector) -> Self {
|
||||
self.ssl = connector;
|
||||
pub fn ssl(mut self, connector: OpensslConnector) -> Self {
|
||||
self.ssl = SslConnector::Openssl(connector);
|
||||
self
|
||||
}
|
||||
|
||||
#[cfg(feature = "rust-tls")]
|
||||
pub fn rustls(mut self, connector: Arc<ClientConfig>) -> Self {
|
||||
self.ssl = SslConnector::Rustls(connector);
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -182,7 +213,7 @@ where
|
|||
self,
|
||||
) -> impl Service<Request = Connect, Response = impl Connection, Error = ConnectError>
|
||||
+ Clone {
|
||||
#[cfg(not(feature = "ssl"))]
|
||||
#[cfg(not(any(feature = "ssl", feature = "rust-tls")))]
|
||||
{
|
||||
let connector = TimeoutService::new(
|
||||
self.timeout,
|
||||
|
@ -207,10 +238,16 @@ where
|
|||
),
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "ssl")]
|
||||
#[cfg(any(feature = "ssl", feature = "rust-tls"))]
|
||||
{
|
||||
const H2: &[u8] = b"h2";
|
||||
#[cfg(feature = "ssl")]
|
||||
use actix_connect::ssl::OpensslConnector;
|
||||
#[cfg(feature = "rust-tls")]
|
||||
use actix_connect::ssl::RustlsConnector;
|
||||
use actix_service::boxed::service;
|
||||
#[cfg(feature = "rust-tls")]
|
||||
use rustls::Session;
|
||||
|
||||
let ssl_service = TimeoutService::new(
|
||||
self.timeout,
|
||||
|
@ -218,24 +255,46 @@ where
|
|||
srv.call(TcpConnect::new(msg.uri).set_addr(msg.addr))
|
||||
})
|
||||
.map_err(ConnectError::from)
|
||||
.and_then(
|
||||
OpensslConnector::service(self.ssl)
|
||||
.map_err(ConnectError::from)
|
||||
.map(|stream| {
|
||||
let sock = stream.into_parts().0;
|
||||
let h2 = sock
|
||||
.get_ref()
|
||||
.ssl()
|
||||
.selected_alpn_protocol()
|
||||
.map(|protos| protos.windows(2).any(|w| w == H2))
|
||||
.unwrap_or(false);
|
||||
if h2 {
|
||||
(sock, Protocol::Http2)
|
||||
} else {
|
||||
(sock, Protocol::Http1)
|
||||
}
|
||||
}),
|
||||
),
|
||||
.and_then(match self.ssl {
|
||||
#[cfg(feature = "ssl")]
|
||||
SslConnector::Openssl(ssl) => service(
|
||||
OpensslConnector::service(ssl)
|
||||
.map_err(ConnectError::from)
|
||||
.map(|stream| {
|
||||
let sock = stream.into_parts().0;
|
||||
let h2 = sock
|
||||
.get_ref()
|
||||
.ssl()
|
||||
.selected_alpn_protocol()
|
||||
.map(|protos| protos.windows(2).any(|w| w == H2))
|
||||
.unwrap_or(false);
|
||||
if h2 {
|
||||
(Box::new(sock) as Box<Io>, Protocol::Http2)
|
||||
} else {
|
||||
(Box::new(sock) as Box<Io>, Protocol::Http1)
|
||||
}
|
||||
}),
|
||||
),
|
||||
#[cfg(feature = "rust-tls")]
|
||||
SslConnector::Rustls(ssl) => service(
|
||||
RustlsConnector::service(ssl)
|
||||
.map_err(ConnectError::from)
|
||||
.map(|stream| {
|
||||
let sock = stream.into_parts().0;
|
||||
let h2 = sock
|
||||
.get_ref()
|
||||
.1
|
||||
.get_alpn_protocol()
|
||||
.map(|protos| protos.windows(2).any(|w| w == H2))
|
||||
.unwrap_or(false);
|
||||
if h2 {
|
||||
(Box::new(sock) as Box<Io>, Protocol::Http2)
|
||||
} else {
|
||||
(Box::new(sock) as Box<Io>, Protocol::Http1)
|
||||
}
|
||||
}),
|
||||
),
|
||||
}),
|
||||
)
|
||||
.map_err(|e| match e {
|
||||
TimeoutError::Service(e) => e,
|
||||
|
@ -275,7 +334,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "ssl"))]
|
||||
#[cfg(not(any(feature = "ssl", feature = "rust-tls")))]
|
||||
mod connect_impl {
|
||||
use futures::future::{err, Either, FutureResult};
|
||||
use futures::Poll;
|
||||
|
@ -337,7 +396,7 @@ mod connect_impl {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "ssl")]
|
||||
#[cfg(any(feature = "ssl", feature = "rust-tls"))]
|
||||
mod connect_impl {
|
||||
use std::marker::PhantomData;
|
||||
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIDPjCCAiYCCQCmkoCBehOyYTANBgkqhkiG9w0BAQsFADBhMQswCQYDVQQGEwJV
|
||||
UzELMAkGA1UECAwCQ0ExCzAJBgNVBAcMAlNGMRAwDgYDVQQKDAdDb21wYW55MQww
|
||||
CgYDVQQLDANPcmcxGDAWBgNVBAMMD3d3dy5leGFtcGxlLmNvbTAeFw0xOTAzMjky
|
||||
MzE5MDlaFw0yMDAzMjgyMzE5MDlaMGExCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJD
|
||||
QTELMAkGA1UEBwwCU0YxEDAOBgNVBAoMB0NvbXBhbnkxDDAKBgNVBAsMA09yZzEY
|
||||
MBYGA1UEAwwPd3d3LmV4YW1wbGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
|
||||
MIIBCgKCAQEA2uFoWm74qumqIIsBBf/rgP3ZtZw6dRQhVoYjIwYk00T1RLmmbt8r
|
||||
YNh3lehmnrQlM/YC3dzcspucGqIfvs5FEReh/vgvsqY3lfy47Q1zzdtBrKq2ZBro
|
||||
AuJUe4ayMYz/L/2jAtPtGDQqWyzhKv6x/oz6N/tKqlzoGbjSGSJUqKAV+Tuo4YI4
|
||||
xw3r/RJg3I3+ruXOgM65GBdja7usI/BhseEOp9VXotoTEItGmvG2RFZ4A7cN124x
|
||||
giFl2IeYuC60jteZ+bnhPiqxcdzf3K4dnZlzrYma+FxwWbaow4wlpQcZVFdZ+K/Y
|
||||
p/Bbm/FDKoUHnEdn/QAanTruRxSGdai0owIDAQABMA0GCSqGSIb3DQEBCwUAA4IB
|
||||
AQAEWn3WAwAbd64f5jo2w4076s2qFiCJjPWoxO6bO75FgFFtw/NNev8pxGVw1ehg
|
||||
HiTO6VRYolL5S/RKOchjA83AcDEBjgf8fKtvTmE9kxZSUIo4kIvv8V9ZM72gJhDN
|
||||
8D/lXduTZ9JMwLOa1NUB8/I6CbaU3VzWkfodArKKpQF3M+LLgK03i12PD0KPQ5zv
|
||||
bwaNoQo6cTmPNIdsVZETRvPqONiCUaQV57G74dGtjeirCh/DO5EYRtb1thgS7TGm
|
||||
+Xg8OC5vZ6g0+xsrSqDBmWNtlI7S3bsL5C3LIEOOAL1ZJHRy2KvIGQ9ipb3XjnKS
|
||||
N7/wlQduRyPH7oaD/o4xf5Gt
|
||||
-----END CERTIFICATE-----
|
|
@ -1,27 +0,0 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpQIBAAKCAQEA2uFoWm74qumqIIsBBf/rgP3ZtZw6dRQhVoYjIwYk00T1RLmm
|
||||
bt8rYNh3lehmnrQlM/YC3dzcspucGqIfvs5FEReh/vgvsqY3lfy47Q1zzdtBrKq2
|
||||
ZBroAuJUe4ayMYz/L/2jAtPtGDQqWyzhKv6x/oz6N/tKqlzoGbjSGSJUqKAV+Tuo
|
||||
4YI4xw3r/RJg3I3+ruXOgM65GBdja7usI/BhseEOp9VXotoTEItGmvG2RFZ4A7cN
|
||||
124xgiFl2IeYuC60jteZ+bnhPiqxcdzf3K4dnZlzrYma+FxwWbaow4wlpQcZVFdZ
|
||||
+K/Yp/Bbm/FDKoUHnEdn/QAanTruRxSGdai0owIDAQABAoIBAQC4lzyQd+ITEbi+
|
||||
dTxJuQj94hgHB1htgKqU888SLI5F9nP6n67y9hb5N9WygSp6UWbGqYTFYwxlPMKr
|
||||
22p2WjL5NTsTcm+XdIKQZW/3y06Mn4qFefsT9XURaZriCjihfU2BRaCCNARSUzwd
|
||||
ZH4I6n9mM7KaH71aa7v6ZVoahE9tXPR6hM+SHQEySW4pWkEu98VpNNeIt6vP7WF9
|
||||
ONGbRa+0En4xgkuaxem2ZYa/GZFFtdQRkroNMhIRlfcPpkjy8DCc8E5RAkOzKC3O
|
||||
lnxQwt+tdNNkGZz02ed2hx/YHPwFYy76y6hK5dxq74iKIaOc8U5t0HjB1zVfwiR0
|
||||
5mcxMncxAoGBAP+RivwXZ4FcxDY1uyziF+rwlC/1RujQFEWXIxsXCnba5DH3yKul
|
||||
iKEIZPZtGhpsnQe367lcXcn7tztuoVjpAnk5L+hQY64tLwYbHeRcOMJ75C2y8FFC
|
||||
NeG5sQsrk3IU1+jhGvrbE7UgOeAuWJmv0M1vPNB/+hGoZBW5W5uU1x89AoGBANtA
|
||||
AhLtAcqQ/Qh2SpVhLljh7U85Be9tbCGua09clkYFzh3bcoBolXKH18Veww0TP0yF
|
||||
0748CKw1A+ITbTVFV+vKvi4jzIxS7mr4wYtVCMssbttQN7y3l30IDxJwa9j3zTJx
|
||||
IUn5OMMLv1JyitLId8HdOy1AdU3MkpJzdLyi1mFfAoGBAL3kL4fGABM/kU7SN6RO
|
||||
zgS0AvdrYOeljBp1BRGg2hab58g02vamxVEZgqMTR7zwjPDqOIz+03U7wda4Ccyd
|
||||
PUhDNJSB/r6xNepshZZi642eLlnCRguqjYyNw72QADtYv2B6uehAlXEUY8xtw0lW
|
||||
OGgcSeyF2pH6M3tswWNlgT3lAoGAQ/BttBelOnP7NKgTLH7Usc4wjyAIaszpePZn
|
||||
Ykw6dLBP0oixzoCZ7seRYSOgJWkVcEz39Db+KP60mVWTvbIjMHm+vOVy+Pip0JQM
|
||||
xXQwKWU3ZNZSrzPkyWW55ejYQn9nIn5T5mxH3ojBXHcJ9Y8RLQ20zKzwrI77zE3i
|
||||
mqGK9NkCgYEAq3dzHI0DGAJrR19sWl2LcqI19sj5a91tHx4cl1dJXS/iApOLLieU
|
||||
zyUGkwfsqjHPAZ7GacICeBojIn/7KdPdlSKAbGVAU3d4qzvFS0qmWzObplBz3niT
|
||||
Xnep2XLaVXqwlFJZZ6AHeKzYmMH0d0raiou2bpEUBqYizy2fi3NI4mA=
|
||||
-----END RSA PRIVATE KEY-----
|
462
actix-http/tests/test_rustls_server.rs
Normal file
462
actix-http/tests/test_rustls_server.rs
Normal file
|
@ -0,0 +1,462 @@
|
|||
#![cfg(feature = "rust-tls")]
|
||||
use actix_codec::{AsyncRead, AsyncWrite};
|
||||
use actix_http::error::PayloadError;
|
||||
use actix_http::http::header::{self, HeaderName, HeaderValue};
|
||||
use actix_http::http::{Method, StatusCode, Version};
|
||||
use actix_http::{body, error, Error, HttpService, Request, Response};
|
||||
use actix_http_test::TestServer;
|
||||
use actix_server::ssl::RustlsAcceptor;
|
||||
use actix_server_config::ServerConfig;
|
||||
use actix_service::{new_service_cfg, NewService};
|
||||
|
||||
use bytes::{Bytes, BytesMut};
|
||||
use futures::future::{self, ok, Future};
|
||||
use futures::stream::{once, Stream};
|
||||
use rustls::{
|
||||
internal::pemfile::{certs, pkcs8_private_keys},
|
||||
NoClientAuth, ServerConfig as RustlsServerConfig,
|
||||
};
|
||||
|
||||
use std::fs::File;
|
||||
use std::io::{BufReader, Result};
|
||||
|
||||
fn load_body<S>(stream: S) -> impl Future<Item = BytesMut, Error = PayloadError>
|
||||
where
|
||||
S: Stream<Item = Bytes, Error = PayloadError>,
|
||||
{
|
||||
stream.fold(BytesMut::new(), move |mut body, chunk| {
|
||||
body.extend_from_slice(&chunk);
|
||||
Ok::<_, PayloadError>(body)
|
||||
})
|
||||
}
|
||||
|
||||
fn ssl_acceptor<T: AsyncRead + AsyncWrite>() -> Result<RustlsAcceptor<T, ()>> {
|
||||
// load ssl keys
|
||||
let mut config = RustlsServerConfig::new(NoClientAuth::new());
|
||||
let cert_file = &mut BufReader::new(File::open("../tests/cert.pem").unwrap());
|
||||
let key_file = &mut BufReader::new(File::open("../tests/key.pem").unwrap());
|
||||
let cert_chain = certs(cert_file).unwrap();
|
||||
let mut keys = pkcs8_private_keys(key_file).unwrap();
|
||||
config.set_single_cert(cert_chain, keys.remove(0)).unwrap();
|
||||
|
||||
let protos = vec![b"h2".to_vec()];
|
||||
config.set_protocols(&protos);
|
||||
Ok(RustlsAcceptor::new(config))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_h2() -> Result<()> {
|
||||
let rustls = ssl_acceptor()?;
|
||||
let mut srv = TestServer::new(move || {
|
||||
rustls
|
||||
.clone()
|
||||
.map_err(|e| println!("Rustls error: {}", e))
|
||||
.and_then(
|
||||
HttpService::build()
|
||||
.h2(|_| future::ok::<_, Error>(Response::Ok().finish()))
|
||||
.map_err(|_| ()),
|
||||
)
|
||||
});
|
||||
|
||||
let response = srv.block_on(srv.sget("/").send()).unwrap();
|
||||
assert!(response.status().is_success());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_h2_1() -> Result<()> {
|
||||
let rustls = ssl_acceptor()?;
|
||||
let mut srv = TestServer::new(move || {
|
||||
rustls
|
||||
.clone()
|
||||
.map_err(|e| println!("Rustls error: {}", e))
|
||||
.and_then(
|
||||
HttpService::build()
|
||||
.finish(|req: Request| {
|
||||
assert!(req.peer_addr().is_some());
|
||||
assert_eq!(req.version(), Version::HTTP_2);
|
||||
future::ok::<_, Error>(Response::Ok().finish())
|
||||
})
|
||||
.map_err(|_| ()),
|
||||
)
|
||||
});
|
||||
|
||||
let response = srv.block_on(srv.sget("/").send()).unwrap();
|
||||
assert!(response.status().is_success());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_h2_body() -> Result<()> {
|
||||
let data = "HELLOWORLD".to_owned().repeat(64 * 1024);
|
||||
let rustls = ssl_acceptor()?;
|
||||
let mut srv = TestServer::new(move || {
|
||||
rustls
|
||||
.clone()
|
||||
.map_err(|e| println!("Rustls error: {}", e))
|
||||
.and_then(
|
||||
HttpService::build()
|
||||
.h2(|mut req: Request<_>| {
|
||||
load_body(req.take_payload())
|
||||
.and_then(|body| Ok(Response::Ok().body(body)))
|
||||
})
|
||||
.map_err(|_| ()),
|
||||
)
|
||||
});
|
||||
|
||||
let response = srv.block_on(srv.sget("/").send_body(data.clone())).unwrap();
|
||||
assert!(response.status().is_success());
|
||||
|
||||
let body = srv.load_body(response).unwrap();
|
||||
assert_eq!(&body, data.as_bytes());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_h2_content_length() {
|
||||
let rustls = ssl_acceptor().unwrap();
|
||||
|
||||
let mut srv = TestServer::new(move || {
|
||||
rustls
|
||||
.clone()
|
||||
.map_err(|e| println!("Rustls error: {}", e))
|
||||
.and_then(
|
||||
HttpService::build()
|
||||
.h2(|req: Request| {
|
||||
let indx: usize = req.uri().path()[1..].parse().unwrap();
|
||||
let statuses = [
|
||||
StatusCode::NO_CONTENT,
|
||||
StatusCode::CONTINUE,
|
||||
StatusCode::SWITCHING_PROTOCOLS,
|
||||
StatusCode::PROCESSING,
|
||||
StatusCode::OK,
|
||||
StatusCode::NOT_FOUND,
|
||||
];
|
||||
future::ok::<_, ()>(Response::new(statuses[indx]))
|
||||
})
|
||||
.map_err(|_| ()),
|
||||
)
|
||||
});
|
||||
|
||||
let header = HeaderName::from_static("content-length");
|
||||
let value = HeaderValue::from_static("0");
|
||||
|
||||
{
|
||||
for i in 0..4 {
|
||||
let req = srv
|
||||
.request(Method::GET, srv.surl(&format!("/{}", i)))
|
||||
.send();
|
||||
let response = srv.block_on(req).unwrap();
|
||||
assert_eq!(response.headers().get(&header), None);
|
||||
|
||||
let req = srv
|
||||
.request(Method::HEAD, srv.surl(&format!("/{}", i)))
|
||||
.send();
|
||||
let response = srv.block_on(req).unwrap();
|
||||
assert_eq!(response.headers().get(&header), None);
|
||||
}
|
||||
|
||||
for i in 4..6 {
|
||||
let req = srv
|
||||
.request(Method::GET, srv.surl(&format!("/{}", i)))
|
||||
.send();
|
||||
let response = srv.block_on(req).unwrap();
|
||||
assert_eq!(response.headers().get(&header), Some(&value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_h2_headers() {
|
||||
let data = STR.repeat(10);
|
||||
let data2 = data.clone();
|
||||
let rustls = ssl_acceptor().unwrap();
|
||||
|
||||
let mut srv = TestServer::new(move || {
|
||||
let data = data.clone();
|
||||
rustls
|
||||
.clone()
|
||||
.map_err(|e| println!("Rustls error: {}", e))
|
||||
.and_then(
|
||||
HttpService::build().h2(move |_| {
|
||||
let mut config = Response::Ok();
|
||||
for idx in 0..90 {
|
||||
config.header(
|
||||
format!("X-TEST-{}", idx).as_str(),
|
||||
"TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
||||
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
||||
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
||||
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
||||
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
||||
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
||||
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
||||
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
||||
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
||||
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
||||
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
||||
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
||||
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST ",
|
||||
);
|
||||
}
|
||||
future::ok::<_, ()>(config.body(data.clone()))
|
||||
}).map_err(|_| ()))
|
||||
});
|
||||
|
||||
let response = srv.block_on(srv.sget("/").send()).unwrap();
|
||||
assert!(response.status().is_success());
|
||||
|
||||
// read response
|
||||
let bytes = srv.load_body(response).unwrap();
|
||||
assert_eq!(bytes, Bytes::from(data2));
|
||||
}
|
||||
|
||||
const STR: &str = "Hello World Hello World Hello World Hello World Hello World \
|
||||
Hello World Hello World Hello World Hello World Hello World \
|
||||
Hello World Hello World Hello World Hello World Hello World \
|
||||
Hello World Hello World Hello World Hello World Hello World \
|
||||
Hello World Hello World Hello World Hello World Hello World \
|
||||
Hello World Hello World Hello World Hello World Hello World \
|
||||
Hello World Hello World Hello World Hello World Hello World \
|
||||
Hello World Hello World Hello World Hello World Hello World \
|
||||
Hello World Hello World Hello World Hello World Hello World \
|
||||
Hello World Hello World Hello World Hello World Hello World \
|
||||
Hello World Hello World Hello World Hello World Hello World \
|
||||
Hello World Hello World Hello World Hello World Hello World \
|
||||
Hello World Hello World Hello World Hello World Hello World \
|
||||
Hello World Hello World Hello World Hello World Hello World \
|
||||
Hello World Hello World Hello World Hello World Hello World \
|
||||
Hello World Hello World Hello World Hello World Hello World \
|
||||
Hello World Hello World Hello World Hello World Hello World \
|
||||
Hello World Hello World Hello World Hello World Hello World \
|
||||
Hello World Hello World Hello World Hello World Hello World \
|
||||
Hello World Hello World Hello World Hello World Hello World \
|
||||
Hello World Hello World Hello World Hello World Hello World";
|
||||
|
||||
#[test]
|
||||
fn test_h2_body2() {
|
||||
let rustls = ssl_acceptor().unwrap();
|
||||
let mut srv = TestServer::new(move || {
|
||||
rustls
|
||||
.clone()
|
||||
.map_err(|e| println!("Rustls error: {}", e))
|
||||
.and_then(
|
||||
HttpService::build()
|
||||
.h2(|_| future::ok::<_, ()>(Response::Ok().body(STR)))
|
||||
.map_err(|_| ()),
|
||||
)
|
||||
});
|
||||
|
||||
let response = srv.block_on(srv.sget("/").send()).unwrap();
|
||||
assert!(response.status().is_success());
|
||||
|
||||
// read response
|
||||
let bytes = srv.load_body(response).unwrap();
|
||||
assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_h2_head_empty() {
|
||||
let rustls = ssl_acceptor().unwrap();
|
||||
let mut srv = TestServer::new(move || {
|
||||
rustls
|
||||
.clone()
|
||||
.map_err(|e| println!("Rustls error: {}", e))
|
||||
.and_then(
|
||||
HttpService::build()
|
||||
.finish(|_| ok::<_, ()>(Response::Ok().body(STR)))
|
||||
.map_err(|_| ()),
|
||||
)
|
||||
});
|
||||
|
||||
let response = srv.block_on(srv.shead("/").send()).unwrap();
|
||||
assert!(response.status().is_success());
|
||||
assert_eq!(response.version(), Version::HTTP_2);
|
||||
|
||||
{
|
||||
let len = response
|
||||
.headers()
|
||||
.get(http::header::CONTENT_LENGTH)
|
||||
.unwrap();
|
||||
assert_eq!(format!("{}", STR.len()), len.to_str().unwrap());
|
||||
}
|
||||
|
||||
// read response
|
||||
let bytes = srv.load_body(response).unwrap();
|
||||
assert!(bytes.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_h2_head_binary() {
|
||||
let rustls = ssl_acceptor().unwrap();
|
||||
let mut srv = TestServer::new(move || {
|
||||
rustls
|
||||
.clone()
|
||||
.map_err(|e| println!("Rustls error: {}", e))
|
||||
.and_then(
|
||||
HttpService::build()
|
||||
.h2(|_| {
|
||||
ok::<_, ()>(
|
||||
Response::Ok().content_length(STR.len() as u64).body(STR),
|
||||
)
|
||||
})
|
||||
.map_err(|_| ()),
|
||||
)
|
||||
});
|
||||
|
||||
let response = srv.block_on(srv.shead("/").send()).unwrap();
|
||||
assert!(response.status().is_success());
|
||||
|
||||
{
|
||||
let len = response
|
||||
.headers()
|
||||
.get(http::header::CONTENT_LENGTH)
|
||||
.unwrap();
|
||||
assert_eq!(format!("{}", STR.len()), len.to_str().unwrap());
|
||||
}
|
||||
|
||||
// read response
|
||||
let bytes = srv.load_body(response).unwrap();
|
||||
assert!(bytes.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_h2_head_binary2() {
|
||||
let rustls = ssl_acceptor().unwrap();
|
||||
let mut srv = TestServer::new(move || {
|
||||
rustls
|
||||
.clone()
|
||||
.map_err(|e| println!("Rustls error: {}", e))
|
||||
.and_then(
|
||||
HttpService::build()
|
||||
.h2(|_| ok::<_, ()>(Response::Ok().body(STR)))
|
||||
.map_err(|_| ()),
|
||||
)
|
||||
});
|
||||
|
||||
let response = srv.block_on(srv.shead("/").send()).unwrap();
|
||||
assert!(response.status().is_success());
|
||||
|
||||
{
|
||||
let len = response
|
||||
.headers()
|
||||
.get(http::header::CONTENT_LENGTH)
|
||||
.unwrap();
|
||||
assert_eq!(format!("{}", STR.len()), len.to_str().unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_h2_body_length() {
|
||||
let rustls = ssl_acceptor().unwrap();
|
||||
let mut srv = TestServer::new(move || {
|
||||
rustls
|
||||
.clone()
|
||||
.map_err(|e| println!("Rustls error: {}", e))
|
||||
.and_then(
|
||||
HttpService::build()
|
||||
.h2(|_| {
|
||||
let body = once(Ok(Bytes::from_static(STR.as_ref())));
|
||||
ok::<_, ()>(
|
||||
Response::Ok()
|
||||
.body(body::SizedStream::new(STR.len() as u64, body)),
|
||||
)
|
||||
})
|
||||
.map_err(|_| ()),
|
||||
)
|
||||
});
|
||||
|
||||
let response = srv.block_on(srv.sget("/").send()).unwrap();
|
||||
assert!(response.status().is_success());
|
||||
|
||||
// read response
|
||||
let bytes = srv.load_body(response).unwrap();
|
||||
assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_h2_body_chunked_explicit() {
|
||||
let rustls = ssl_acceptor().unwrap();
|
||||
let mut srv = TestServer::new(move || {
|
||||
rustls
|
||||
.clone()
|
||||
.map_err(|e| println!("Rustls error: {}", e))
|
||||
.and_then(
|
||||
HttpService::build()
|
||||
.h2(|_| {
|
||||
let body =
|
||||
once::<_, Error>(Ok(Bytes::from_static(STR.as_ref())));
|
||||
ok::<_, ()>(
|
||||
Response::Ok()
|
||||
.header(header::TRANSFER_ENCODING, "chunked")
|
||||
.streaming(body),
|
||||
)
|
||||
})
|
||||
.map_err(|_| ()),
|
||||
)
|
||||
});
|
||||
|
||||
let response = srv.block_on(srv.sget("/").send()).unwrap();
|
||||
assert!(response.status().is_success());
|
||||
assert!(!response.headers().contains_key(header::TRANSFER_ENCODING));
|
||||
|
||||
// read response
|
||||
let bytes = srv.load_body(response).unwrap();
|
||||
|
||||
// decode
|
||||
assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_h2_response_http_error_handling() {
|
||||
let rustls = ssl_acceptor().unwrap();
|
||||
|
||||
let mut srv = TestServer::new(move || {
|
||||
rustls
|
||||
.clone()
|
||||
.map_err(|e| println!("Rustls error: {}", e))
|
||||
.and_then(
|
||||
HttpService::build()
|
||||
.h2(new_service_cfg(|_: &ServerConfig| {
|
||||
Ok::<_, ()>(|_| {
|
||||
let broken_header = Bytes::from_static(b"\0\0\0");
|
||||
ok::<_, ()>(
|
||||
Response::Ok()
|
||||
.header(http::header::CONTENT_TYPE, broken_header)
|
||||
.body(STR),
|
||||
)
|
||||
})
|
||||
}))
|
||||
.map_err(|_| ()),
|
||||
)
|
||||
});
|
||||
|
||||
let response = srv.block_on(srv.sget("/").send()).unwrap();
|
||||
assert_eq!(response.status(), http::StatusCode::INTERNAL_SERVER_ERROR);
|
||||
|
||||
// read response
|
||||
let bytes = srv.load_body(response).unwrap();
|
||||
assert_eq!(bytes, Bytes::from_static(b"failed to parse header value"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_h2_service_error() {
|
||||
let rustls = ssl_acceptor().unwrap();
|
||||
|
||||
let mut srv = TestServer::new(move || {
|
||||
rustls
|
||||
.clone()
|
||||
.map_err(|e| println!("Rustls error: {}", e))
|
||||
.and_then(
|
||||
HttpService::build()
|
||||
.h2(|_| Err::<Response, Error>(error::ErrorBadRequest("error")))
|
||||
.map_err(|_| ()),
|
||||
)
|
||||
});
|
||||
|
||||
let response = srv.block_on(srv.sget("/").send()).unwrap();
|
||||
assert_eq!(response.status(), http::StatusCode::INTERNAL_SERVER_ERROR);
|
||||
|
||||
// read response
|
||||
let bytes = srv.load_body(response).unwrap();
|
||||
assert!(bytes.is_empty());
|
||||
}
|
|
@ -2,32 +2,19 @@ use std::io::{Read, Write};
|
|||
use std::time::Duration;
|
||||
use std::{net, thread};
|
||||
|
||||
use actix_codec::{AsyncRead, AsyncWrite};
|
||||
use actix_http_test::TestServer;
|
||||
use actix_server_config::ServerConfig;
|
||||
use actix_service::{new_service_cfg, service_fn, NewService};
|
||||
use bytes::{Bytes, BytesMut};
|
||||
use bytes::Bytes;
|
||||
use futures::future::{self, ok, Future};
|
||||
use futures::stream::{once, Stream};
|
||||
use regex::Regex;
|
||||
use tokio_timer::sleep;
|
||||
|
||||
use actix_http::error::PayloadError;
|
||||
use actix_http::{
|
||||
body, error, http, http::header, Error, HttpService, KeepAlive, Request, Response,
|
||||
};
|
||||
|
||||
#[cfg(feature = "ssl")]
|
||||
fn load_body<S>(stream: S) -> impl Future<Item = BytesMut, Error = PayloadError>
|
||||
where
|
||||
S: Stream<Item = Bytes, Error = PayloadError>,
|
||||
{
|
||||
stream.fold(BytesMut::new(), move |mut body, chunk| {
|
||||
body.extend_from_slice(&chunk);
|
||||
Ok::<_, PayloadError>(body)
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_h1() {
|
||||
let mut srv = TestServer::new(|| {
|
||||
|
@ -64,101 +51,6 @@ fn test_h1_2() {
|
|||
assert!(response.status().is_success());
|
||||
}
|
||||
|
||||
#[cfg(feature = "ssl")]
|
||||
fn ssl_acceptor<T: AsyncRead + AsyncWrite>(
|
||||
) -> std::io::Result<actix_server::ssl::OpensslAcceptor<T, ()>> {
|
||||
use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod};
|
||||
// load ssl keys
|
||||
let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap();
|
||||
builder
|
||||
.set_private_key_file("tests/key.pem", SslFiletype::PEM)
|
||||
.unwrap();
|
||||
builder
|
||||
.set_certificate_chain_file("tests/cert.pem")
|
||||
.unwrap();
|
||||
builder.set_alpn_select_callback(|_, protos| {
|
||||
const H2: &[u8] = b"\x02h2";
|
||||
if protos.windows(3).any(|window| window == H2) {
|
||||
Ok(b"h2")
|
||||
} else {
|
||||
Err(openssl::ssl::AlpnError::NOACK)
|
||||
}
|
||||
});
|
||||
builder.set_alpn_protos(b"\x02h2")?;
|
||||
Ok(actix_server::ssl::OpensslAcceptor::new(builder.build()))
|
||||
}
|
||||
|
||||
#[cfg(feature = "ssl")]
|
||||
#[test]
|
||||
fn test_h2() -> std::io::Result<()> {
|
||||
let openssl = ssl_acceptor()?;
|
||||
let mut srv = TestServer::new(move || {
|
||||
openssl
|
||||
.clone()
|
||||
.map_err(|e| println!("Openssl error: {}", e))
|
||||
.and_then(
|
||||
HttpService::build()
|
||||
.h2(|_| future::ok::<_, Error>(Response::Ok().finish()))
|
||||
.map_err(|_| ()),
|
||||
)
|
||||
});
|
||||
|
||||
let response = srv.block_on(srv.sget("/").send()).unwrap();
|
||||
assert!(response.status().is_success());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "ssl")]
|
||||
#[test]
|
||||
fn test_h2_1() -> std::io::Result<()> {
|
||||
let openssl = ssl_acceptor()?;
|
||||
let mut srv = TestServer::new(move || {
|
||||
openssl
|
||||
.clone()
|
||||
.map_err(|e| println!("Openssl error: {}", e))
|
||||
.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())
|
||||
})
|
||||
.map_err(|_| ()),
|
||||
)
|
||||
});
|
||||
|
||||
let response = srv.block_on(srv.sget("/").send()).unwrap();
|
||||
assert!(response.status().is_success());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "ssl")]
|
||||
#[test]
|
||||
fn test_h2_body() -> std::io::Result<()> {
|
||||
let data = "HELLOWORLD".to_owned().repeat(64 * 1024);
|
||||
let openssl = ssl_acceptor()?;
|
||||
let mut srv = TestServer::new(move || {
|
||||
openssl
|
||||
.clone()
|
||||
.map_err(|e| println!("Openssl error: {}", e))
|
||||
.and_then(
|
||||
HttpService::build()
|
||||
.h2(|mut req: Request<_>| {
|
||||
load_body(req.take_payload())
|
||||
.and_then(|body| Ok(Response::Ok().body(body)))
|
||||
})
|
||||
.map_err(|_| ()),
|
||||
)
|
||||
});
|
||||
|
||||
let response = srv.block_on(srv.sget("/").send_body(data.clone())).unwrap();
|
||||
assert!(response.status().is_success());
|
||||
|
||||
let body = srv.load_body(response).unwrap();
|
||||
assert_eq!(&body, data.as_bytes());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_expect_continue() {
|
||||
let srv = TestServer::new(|| {
|
||||
|
@ -457,65 +349,6 @@ fn test_content_length() {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "ssl")]
|
||||
#[test]
|
||||
fn test_h2_content_length() {
|
||||
use actix_http::http::{
|
||||
header::{HeaderName, HeaderValue},
|
||||
StatusCode,
|
||||
};
|
||||
let openssl = ssl_acceptor().unwrap();
|
||||
|
||||
let mut srv = TestServer::new(move || {
|
||||
openssl
|
||||
.clone()
|
||||
.map_err(|e| println!("Openssl error: {}", e))
|
||||
.and_then(
|
||||
HttpService::build()
|
||||
.h2(|req: Request| {
|
||||
let indx: usize = req.uri().path()[1..].parse().unwrap();
|
||||
let statuses = [
|
||||
StatusCode::NO_CONTENT,
|
||||
StatusCode::CONTINUE,
|
||||
StatusCode::SWITCHING_PROTOCOLS,
|
||||
StatusCode::PROCESSING,
|
||||
StatusCode::OK,
|
||||
StatusCode::NOT_FOUND,
|
||||
];
|
||||
future::ok::<_, ()>(Response::new(statuses[indx]))
|
||||
})
|
||||
.map_err(|_| ()),
|
||||
)
|
||||
});
|
||||
|
||||
let header = HeaderName::from_static("content-length");
|
||||
let value = HeaderValue::from_static("0");
|
||||
|
||||
{
|
||||
for i in 0..4 {
|
||||
let req = srv
|
||||
.request(http::Method::GET, srv.surl(&format!("/{}", i)))
|
||||
.send();
|
||||
let response = srv.block_on(req).unwrap();
|
||||
assert_eq!(response.headers().get(&header), None);
|
||||
|
||||
let req = srv
|
||||
.request(http::Method::HEAD, srv.surl(&format!("/{}", i)))
|
||||
.send();
|
||||
let response = srv.block_on(req).unwrap();
|
||||
assert_eq!(response.headers().get(&header), None);
|
||||
}
|
||||
|
||||
for i in 4..6 {
|
||||
let req = srv
|
||||
.request(http::Method::GET, srv.surl(&format!("/{}", i)))
|
||||
.send();
|
||||
let response = srv.block_on(req).unwrap();
|
||||
assert_eq!(response.headers().get(&header), Some(&value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_h1_headers() {
|
||||
let data = STR.repeat(10);
|
||||
|
@ -555,51 +388,6 @@ fn test_h1_headers() {
|
|||
assert_eq!(bytes, Bytes::from(data2));
|
||||
}
|
||||
|
||||
#[cfg(feature = "ssl")]
|
||||
#[test]
|
||||
fn test_h2_headers() {
|
||||
let data = STR.repeat(10);
|
||||
let data2 = data.clone();
|
||||
let openssl = ssl_acceptor().unwrap();
|
||||
|
||||
let mut srv = TestServer::new(move || {
|
||||
let data = data.clone();
|
||||
openssl
|
||||
.clone()
|
||||
.map_err(|e| println!("Openssl error: {}", e))
|
||||
.and_then(
|
||||
HttpService::build().h2(move |_| {
|
||||
let mut builder = Response::Ok();
|
||||
for idx in 0..90 {
|
||||
builder.header(
|
||||
format!("X-TEST-{}", idx).as_str(),
|
||||
"TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
||||
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
||||
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
||||
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
||||
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
||||
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
||||
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
||||
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
||||
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
||||
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
||||
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
||||
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
||||
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST ",
|
||||
);
|
||||
}
|
||||
future::ok::<_, ()>(builder.body(data.clone()))
|
||||
}).map_err(|_| ()))
|
||||
});
|
||||
|
||||
let response = srv.block_on(srv.sget("/").send()).unwrap();
|
||||
assert!(response.status().is_success());
|
||||
|
||||
// read response
|
||||
let bytes = srv.load_body(response).unwrap();
|
||||
assert_eq!(bytes, Bytes::from(data2));
|
||||
}
|
||||
|
||||
const STR: &str = "Hello World Hello World Hello World Hello World Hello World \
|
||||
Hello World Hello World Hello World Hello World Hello World \
|
||||
Hello World Hello World Hello World Hello World Hello World \
|
||||
|
@ -636,29 +424,6 @@ fn test_h1_body() {
|
|||
assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
|
||||
}
|
||||
|
||||
#[cfg(feature = "ssl")]
|
||||
#[test]
|
||||
fn test_h2_body2() {
|
||||
let openssl = ssl_acceptor().unwrap();
|
||||
let mut srv = TestServer::new(move || {
|
||||
openssl
|
||||
.clone()
|
||||
.map_err(|e| println!("Openssl error: {}", e))
|
||||
.and_then(
|
||||
HttpService::build()
|
||||
.h2(|_| future::ok::<_, ()>(Response::Ok().body(STR)))
|
||||
.map_err(|_| ()),
|
||||
)
|
||||
});
|
||||
|
||||
let response = srv.block_on(srv.sget("/").send()).unwrap();
|
||||
assert!(response.status().is_success());
|
||||
|
||||
// read response
|
||||
let bytes = srv.load_body(response).unwrap();
|
||||
assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_h1_head_empty() {
|
||||
let mut srv = TestServer::new(|| {
|
||||
|
@ -681,38 +446,6 @@ fn test_h1_head_empty() {
|
|||
assert!(bytes.is_empty());
|
||||
}
|
||||
|
||||
#[cfg(feature = "ssl")]
|
||||
#[test]
|
||||
fn test_h2_head_empty() {
|
||||
let openssl = ssl_acceptor().unwrap();
|
||||
let mut srv = TestServer::new(move || {
|
||||
openssl
|
||||
.clone()
|
||||
.map_err(|e| println!("Openssl error: {}", e))
|
||||
.and_then(
|
||||
HttpService::build()
|
||||
.finish(|_| ok::<_, ()>(Response::Ok().body(STR)))
|
||||
.map_err(|_| ()),
|
||||
)
|
||||
});
|
||||
|
||||
let response = srv.block_on(srv.shead("/").send()).unwrap();
|
||||
assert!(response.status().is_success());
|
||||
assert_eq!(response.version(), http::Version::HTTP_2);
|
||||
|
||||
{
|
||||
let len = response
|
||||
.headers()
|
||||
.get(http::header::CONTENT_LENGTH)
|
||||
.unwrap();
|
||||
assert_eq!(format!("{}", STR.len()), len.to_str().unwrap());
|
||||
}
|
||||
|
||||
// read response
|
||||
let bytes = srv.load_body(response).unwrap();
|
||||
assert!(bytes.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_h1_head_binary() {
|
||||
let mut srv = TestServer::new(|| {
|
||||
|
@ -737,41 +470,6 @@ fn test_h1_head_binary() {
|
|||
assert!(bytes.is_empty());
|
||||
}
|
||||
|
||||
#[cfg(feature = "ssl")]
|
||||
#[test]
|
||||
fn test_h2_head_binary() {
|
||||
let openssl = ssl_acceptor().unwrap();
|
||||
let mut srv = TestServer::new(move || {
|
||||
openssl
|
||||
.clone()
|
||||
.map_err(|e| println!("Openssl error: {}", e))
|
||||
.and_then(
|
||||
HttpService::build()
|
||||
.h2(|_| {
|
||||
ok::<_, ()>(
|
||||
Response::Ok().content_length(STR.len() as u64).body(STR),
|
||||
)
|
||||
})
|
||||
.map_err(|_| ()),
|
||||
)
|
||||
});
|
||||
|
||||
let response = srv.block_on(srv.shead("/").send()).unwrap();
|
||||
assert!(response.status().is_success());
|
||||
|
||||
{
|
||||
let len = response
|
||||
.headers()
|
||||
.get(http::header::CONTENT_LENGTH)
|
||||
.unwrap();
|
||||
assert_eq!(format!("{}", STR.len()), len.to_str().unwrap());
|
||||
}
|
||||
|
||||
// read response
|
||||
let bytes = srv.load_body(response).unwrap();
|
||||
assert!(bytes.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_h1_head_binary2() {
|
||||
let mut srv = TestServer::new(|| {
|
||||
|
@ -790,33 +488,6 @@ fn test_h1_head_binary2() {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "ssl")]
|
||||
#[test]
|
||||
fn test_h2_head_binary2() {
|
||||
let openssl = ssl_acceptor().unwrap();
|
||||
let mut srv = TestServer::new(move || {
|
||||
openssl
|
||||
.clone()
|
||||
.map_err(|e| println!("Openssl error: {}", e))
|
||||
.and_then(
|
||||
HttpService::build()
|
||||
.h2(|_| ok::<_, ()>(Response::Ok().body(STR)))
|
||||
.map_err(|_| ()),
|
||||
)
|
||||
});
|
||||
|
||||
let response = srv.block_on(srv.shead("/").send()).unwrap();
|
||||
assert!(response.status().is_success());
|
||||
|
||||
{
|
||||
let len = response
|
||||
.headers()
|
||||
.get(http::header::CONTENT_LENGTH)
|
||||
.unwrap();
|
||||
assert_eq!(format!("{}", STR.len()), len.to_str().unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_h1_body_length() {
|
||||
let mut srv = TestServer::new(|| {
|
||||
|
@ -836,35 +507,6 @@ fn test_h1_body_length() {
|
|||
assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
|
||||
}
|
||||
|
||||
#[cfg(feature = "ssl")]
|
||||
#[test]
|
||||
fn test_h2_body_length() {
|
||||
let openssl = ssl_acceptor().unwrap();
|
||||
let mut srv = TestServer::new(move || {
|
||||
openssl
|
||||
.clone()
|
||||
.map_err(|e| println!("Openssl error: {}", e))
|
||||
.and_then(
|
||||
HttpService::build()
|
||||
.h2(|_| {
|
||||
let body = once(Ok(Bytes::from_static(STR.as_ref())));
|
||||
ok::<_, ()>(
|
||||
Response::Ok()
|
||||
.body(body::SizedStream::new(STR.len() as u64, body)),
|
||||
)
|
||||
})
|
||||
.map_err(|_| ()),
|
||||
)
|
||||
});
|
||||
|
||||
let response = srv.block_on(srv.sget("/").send()).unwrap();
|
||||
assert!(response.status().is_success());
|
||||
|
||||
// read response
|
||||
let bytes = srv.load_body(response).unwrap();
|
||||
assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_h1_body_chunked_explicit() {
|
||||
let mut srv = TestServer::new(|| {
|
||||
|
@ -897,40 +539,6 @@ fn test_h1_body_chunked_explicit() {
|
|||
assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
|
||||
}
|
||||
|
||||
#[cfg(feature = "ssl")]
|
||||
#[test]
|
||||
fn test_h2_body_chunked_explicit() {
|
||||
let openssl = ssl_acceptor().unwrap();
|
||||
let mut srv = TestServer::new(move || {
|
||||
openssl
|
||||
.clone()
|
||||
.map_err(|e| println!("Openssl error: {}", e))
|
||||
.and_then(
|
||||
HttpService::build()
|
||||
.h2(|_| {
|
||||
let body =
|
||||
once::<_, Error>(Ok(Bytes::from_static(STR.as_ref())));
|
||||
ok::<_, ()>(
|
||||
Response::Ok()
|
||||
.header(header::TRANSFER_ENCODING, "chunked")
|
||||
.streaming(body),
|
||||
)
|
||||
})
|
||||
.map_err(|_| ()),
|
||||
)
|
||||
});
|
||||
|
||||
let response = srv.block_on(srv.sget("/").send()).unwrap();
|
||||
assert!(response.status().is_success());
|
||||
assert!(!response.headers().contains_key(header::TRANSFER_ENCODING));
|
||||
|
||||
// read response
|
||||
let bytes = srv.load_body(response).unwrap();
|
||||
|
||||
// decode
|
||||
assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_h1_body_chunked_implicit() {
|
||||
let mut srv = TestServer::new(|| {
|
||||
|
@ -980,39 +588,6 @@ fn test_h1_response_http_error_handling() {
|
|||
assert_eq!(bytes, Bytes::from_static(b"failed to parse header value"));
|
||||
}
|
||||
|
||||
#[cfg(feature = "ssl")]
|
||||
#[test]
|
||||
fn test_h2_response_http_error_handling() {
|
||||
let openssl = ssl_acceptor().unwrap();
|
||||
|
||||
let mut srv = TestServer::new(move || {
|
||||
openssl
|
||||
.clone()
|
||||
.map_err(|e| println!("Openssl error: {}", e))
|
||||
.and_then(
|
||||
HttpService::build()
|
||||
.h2(new_service_cfg(|_: &ServerConfig| {
|
||||
Ok::<_, ()>(|_| {
|
||||
let broken_header = Bytes::from_static(b"\0\0\0");
|
||||
ok::<_, ()>(
|
||||
Response::Ok()
|
||||
.header(http::header::CONTENT_TYPE, broken_header)
|
||||
.body(STR),
|
||||
)
|
||||
})
|
||||
}))
|
||||
.map_err(|_| ()),
|
||||
)
|
||||
});
|
||||
|
||||
let response = srv.block_on(srv.sget("/").send()).unwrap();
|
||||
assert_eq!(response.status(), http::StatusCode::INTERNAL_SERVER_ERROR);
|
||||
|
||||
// read response
|
||||
let bytes = srv.load_body(response).unwrap();
|
||||
assert_eq!(bytes, Bytes::from_static(b"failed to parse header value"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_h1_service_error() {
|
||||
let mut srv = TestServer::new(|| {
|
||||
|
@ -1027,27 +602,3 @@ fn test_h1_service_error() {
|
|||
let bytes = srv.load_body(response).unwrap();
|
||||
assert_eq!(bytes, Bytes::from_static(b"error"));
|
||||
}
|
||||
|
||||
#[cfg(feature = "ssl")]
|
||||
#[test]
|
||||
fn test_h2_service_error() {
|
||||
let openssl = ssl_acceptor().unwrap();
|
||||
|
||||
let mut srv = TestServer::new(move || {
|
||||
openssl
|
||||
.clone()
|
||||
.map_err(|e| println!("Openssl error: {}", e))
|
||||
.and_then(
|
||||
HttpService::build()
|
||||
.h2(|_| Err::<Response, Error>(error::ErrorBadRequest("error")))
|
||||
.map_err(|_| ()),
|
||||
)
|
||||
});
|
||||
|
||||
let response = srv.block_on(srv.sget("/").send()).unwrap();
|
||||
assert_eq!(response.status(), http::StatusCode::INTERNAL_SERVER_ERROR);
|
||||
|
||||
// read response
|
||||
let bytes = srv.load_body(response).unwrap();
|
||||
assert!(bytes.is_empty());
|
||||
}
|
||||
|
|
455
actix-http/tests/test_ssl_server.rs
Normal file
455
actix-http/tests/test_ssl_server.rs
Normal file
|
@ -0,0 +1,455 @@
|
|||
#![cfg(feature = "ssl")]
|
||||
use actix_codec::{AsyncRead, AsyncWrite};
|
||||
use actix_http::error::{ErrorBadRequest, PayloadError};
|
||||
use actix_http::http::header::{self, HeaderName, HeaderValue};
|
||||
use actix_http::http::{Method, StatusCode, Version};
|
||||
use actix_http::{body, Error, HttpService, Request, Response};
|
||||
use actix_http_test::TestServer;
|
||||
use actix_server::ssl::OpensslAcceptor;
|
||||
use actix_server_config::ServerConfig;
|
||||
use actix_service::{new_service_cfg, NewService};
|
||||
|
||||
use bytes::{Bytes, BytesMut};
|
||||
use futures::future::{ok, Future};
|
||||
use futures::stream::{once, Stream};
|
||||
use openssl::ssl::{AlpnError, SslAcceptor, SslFiletype, SslMethod};
|
||||
use std::io::Result;
|
||||
|
||||
fn load_body<S>(stream: S) -> impl Future<Item = BytesMut, Error = PayloadError>
|
||||
where
|
||||
S: Stream<Item = Bytes, Error = PayloadError>,
|
||||
{
|
||||
stream.fold(BytesMut::new(), move |mut body, chunk| {
|
||||
body.extend_from_slice(&chunk);
|
||||
Ok::<_, PayloadError>(body)
|
||||
})
|
||||
}
|
||||
|
||||
fn ssl_acceptor<T: AsyncRead + AsyncWrite>() -> Result<OpensslAcceptor<T, ()>> {
|
||||
// load ssl keys
|
||||
let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap();
|
||||
builder
|
||||
.set_private_key_file("../tests/key.pem", SslFiletype::PEM)
|
||||
.unwrap();
|
||||
builder
|
||||
.set_certificate_chain_file("../tests/cert.pem")
|
||||
.unwrap();
|
||||
builder.set_alpn_select_callback(|_, protos| {
|
||||
const H2: &[u8] = b"\x02h2";
|
||||
if protos.windows(3).any(|window| window == H2) {
|
||||
Ok(b"h2")
|
||||
} else {
|
||||
Err(AlpnError::NOACK)
|
||||
}
|
||||
});
|
||||
builder.set_alpn_protos(b"\x02h2")?;
|
||||
Ok(OpensslAcceptor::new(builder.build()))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_h2() -> Result<()> {
|
||||
let openssl = ssl_acceptor()?;
|
||||
let mut srv = TestServer::new(move || {
|
||||
openssl
|
||||
.clone()
|
||||
.map_err(|e| println!("Openssl error: {}", e))
|
||||
.and_then(
|
||||
HttpService::build()
|
||||
.h2(|_| ok::<_, Error>(Response::Ok().finish()))
|
||||
.map_err(|_| ()),
|
||||
)
|
||||
});
|
||||
|
||||
let response = srv.block_on(srv.sget("/").send()).unwrap();
|
||||
assert!(response.status().is_success());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_h2_1() -> Result<()> {
|
||||
let openssl = ssl_acceptor()?;
|
||||
let mut srv = TestServer::new(move || {
|
||||
openssl
|
||||
.clone()
|
||||
.map_err(|e| println!("Openssl error: {}", e))
|
||||
.and_then(
|
||||
HttpService::build()
|
||||
.finish(|req: Request| {
|
||||
assert!(req.peer_addr().is_some());
|
||||
assert_eq!(req.version(), Version::HTTP_2);
|
||||
ok::<_, Error>(Response::Ok().finish())
|
||||
})
|
||||
.map_err(|_| ()),
|
||||
)
|
||||
});
|
||||
|
||||
let response = srv.block_on(srv.sget("/").send()).unwrap();
|
||||
assert!(response.status().is_success());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_h2_body() -> Result<()> {
|
||||
let data = "HELLOWORLD".to_owned().repeat(64 * 1024);
|
||||
let openssl = ssl_acceptor()?;
|
||||
let mut srv = TestServer::new(move || {
|
||||
openssl
|
||||
.clone()
|
||||
.map_err(|e| println!("Openssl error: {}", e))
|
||||
.and_then(
|
||||
HttpService::build()
|
||||
.h2(|mut req: Request<_>| {
|
||||
load_body(req.take_payload())
|
||||
.and_then(|body| Ok(Response::Ok().body(body)))
|
||||
})
|
||||
.map_err(|_| ()),
|
||||
)
|
||||
});
|
||||
|
||||
let response = srv.block_on(srv.sget("/").send_body(data.clone())).unwrap();
|
||||
assert!(response.status().is_success());
|
||||
|
||||
let body = srv.load_body(response).unwrap();
|
||||
assert_eq!(&body, data.as_bytes());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_h2_content_length() {
|
||||
let openssl = ssl_acceptor().unwrap();
|
||||
|
||||
let mut srv = TestServer::new(move || {
|
||||
openssl
|
||||
.clone()
|
||||
.map_err(|e| println!("Openssl error: {}", e))
|
||||
.and_then(
|
||||
HttpService::build()
|
||||
.h2(|req: Request| {
|
||||
let indx: usize = req.uri().path()[1..].parse().unwrap();
|
||||
let statuses = [
|
||||
StatusCode::NO_CONTENT,
|
||||
StatusCode::CONTINUE,
|
||||
StatusCode::SWITCHING_PROTOCOLS,
|
||||
StatusCode::PROCESSING,
|
||||
StatusCode::OK,
|
||||
StatusCode::NOT_FOUND,
|
||||
];
|
||||
ok::<_, ()>(Response::new(statuses[indx]))
|
||||
})
|
||||
.map_err(|_| ()),
|
||||
)
|
||||
});
|
||||
|
||||
let header = HeaderName::from_static("content-length");
|
||||
let value = HeaderValue::from_static("0");
|
||||
|
||||
{
|
||||
for i in 0..4 {
|
||||
let req = srv
|
||||
.request(Method::GET, srv.surl(&format!("/{}", i)))
|
||||
.send();
|
||||
let response = srv.block_on(req).unwrap();
|
||||
assert_eq!(response.headers().get(&header), None);
|
||||
|
||||
let req = srv
|
||||
.request(Method::HEAD, srv.surl(&format!("/{}", i)))
|
||||
.send();
|
||||
let response = srv.block_on(req).unwrap();
|
||||
assert_eq!(response.headers().get(&header), None);
|
||||
}
|
||||
|
||||
for i in 4..6 {
|
||||
let req = srv
|
||||
.request(Method::GET, srv.surl(&format!("/{}", i)))
|
||||
.send();
|
||||
let response = srv.block_on(req).unwrap();
|
||||
assert_eq!(response.headers().get(&header), Some(&value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_h2_headers() {
|
||||
let data = STR.repeat(10);
|
||||
let data2 = data.clone();
|
||||
let openssl = ssl_acceptor().unwrap();
|
||||
|
||||
let mut srv = TestServer::new(move || {
|
||||
let data = data.clone();
|
||||
openssl
|
||||
.clone()
|
||||
.map_err(|e| println!("Openssl error: {}", e))
|
||||
.and_then(
|
||||
HttpService::build().h2(move |_| {
|
||||
let mut builder = Response::Ok();
|
||||
for idx in 0..90 {
|
||||
builder.header(
|
||||
format!("X-TEST-{}", idx).as_str(),
|
||||
"TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
||||
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
||||
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
||||
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
||||
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
||||
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
||||
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
||||
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
||||
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
||||
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
||||
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
||||
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
|
||||
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST ",
|
||||
);
|
||||
}
|
||||
ok::<_, ()>(builder.body(data.clone()))
|
||||
}).map_err(|_| ()))
|
||||
});
|
||||
|
||||
let response = srv.block_on(srv.sget("/").send()).unwrap();
|
||||
assert!(response.status().is_success());
|
||||
|
||||
// read response
|
||||
let bytes = srv.load_body(response).unwrap();
|
||||
assert_eq!(bytes, Bytes::from(data2));
|
||||
}
|
||||
|
||||
const STR: &str = "Hello World Hello World Hello World Hello World Hello World \
|
||||
Hello World Hello World Hello World Hello World Hello World \
|
||||
Hello World Hello World Hello World Hello World Hello World \
|
||||
Hello World Hello World Hello World Hello World Hello World \
|
||||
Hello World Hello World Hello World Hello World Hello World \
|
||||
Hello World Hello World Hello World Hello World Hello World \
|
||||
Hello World Hello World Hello World Hello World Hello World \
|
||||
Hello World Hello World Hello World Hello World Hello World \
|
||||
Hello World Hello World Hello World Hello World Hello World \
|
||||
Hello World Hello World Hello World Hello World Hello World \
|
||||
Hello World Hello World Hello World Hello World Hello World \
|
||||
Hello World Hello World Hello World Hello World Hello World \
|
||||
Hello World Hello World Hello World Hello World Hello World \
|
||||
Hello World Hello World Hello World Hello World Hello World \
|
||||
Hello World Hello World Hello World Hello World Hello World \
|
||||
Hello World Hello World Hello World Hello World Hello World \
|
||||
Hello World Hello World Hello World Hello World Hello World \
|
||||
Hello World Hello World Hello World Hello World Hello World \
|
||||
Hello World Hello World Hello World Hello World Hello World \
|
||||
Hello World Hello World Hello World Hello World Hello World \
|
||||
Hello World Hello World Hello World Hello World Hello World";
|
||||
|
||||
#[test]
|
||||
fn test_h2_body2() {
|
||||
let openssl = ssl_acceptor().unwrap();
|
||||
let mut srv = TestServer::new(move || {
|
||||
openssl
|
||||
.clone()
|
||||
.map_err(|e| println!("Openssl error: {}", e))
|
||||
.and_then(
|
||||
HttpService::build()
|
||||
.h2(|_| ok::<_, ()>(Response::Ok().body(STR)))
|
||||
.map_err(|_| ()),
|
||||
)
|
||||
});
|
||||
|
||||
let response = srv.block_on(srv.sget("/").send()).unwrap();
|
||||
assert!(response.status().is_success());
|
||||
|
||||
// read response
|
||||
let bytes = srv.load_body(response).unwrap();
|
||||
assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_h2_head_empty() {
|
||||
let openssl = ssl_acceptor().unwrap();
|
||||
let mut srv = TestServer::new(move || {
|
||||
openssl
|
||||
.clone()
|
||||
.map_err(|e| println!("Openssl error: {}", e))
|
||||
.and_then(
|
||||
HttpService::build()
|
||||
.finish(|_| ok::<_, ()>(Response::Ok().body(STR)))
|
||||
.map_err(|_| ()),
|
||||
)
|
||||
});
|
||||
|
||||
let response = srv.block_on(srv.shead("/").send()).unwrap();
|
||||
assert!(response.status().is_success());
|
||||
assert_eq!(response.version(), Version::HTTP_2);
|
||||
|
||||
{
|
||||
let len = response.headers().get(header::CONTENT_LENGTH).unwrap();
|
||||
assert_eq!(format!("{}", STR.len()), len.to_str().unwrap());
|
||||
}
|
||||
|
||||
// read response
|
||||
let bytes = srv.load_body(response).unwrap();
|
||||
assert!(bytes.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_h2_head_binary() {
|
||||
let openssl = ssl_acceptor().unwrap();
|
||||
let mut srv = TestServer::new(move || {
|
||||
openssl
|
||||
.clone()
|
||||
.map_err(|e| println!("Openssl error: {}", e))
|
||||
.and_then(
|
||||
HttpService::build()
|
||||
.h2(|_| {
|
||||
ok::<_, ()>(
|
||||
Response::Ok().content_length(STR.len() as u64).body(STR),
|
||||
)
|
||||
})
|
||||
.map_err(|_| ()),
|
||||
)
|
||||
});
|
||||
|
||||
let response = srv.block_on(srv.shead("/").send()).unwrap();
|
||||
assert!(response.status().is_success());
|
||||
|
||||
{
|
||||
let len = response.headers().get(header::CONTENT_LENGTH).unwrap();
|
||||
assert_eq!(format!("{}", STR.len()), len.to_str().unwrap());
|
||||
}
|
||||
|
||||
// read response
|
||||
let bytes = srv.load_body(response).unwrap();
|
||||
assert!(bytes.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_h2_head_binary2() {
|
||||
let openssl = ssl_acceptor().unwrap();
|
||||
let mut srv = TestServer::new(move || {
|
||||
openssl
|
||||
.clone()
|
||||
.map_err(|e| println!("Openssl error: {}", e))
|
||||
.and_then(
|
||||
HttpService::build()
|
||||
.h2(|_| ok::<_, ()>(Response::Ok().body(STR)))
|
||||
.map_err(|_| ()),
|
||||
)
|
||||
});
|
||||
|
||||
let response = srv.block_on(srv.shead("/").send()).unwrap();
|
||||
assert!(response.status().is_success());
|
||||
|
||||
{
|
||||
let len = response.headers().get(header::CONTENT_LENGTH).unwrap();
|
||||
assert_eq!(format!("{}", STR.len()), len.to_str().unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_h2_body_length() {
|
||||
let openssl = ssl_acceptor().unwrap();
|
||||
let mut srv = TestServer::new(move || {
|
||||
openssl
|
||||
.clone()
|
||||
.map_err(|e| println!("Openssl error: {}", e))
|
||||
.and_then(
|
||||
HttpService::build()
|
||||
.h2(|_| {
|
||||
let body = once(Ok(Bytes::from_static(STR.as_ref())));
|
||||
ok::<_, ()>(
|
||||
Response::Ok()
|
||||
.body(body::SizedStream::new(STR.len() as u64, body)),
|
||||
)
|
||||
})
|
||||
.map_err(|_| ()),
|
||||
)
|
||||
});
|
||||
|
||||
let response = srv.block_on(srv.sget("/").send()).unwrap();
|
||||
assert!(response.status().is_success());
|
||||
|
||||
// read response
|
||||
let bytes = srv.load_body(response).unwrap();
|
||||
assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_h2_body_chunked_explicit() {
|
||||
let openssl = ssl_acceptor().unwrap();
|
||||
let mut srv = TestServer::new(move || {
|
||||
openssl
|
||||
.clone()
|
||||
.map_err(|e| println!("Openssl error: {}", e))
|
||||
.and_then(
|
||||
HttpService::build()
|
||||
.h2(|_| {
|
||||
let body =
|
||||
once::<_, Error>(Ok(Bytes::from_static(STR.as_ref())));
|
||||
ok::<_, ()>(
|
||||
Response::Ok()
|
||||
.header(header::TRANSFER_ENCODING, "chunked")
|
||||
.streaming(body),
|
||||
)
|
||||
})
|
||||
.map_err(|_| ()),
|
||||
)
|
||||
});
|
||||
|
||||
let response = srv.block_on(srv.sget("/").send()).unwrap();
|
||||
assert!(response.status().is_success());
|
||||
assert!(!response.headers().contains_key(header::TRANSFER_ENCODING));
|
||||
|
||||
// read response
|
||||
let bytes = srv.load_body(response).unwrap();
|
||||
|
||||
// decode
|
||||
assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_h2_response_http_error_handling() {
|
||||
let openssl = ssl_acceptor().unwrap();
|
||||
|
||||
let mut srv = TestServer::new(move || {
|
||||
openssl
|
||||
.clone()
|
||||
.map_err(|e| println!("Openssl error: {}", e))
|
||||
.and_then(
|
||||
HttpService::build()
|
||||
.h2(new_service_cfg(|_: &ServerConfig| {
|
||||
Ok::<_, ()>(|_| {
|
||||
let broken_header = Bytes::from_static(b"\0\0\0");
|
||||
ok::<_, ()>(
|
||||
Response::Ok()
|
||||
.header(header::CONTENT_TYPE, broken_header)
|
||||
.body(STR),
|
||||
)
|
||||
})
|
||||
}))
|
||||
.map_err(|_| ()),
|
||||
)
|
||||
});
|
||||
|
||||
let response = srv.block_on(srv.sget("/").send()).unwrap();
|
||||
assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR);
|
||||
|
||||
// read response
|
||||
let bytes = srv.load_body(response).unwrap();
|
||||
assert_eq!(bytes, Bytes::from_static(b"failed to parse header value"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_h2_service_error() {
|
||||
let openssl = ssl_acceptor().unwrap();
|
||||
|
||||
let mut srv = TestServer::new(move || {
|
||||
openssl
|
||||
.clone()
|
||||
.map_err(|e| println!("Openssl error: {}", e))
|
||||
.and_then(
|
||||
HttpService::build()
|
||||
.h2(|_| Err::<Response, Error>(ErrorBadRequest("error")))
|
||||
.map_err(|_| ()),
|
||||
)
|
||||
});
|
||||
|
||||
let response = srv.block_on(srv.sget("/").send()).unwrap();
|
||||
assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR);
|
||||
|
||||
// read response
|
||||
let bytes = srv.load_body(response).unwrap();
|
||||
assert!(bytes.is_empty());
|
||||
}
|
|
@ -142,7 +142,6 @@ fn test_body() {
|
|||
assert!(response.status().is_success());
|
||||
assert_eq!(response.status(), http::StatusCode::NO_CONTENT);
|
||||
|
||||
let mut srv = TestServer::new(|| HttpService::new(App::new().service(auto_sync)));
|
||||
let request = srv.request(http::Method::GET, srv.url("/test"));
|
||||
let response = srv.block_on(request.send()).unwrap();
|
||||
assert!(response.status().is_success());
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
# Changes
|
||||
|
||||
## [0.2.3] - 2019-07-xx
|
||||
|
||||
* Add `rustls` support
|
||||
|
||||
## [0.2.2] - 2019-07-01
|
||||
|
||||
### Changed
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "awc"
|
||||
version = "0.2.2"
|
||||
version = "0.2.3"
|
||||
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
||||
description = "Actix http client."
|
||||
readme = "README.md"
|
||||
|
@ -29,6 +29,9 @@ default = ["brotli", "flate2-zlib"]
|
|||
# openssl
|
||||
ssl = ["openssl", "actix-http/ssl"]
|
||||
|
||||
# rustls
|
||||
rust-tls = ["rustls", "actix-http/rust-tls"]
|
||||
|
||||
# brotli encoding, requires c compiler
|
||||
brotli = ["actix-http/brotli"]
|
||||
|
||||
|
@ -41,7 +44,7 @@ flate2-rust = ["actix-http/flate2-rust"]
|
|||
[dependencies]
|
||||
actix-codec = "0.1.2"
|
||||
actix-service = "0.4.1"
|
||||
actix-http = "0.2.4"
|
||||
actix-http = "0.2.8"
|
||||
base64 = "0.10.1"
|
||||
bytes = "0.4"
|
||||
derive_more = "0.15.0"
|
||||
|
@ -55,6 +58,7 @@ serde_json = "1.0"
|
|||
serde_urlencoded = "0.5.3"
|
||||
tokio-timer = "0.2.8"
|
||||
openssl = { version="0.10", optional = true }
|
||||
rustls = { version = "0.15.2", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
actix-rt = "0.2.2"
|
||||
|
@ -62,9 +66,11 @@ actix-web = { version = "1.0.0", features=["ssl"] }
|
|||
actix-http = { version = "0.2.4", features=["ssl"] }
|
||||
actix-http-test = { version = "0.2.0", features=["ssl"] }
|
||||
actix-utils = "0.4.1"
|
||||
actix-server = { version = "0.5.1", features=["ssl"] }
|
||||
actix-server = { version = "0.6.0", features=["ssl", "rust-tls"] }
|
||||
brotli2 = { version="0.3.2" }
|
||||
flate2 = { version="1.0.2" }
|
||||
env_logger = "0.6"
|
||||
rand = "0.7"
|
||||
tokio-tcp = "0.1"
|
||||
webpki = "0.19"
|
||||
rustls = { version = "0.15.2", features = ["dangerous_configuration"] }
|
||||
|
|
|
@ -12,11 +12,10 @@ use flate2::Compression;
|
|||
use futures::Future;
|
||||
use rand::Rng;
|
||||
|
||||
use actix_codec::{AsyncRead, AsyncWrite};
|
||||
use actix_http::HttpService;
|
||||
use actix_http_test::TestServer;
|
||||
use actix_service::{service_fn, NewService};
|
||||
use actix_web::http::{Cookie, Version};
|
||||
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;
|
||||
|
@ -43,30 +42,6 @@ const STR: &str = "Hello World Hello World Hello World Hello World Hello World \
|
|||
Hello World Hello World Hello World Hello World Hello World \
|
||||
Hello World Hello World Hello World Hello World Hello World";
|
||||
|
||||
#[cfg(feature = "ssl")]
|
||||
fn ssl_acceptor<T: AsyncRead + AsyncWrite>(
|
||||
) -> std::io::Result<actix_server::ssl::OpensslAcceptor<T, ()>> {
|
||||
use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod};
|
||||
// load ssl keys
|
||||
let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap();
|
||||
builder
|
||||
.set_private_key_file("../tests/key.pem", SslFiletype::PEM)
|
||||
.unwrap();
|
||||
builder
|
||||
.set_certificate_chain_file("../tests/cert.pem")
|
||||
.unwrap();
|
||||
builder.set_alpn_select_callback(|_, protos| {
|
||||
const H2: &[u8] = b"\x02h2";
|
||||
if protos.windows(3).any(|window| window == H2) {
|
||||
Ok(b"h2")
|
||||
} else {
|
||||
Err(openssl::ssl::AlpnError::NOACK)
|
||||
}
|
||||
});
|
||||
builder.set_alpn_protos(b"\x02h2")?;
|
||||
Ok(actix_server::ssl::OpensslAcceptor::new(builder.build()))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_simple() {
|
||||
let mut srv =
|
||||
|
@ -207,60 +182,6 @@ fn test_connection_reuse() {
|
|||
assert_eq!(num.load(Ordering::Relaxed), 1);
|
||||
}
|
||||
|
||||
#[cfg(feature = "ssl")]
|
||||
#[test]
|
||||
fn test_connection_reuse_h2() {
|
||||
let openssl = ssl_acceptor().unwrap();
|
||||
let num = Arc::new(AtomicUsize::new(0));
|
||||
let num2 = num.clone();
|
||||
|
||||
let mut srv = TestServer::new(move || {
|
||||
let num2 = num2.clone();
|
||||
service_fn(move |io| {
|
||||
num2.fetch_add(1, Ordering::Relaxed);
|
||||
Ok(io)
|
||||
})
|
||||
.and_then(
|
||||
openssl
|
||||
.clone()
|
||||
.map_err(|e| println!("Openssl error: {}", e)),
|
||||
)
|
||||
.and_then(
|
||||
HttpService::build()
|
||||
.h2(App::new()
|
||||
.service(web::resource("/").route(web::to(|| HttpResponse::Ok()))))
|
||||
.map_err(|_| ()),
|
||||
)
|
||||
});
|
||||
|
||||
// disable ssl verification
|
||||
use openssl::ssl::{SslConnector, SslMethod, SslVerifyMode};
|
||||
|
||||
let mut builder = SslConnector::builder(SslMethod::tls()).unwrap();
|
||||
builder.set_verify(SslVerifyMode::NONE);
|
||||
let _ = builder
|
||||
.set_alpn_protos(b"\x02h2\x08http/1.1")
|
||||
.map_err(|e| log::error!("Can not set alpn protocol: {:?}", e));
|
||||
|
||||
let client = awc::Client::build()
|
||||
.connector(awc::Connector::new().ssl(builder.build()).finish())
|
||||
.finish();
|
||||
|
||||
// req 1
|
||||
let request = client.get(srv.surl("/")).send();
|
||||
let response = srv.block_on(request).unwrap();
|
||||
assert!(response.status().is_success());
|
||||
|
||||
// req 2
|
||||
let req = client.post(srv.surl("/"));
|
||||
let response = srv.block_on_fn(move || req.send()).unwrap();
|
||||
assert!(response.status().is_success());
|
||||
assert_eq!(response.version(), Version::HTTP_2);
|
||||
|
||||
// one connection
|
||||
assert_eq!(num.load(Ordering::Relaxed), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_connection_force_close() {
|
||||
let num = Arc::new(AtomicUsize::new(0));
|
||||
|
|
96
awc/tests/test_rustls_client.rs
Normal file
96
awc/tests/test_rustls_client.rs
Normal file
|
@ -0,0 +1,96 @@
|
|||
#![cfg(feature = "rust-tls")]
|
||||
use rustls::{
|
||||
internal::pemfile::{certs, pkcs8_private_keys},
|
||||
ClientConfig, NoClientAuth,
|
||||
};
|
||||
|
||||
use std::fs::File;
|
||||
use std::io::{BufReader, Result};
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
||||
use actix_codec::{AsyncRead, AsyncWrite};
|
||||
use actix_http::HttpService;
|
||||
use actix_http_test::TestServer;
|
||||
use actix_server::ssl::RustlsAcceptor;
|
||||
use actix_service::{service_fn, NewService};
|
||||
use actix_web::http::Version;
|
||||
use actix_web::{web, App, HttpResponse};
|
||||
|
||||
fn ssl_acceptor<T: AsyncRead + AsyncWrite>() -> Result<RustlsAcceptor<T, ()>> {
|
||||
use rustls::ServerConfig;
|
||||
// load ssl keys
|
||||
let mut config = ServerConfig::new(NoClientAuth::new());
|
||||
let cert_file = &mut BufReader::new(File::open("../tests/cert.pem").unwrap());
|
||||
let key_file = &mut BufReader::new(File::open("../tests/key.pem").unwrap());
|
||||
let cert_chain = certs(cert_file).unwrap();
|
||||
let mut keys = pkcs8_private_keys(key_file).unwrap();
|
||||
config.set_single_cert(cert_chain, keys.remove(0)).unwrap();
|
||||
let protos = vec![b"h2".to_vec()];
|
||||
config.set_protocols(&protos);
|
||||
Ok(RustlsAcceptor::new(config))
|
||||
}
|
||||
|
||||
mod danger {
|
||||
pub struct NoCertificateVerification {}
|
||||
|
||||
impl rustls::ServerCertVerifier for NoCertificateVerification {
|
||||
fn verify_server_cert(
|
||||
&self,
|
||||
_roots: &rustls::RootCertStore,
|
||||
_presented_certs: &[rustls::Certificate],
|
||||
_dns_name: webpki::DNSNameRef<'_>,
|
||||
_ocsp: &[u8],
|
||||
) -> Result<rustls::ServerCertVerified, rustls::TLSError> {
|
||||
Ok(rustls::ServerCertVerified::assertion())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_connection_reuse_h2() {
|
||||
let rustls = ssl_acceptor().unwrap();
|
||||
let num = Arc::new(AtomicUsize::new(0));
|
||||
let num2 = num.clone();
|
||||
|
||||
let mut srv = TestServer::new(move || {
|
||||
let num2 = num2.clone();
|
||||
service_fn(move |io| {
|
||||
num2.fetch_add(1, Ordering::Relaxed);
|
||||
Ok(io)
|
||||
})
|
||||
.and_then(rustls.clone().map_err(|e| println!("Rustls error: {}", e)))
|
||||
.and_then(
|
||||
HttpService::build()
|
||||
.h2(App::new()
|
||||
.service(web::resource("/").route(web::to(|| HttpResponse::Ok()))))
|
||||
.map_err(|_| ()),
|
||||
)
|
||||
});
|
||||
|
||||
// disable ssl verification
|
||||
let mut config = ClientConfig::new();
|
||||
let protos = vec![b"h2".to_vec(), b"http/1.1".to_vec()];
|
||||
config.set_protocols(&protos);
|
||||
config
|
||||
.dangerous()
|
||||
.set_certificate_verifier(Arc::new(danger::NoCertificateVerification {}));
|
||||
|
||||
let client = awc::Client::build()
|
||||
.connector(awc::Connector::new().rustls(Arc::new(config)).finish())
|
||||
.finish();
|
||||
|
||||
// req 1
|
||||
let request = client.get(srv.surl("/")).send();
|
||||
let response = srv.block_on(request).unwrap();
|
||||
assert!(response.status().is_success());
|
||||
|
||||
// req 2
|
||||
let req = client.post(srv.surl("/"));
|
||||
let response = srv.block_on_fn(move || req.send()).unwrap();
|
||||
assert!(response.status().is_success());
|
||||
assert_eq!(response.version(), Version::HTTP_2);
|
||||
|
||||
// one connection
|
||||
assert_eq!(num.load(Ordering::Relaxed), 1);
|
||||
}
|
86
awc/tests/test_ssl_client.rs
Normal file
86
awc/tests/test_ssl_client.rs
Normal file
|
@ -0,0 +1,86 @@
|
|||
#![cfg(feature = "ssl")]
|
||||
use openssl::ssl::{SslAcceptor, SslConnector, SslFiletype, SslMethod, SslVerifyMode};
|
||||
|
||||
use std::io::Result;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
||||
use actix_codec::{AsyncRead, AsyncWrite};
|
||||
use actix_http::HttpService;
|
||||
use actix_http_test::TestServer;
|
||||
use actix_server::ssl::OpensslAcceptor;
|
||||
use actix_service::{service_fn, NewService};
|
||||
use actix_web::http::Version;
|
||||
use actix_web::{web, App, HttpResponse};
|
||||
|
||||
fn ssl_acceptor<T: AsyncRead + AsyncWrite>() -> Result<OpensslAcceptor<T, ()>> {
|
||||
// load ssl keys
|
||||
let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap();
|
||||
builder
|
||||
.set_private_key_file("../tests/key.pem", SslFiletype::PEM)
|
||||
.unwrap();
|
||||
builder
|
||||
.set_certificate_chain_file("../tests/cert.pem")
|
||||
.unwrap();
|
||||
builder.set_alpn_select_callback(|_, protos| {
|
||||
const H2: &[u8] = b"\x02h2";
|
||||
if protos.windows(3).any(|window| window == H2) {
|
||||
Ok(b"h2")
|
||||
} else {
|
||||
Err(openssl::ssl::AlpnError::NOACK)
|
||||
}
|
||||
});
|
||||
builder.set_alpn_protos(b"\x02h2")?;
|
||||
Ok(actix_server::ssl::OpensslAcceptor::new(builder.build()))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_connection_reuse_h2() {
|
||||
let openssl = ssl_acceptor().unwrap();
|
||||
let num = Arc::new(AtomicUsize::new(0));
|
||||
let num2 = num.clone();
|
||||
|
||||
let mut srv = TestServer::new(move || {
|
||||
let num2 = num2.clone();
|
||||
service_fn(move |io| {
|
||||
num2.fetch_add(1, Ordering::Relaxed);
|
||||
Ok(io)
|
||||
})
|
||||
.and_then(
|
||||
openssl
|
||||
.clone()
|
||||
.map_err(|e| println!("Openssl error: {}", e)),
|
||||
)
|
||||
.and_then(
|
||||
HttpService::build()
|
||||
.h2(App::new()
|
||||
.service(web::resource("/").route(web::to(|| HttpResponse::Ok()))))
|
||||
.map_err(|_| ()),
|
||||
)
|
||||
});
|
||||
|
||||
// disable ssl verification
|
||||
let mut builder = SslConnector::builder(SslMethod::tls()).unwrap();
|
||||
builder.set_verify(SslVerifyMode::NONE);
|
||||
let _ = builder
|
||||
.set_alpn_protos(b"\x02h2\x08http/1.1")
|
||||
.map_err(|e| log::error!("Can not set alpn protocol: {:?}", e));
|
||||
|
||||
let client = awc::Client::build()
|
||||
.connector(awc::Connector::new().ssl(builder.build()).finish())
|
||||
.finish();
|
||||
|
||||
// req 1
|
||||
let request = client.get(srv.surl("/")).send();
|
||||
let response = srv.block_on(request).unwrap();
|
||||
assert!(response.status().is_success());
|
||||
|
||||
// req 2
|
||||
let req = client.post(srv.surl("/"));
|
||||
let response = srv.block_on_fn(move || req.send()).unwrap();
|
||||
assert!(response.status().is_success());
|
||||
assert_eq!(response.version(), Version::HTTP_2);
|
||||
|
||||
// one connection
|
||||
assert_eq!(num.load(Ordering::Relaxed), 1);
|
||||
}
|
|
@ -20,6 +20,7 @@ fn no_params() -> &'static str {
|
|||
"Hello world!\r\n"
|
||||
}
|
||||
|
||||
#[cfg(feature = "uds")]
|
||||
fn main() -> std::io::Result<()> {
|
||||
std::env::set_var("RUST_LOG", "actix_server=info,actix_web=info");
|
||||
env_logger::init();
|
||||
|
@ -47,3 +48,6 @@ fn main() -> std::io::Result<()> {
|
|||
.workers(1)
|
||||
.run()
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "uds"))]
|
||||
fn main() {}
|
||||
|
|
|
@ -36,6 +36,7 @@ actix-service = "0.4.1"
|
|||
actix-server = "0.6.0"
|
||||
actix-utils = "0.4.1"
|
||||
awc = "0.2.2"
|
||||
actix-connect = "0.2.2"
|
||||
|
||||
base64 = "0.10"
|
||||
bytes = "0.4"
|
||||
|
|
|
@ -163,6 +163,10 @@ impl TestServer {
|
|||
Ok::<Client, ()>(Client::build().connector(connector).finish())
|
||||
}))
|
||||
.unwrap();
|
||||
rt.block_on(lazy(
|
||||
|| Ok::<_, ()>(actix_connect::start_default_resolver()),
|
||||
))
|
||||
.unwrap();
|
||||
System::set_current(system);
|
||||
TestServerRuntime { addr, rt, client }
|
||||
}
|
||||
|
@ -212,18 +216,18 @@ impl TestServerRuntime {
|
|||
/// Construct test server url
|
||||
pub fn url(&self, uri: &str) -> String {
|
||||
if uri.starts_with('/') {
|
||||
format!("http://127.0.0.1:{}{}", self.addr.port(), uri)
|
||||
format!("http://localhost:{}{}", self.addr.port(), uri)
|
||||
} else {
|
||||
format!("http://127.0.0.1:{}/{}", self.addr.port(), uri)
|
||||
format!("http://localhost:{}/{}", self.addr.port(), uri)
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct test https server url
|
||||
pub fn surl(&self, uri: &str) -> String {
|
||||
if uri.starts_with('/') {
|
||||
format!("https://127.0.0.1:{}{}", self.addr.port(), uri)
|
||||
format!("https://localhost:{}{}", self.addr.port(), uri)
|
||||
} else {
|
||||
format!("https://127.0.0.1:{}/{}", self.addr.port(), uri)
|
||||
format!("https://localhost:{}/{}", self.addr.port(), uri)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,20 +1,32 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIDPjCCAiYCCQCmkoCBehOyYTANBgkqhkiG9w0BAQsFADBhMQswCQYDVQQGEwJV
|
||||
UzELMAkGA1UECAwCQ0ExCzAJBgNVBAcMAlNGMRAwDgYDVQQKDAdDb21wYW55MQww
|
||||
CgYDVQQLDANPcmcxGDAWBgNVBAMMD3d3dy5leGFtcGxlLmNvbTAeFw0xOTAzMjky
|
||||
MzE5MDlaFw0yMDAzMjgyMzE5MDlaMGExCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJD
|
||||
QTELMAkGA1UEBwwCU0YxEDAOBgNVBAoMB0NvbXBhbnkxDDAKBgNVBAsMA09yZzEY
|
||||
MBYGA1UEAwwPd3d3LmV4YW1wbGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
|
||||
MIIBCgKCAQEA2uFoWm74qumqIIsBBf/rgP3ZtZw6dRQhVoYjIwYk00T1RLmmbt8r
|
||||
YNh3lehmnrQlM/YC3dzcspucGqIfvs5FEReh/vgvsqY3lfy47Q1zzdtBrKq2ZBro
|
||||
AuJUe4ayMYz/L/2jAtPtGDQqWyzhKv6x/oz6N/tKqlzoGbjSGSJUqKAV+Tuo4YI4
|
||||
xw3r/RJg3I3+ruXOgM65GBdja7usI/BhseEOp9VXotoTEItGmvG2RFZ4A7cN124x
|
||||
giFl2IeYuC60jteZ+bnhPiqxcdzf3K4dnZlzrYma+FxwWbaow4wlpQcZVFdZ+K/Y
|
||||
p/Bbm/FDKoUHnEdn/QAanTruRxSGdai0owIDAQABMA0GCSqGSIb3DQEBCwUAA4IB
|
||||
AQAEWn3WAwAbd64f5jo2w4076s2qFiCJjPWoxO6bO75FgFFtw/NNev8pxGVw1ehg
|
||||
HiTO6VRYolL5S/RKOchjA83AcDEBjgf8fKtvTmE9kxZSUIo4kIvv8V9ZM72gJhDN
|
||||
8D/lXduTZ9JMwLOa1NUB8/I6CbaU3VzWkfodArKKpQF3M+LLgK03i12PD0KPQ5zv
|
||||
bwaNoQo6cTmPNIdsVZETRvPqONiCUaQV57G74dGtjeirCh/DO5EYRtb1thgS7TGm
|
||||
+Xg8OC5vZ6g0+xsrSqDBmWNtlI7S3bsL5C3LIEOOAL1ZJHRy2KvIGQ9ipb3XjnKS
|
||||
N7/wlQduRyPH7oaD/o4xf5Gt
|
||||
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
|
||||
+xh1SDNjFLRd6sGzZHPMgXN0
|
||||
-----END CERTIFICATE-----
|
||||
|
|
|
@ -1,27 +1,52 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpQIBAAKCAQEA2uFoWm74qumqIIsBBf/rgP3ZtZw6dRQhVoYjIwYk00T1RLmm
|
||||
bt8rYNh3lehmnrQlM/YC3dzcspucGqIfvs5FEReh/vgvsqY3lfy47Q1zzdtBrKq2
|
||||
ZBroAuJUe4ayMYz/L/2jAtPtGDQqWyzhKv6x/oz6N/tKqlzoGbjSGSJUqKAV+Tuo
|
||||
4YI4xw3r/RJg3I3+ruXOgM65GBdja7usI/BhseEOp9VXotoTEItGmvG2RFZ4A7cN
|
||||
124xgiFl2IeYuC60jteZ+bnhPiqxcdzf3K4dnZlzrYma+FxwWbaow4wlpQcZVFdZ
|
||||
+K/Yp/Bbm/FDKoUHnEdn/QAanTruRxSGdai0owIDAQABAoIBAQC4lzyQd+ITEbi+
|
||||
dTxJuQj94hgHB1htgKqU888SLI5F9nP6n67y9hb5N9WygSp6UWbGqYTFYwxlPMKr
|
||||
22p2WjL5NTsTcm+XdIKQZW/3y06Mn4qFefsT9XURaZriCjihfU2BRaCCNARSUzwd
|
||||
ZH4I6n9mM7KaH71aa7v6ZVoahE9tXPR6hM+SHQEySW4pWkEu98VpNNeIt6vP7WF9
|
||||
ONGbRa+0En4xgkuaxem2ZYa/GZFFtdQRkroNMhIRlfcPpkjy8DCc8E5RAkOzKC3O
|
||||
lnxQwt+tdNNkGZz02ed2hx/YHPwFYy76y6hK5dxq74iKIaOc8U5t0HjB1zVfwiR0
|
||||
5mcxMncxAoGBAP+RivwXZ4FcxDY1uyziF+rwlC/1RujQFEWXIxsXCnba5DH3yKul
|
||||
iKEIZPZtGhpsnQe367lcXcn7tztuoVjpAnk5L+hQY64tLwYbHeRcOMJ75C2y8FFC
|
||||
NeG5sQsrk3IU1+jhGvrbE7UgOeAuWJmv0M1vPNB/+hGoZBW5W5uU1x89AoGBANtA
|
||||
AhLtAcqQ/Qh2SpVhLljh7U85Be9tbCGua09clkYFzh3bcoBolXKH18Veww0TP0yF
|
||||
0748CKw1A+ITbTVFV+vKvi4jzIxS7mr4wYtVCMssbttQN7y3l30IDxJwa9j3zTJx
|
||||
IUn5OMMLv1JyitLId8HdOy1AdU3MkpJzdLyi1mFfAoGBAL3kL4fGABM/kU7SN6RO
|
||||
zgS0AvdrYOeljBp1BRGg2hab58g02vamxVEZgqMTR7zwjPDqOIz+03U7wda4Ccyd
|
||||
PUhDNJSB/r6xNepshZZi642eLlnCRguqjYyNw72QADtYv2B6uehAlXEUY8xtw0lW
|
||||
OGgcSeyF2pH6M3tswWNlgT3lAoGAQ/BttBelOnP7NKgTLH7Usc4wjyAIaszpePZn
|
||||
Ykw6dLBP0oixzoCZ7seRYSOgJWkVcEz39Db+KP60mVWTvbIjMHm+vOVy+Pip0JQM
|
||||
xXQwKWU3ZNZSrzPkyWW55ejYQn9nIn5T5mxH3ojBXHcJ9Y8RLQ20zKzwrI77zE3i
|
||||
mqGK9NkCgYEAq3dzHI0DGAJrR19sWl2LcqI19sj5a91tHx4cl1dJXS/iApOLLieU
|
||||
zyUGkwfsqjHPAZ7GacICeBojIn/7KdPdlSKAbGVAU3d4qzvFS0qmWzObplBz3niT
|
||||
Xnep2XLaVXqwlFJZZ6AHeKzYmMH0d0raiou2bpEUBqYizy2fi3NI4mA=
|
||||
-----END RSA PRIVATE KEY-----
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
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
|
||||
o21xtGh0nP2Fq1TC6jFO+9cpbK8jNA==
|
||||
-----END PRIVATE KEY-----
|
||||
|
|
|
@ -16,6 +16,7 @@ use flate2::Compression;
|
|||
use futures::stream::once;
|
||||
use rand::{distributions::Alphanumeric, Rng};
|
||||
|
||||
use actix_connect::start_default_resolver;
|
||||
use actix_web::middleware::{BodyEncoding, Compress};
|
||||
use actix_web::{dev, http, test, web, App, HttpResponse, HttpServer};
|
||||
|
||||
|
@ -782,7 +783,7 @@ fn test_brotli_encoding_large() {
|
|||
#[test]
|
||||
fn test_reading_deflate_encoding_large_random_ssl() {
|
||||
use openssl::ssl::{SslConnector, SslMethod, SslVerifyMode};
|
||||
use rustls::internal::pemfile::{certs, rsa_private_keys};
|
||||
use rustls::internal::pemfile::{certs, pkcs8_private_keys};
|
||||
use rustls::{NoClientAuth, ServerConfig};
|
||||
use std::fs::File;
|
||||
use std::io::BufReader;
|
||||
|
@ -803,7 +804,7 @@ fn test_reading_deflate_encoding_large_random_ssl() {
|
|||
let cert_file = &mut BufReader::new(File::open("tests/cert.pem").unwrap());
|
||||
let key_file = &mut BufReader::new(File::open("tests/key.pem").unwrap());
|
||||
let cert_chain = certs(cert_file).unwrap();
|
||||
let mut keys = rsa_private_keys(key_file).unwrap();
|
||||
let mut keys = pkcs8_private_keys(key_file).unwrap();
|
||||
config.set_single_cert(cert_chain, keys.remove(0)).unwrap();
|
||||
|
||||
let srv = HttpServer::new(|| {
|
||||
|
@ -823,6 +824,7 @@ fn test_reading_deflate_encoding_large_random_ssl() {
|
|||
let _ = sys.run();
|
||||
});
|
||||
let (srv, _sys) = rx.recv().unwrap();
|
||||
test::block_on(futures::lazy(|| Ok::<_, ()>(start_default_resolver()))).unwrap();
|
||||
let client = test::run_on(|| {
|
||||
let mut builder = SslConnector::builder(SslMethod::tls()).unwrap();
|
||||
builder.set_verify(SslVerifyMode::NONE);
|
||||
|
@ -844,19 +846,18 @@ fn test_reading_deflate_encoding_large_random_ssl() {
|
|||
let enc = e.finish().unwrap();
|
||||
|
||||
// client request
|
||||
let _req = client
|
||||
.post(format!("https://{}/", addr))
|
||||
let req = client
|
||||
.post(format!("https://localhost:{}/", addr.port()))
|
||||
.header(http::header::CONTENT_ENCODING, "deflate")
|
||||
.send_body(enc);
|
||||
|
||||
// TODO: fix
|
||||
// let response = test::block_on(req).unwrap();
|
||||
// assert!(response.status().is_success());
|
||||
let mut response = test::block_on(req).unwrap();
|
||||
assert!(response.status().is_success());
|
||||
|
||||
// read response
|
||||
// let bytes = test::block_on(response.body()).unwrap();
|
||||
// assert_eq!(bytes.len(), data.len());
|
||||
// assert_eq!(bytes, Bytes::from(data));
|
||||
let bytes = test::block_on(response.body()).unwrap();
|
||||
assert_eq!(bytes.len(), data.len());
|
||||
assert_eq!(bytes, Bytes::from(data));
|
||||
|
||||
// stop
|
||||
let _ = srv.stop(false);
|
||||
|
|
Loading…
Reference in a new issue