From 1ffa7d18d37e293e7659562d77059b44de38d122 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Tue, 19 Nov 2019 18:54:19 +0600 Subject: [PATCH] drop unpin constraint --- actix-http/Cargo.toml | 3 +- actix-http/examples/echo.rs | 34 +- actix-http/examples/echo2.rs | 29 +- actix-http/src/body.rs | 73 ++- actix-http/src/builder.rs | 55 +- actix-http/src/client/connection.rs | 60 +- actix-http/src/client/connector.rs | 129 +--- actix-http/src/client/error.rs | 8 +- actix-http/src/client/pool.rs | 35 +- actix-http/src/config.rs | 2 +- actix-http/src/error.rs | 20 +- actix-http/src/h1/dispatcher.rs | 103 +-- actix-http/src/h1/payload.rs | 27 +- actix-http/src/h1/service.rs | 95 +-- actix-http/src/h1/utils.rs | 3 +- actix-http/src/h2/dispatcher.rs | 93 ++- actix-http/src/h2/service.rs | 59 +- actix-http/src/request.rs | 5 + actix-http/src/response.rs | 4 +- actix-http/src/service.rs | 262 ++++---- actix-http/src/ws/transport.rs | 14 +- actix-http/tests/test_client.rs | 88 +-- actix-http/tests/test_openssl.rs | 545 ++++++++++++++++ actix-http/tests/test_rustls.rs | 474 ++++++++++++++ actix-http/tests/test_rustls_server.rs | 462 ------------- actix-http/tests/test_server.rs | 855 +++++++++++++------------ actix-http/tests/test_ssl_server.rs | 480 -------------- actix-http/tests/test_ws.rs | 100 +-- test-server/src/lib.rs | 37 +- tests/cert.pem | 47 +- tests/key.pem | 76 +-- 31 files changed, 2136 insertions(+), 2141 deletions(-) create mode 100644 actix-http/tests/test_openssl.rs create mode 100644 actix-http/tests/test_rustls.rs delete mode 100644 actix-http/tests/test_rustls_server.rs delete mode 100644 actix-http/tests/test_ssl_server.rs diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index 36f3cef4..edc93f09 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -74,6 +74,7 @@ language-tags = "0.2" log = "0.4" mime = "0.3" percent-encoding = "2.1" +pin-project = "0.4.5" rand = "0.7" regex = "1.0" serde = "1.0" @@ -107,7 +108,7 @@ webpki-roots = { version = "0.18", optional = true } [dev-dependencies] actix-rt = "1.0.0-alpha.1" -actix-server = { version = "0.8.0-alpha.1", features=["openssl"] } +actix-server = { version = "0.8.0-alpha.1", features=["openssl", "rustls"] } actix-connect = { version = "1.0.0-alpha.1", features=["openssl"] } actix-http-test = { version = "0.3.0-alpha.1", features=["openssl"] } env_logger = "0.6" diff --git a/actix-http/examples/echo.rs b/actix-http/examples/echo.rs index c36292c4..ba81020c 100644 --- a/actix-http/examples/echo.rs +++ b/actix-http/examples/echo.rs @@ -1,9 +1,9 @@ use std::{env, io}; -use actix_http::{error::PayloadError, HttpService, Request, Response}; +use actix_http::{Error, HttpService, Request, Response}; use actix_server::Server; use bytes::BytesMut; -use futures::{Future, Stream}; +use futures::StreamExt; use http::header::HeaderValue; use log::info; @@ -17,20 +17,22 @@ fn main() -> io::Result<()> { .client_timeout(1000) .client_disconnect(1000) .finish(|mut req: Request| { - req.take_payload() - .fold(BytesMut::new(), move |mut body, chunk| { - body.extend_from_slice(&chunk); - Ok::<_, PayloadError>(body) - }) - .and_then(|bytes| { - info!("request body: {:?}", bytes); - let mut res = Response::Ok(); - res.header( - "x-head", - HeaderValue::from_static("dummy value!"), - ); - Ok(res.body(bytes)) - }) + async move { + let mut body = BytesMut::new(); + while let Some(item) = req.payload().next().await { + body.extend_from_slice(&item?); + } + + info!("request body: {:?}", body); + Ok::<_, Error>( + Response::Ok() + .header( + "x-head", + HeaderValue::from_static("dummy value!"), + ) + .body(body), + ) + } }) })? .run() diff --git a/actix-http/examples/echo2.rs b/actix-http/examples/echo2.rs index b239796b..3776c7d5 100644 --- a/actix-http/examples/echo2.rs +++ b/actix-http/examples/echo2.rs @@ -1,25 +1,22 @@ use std::{env, io}; use actix_http::http::HeaderValue; -use actix_http::{error::PayloadError, Error, HttpService, Request, Response}; +use actix_http::{Error, HttpService, Request, Response}; use actix_server::Server; use bytes::BytesMut; -use futures::{Future, Stream}; +use futures::StreamExt; use log::info; -fn handle_request(mut req: Request) -> impl Future { - req.take_payload() - .fold(BytesMut::new(), move |mut body, chunk| { - body.extend_from_slice(&chunk); - Ok::<_, PayloadError>(body) - }) - .from_err() - .and_then(|bytes| { - info!("request body: {:?}", bytes); - let mut res = Response::Ok(); - res.header("x-head", HeaderValue::from_static("dummy value!")); - Ok(res.body(bytes)) - }) +async fn handle_request(mut req: Request) -> Result { + let mut body = BytesMut::new(); + while let Some(item) = req.payload().next().await { + body.extend_from_slice(&item?) + } + + info!("request body: {:?}", body); + Ok(Response::Ok() + .header("x-head", HeaderValue::from_static("dummy value!")) + .body(body)) } fn main() -> io::Result<()> { @@ -28,7 +25,7 @@ fn main() -> io::Result<()> { Server::build() .bind("echo", "127.0.0.1:8080", || { - HttpService::build().finish(|_req: Request| handle_request(_req)) + HttpService::build().finish(handle_request) })? .run() } diff --git a/actix-http/src/body.rs b/actix-http/src/body.rs index 44e76ae0..1d3a43fe 100644 --- a/actix-http/src/body.rs +++ b/actix-http/src/body.rs @@ -5,6 +5,7 @@ use std::{fmt, mem}; use bytes::{Bytes, BytesMut}; use futures::Stream; +use pin_project::{pin_project, project}; use crate::error::Error; @@ -31,7 +32,7 @@ impl BodySize { } /// Type that provides this trait can be streamed to a peer. -pub trait MessageBody: Unpin { +pub trait MessageBody { fn size(&self) -> BodySize; fn poll_next(&mut self, cx: &mut Context) -> Poll>>; @@ -57,6 +58,7 @@ impl MessageBody for Box { } } +#[pin_project] pub enum ResponseBody { Body(B), Other(Body), @@ -106,8 +108,13 @@ impl MessageBody for ResponseBody { impl Stream for ResponseBody { type Item = Result; + #[project] fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - self.get_mut().poll_next(cx) + #[project] + match self.project() { + ResponseBody::Body(ref mut body) => body.poll_next(cx), + ResponseBody::Other(ref mut body) => body.poll_next(cx), + } } } @@ -243,7 +250,7 @@ impl From for Body { impl From> for Body where - S: Stream> + Unpin + 'static, + S: Stream> + 'static, { fn from(s: SizedStream) -> Body { Body::from_message(s) @@ -252,7 +259,7 @@ where impl From> for Body where - S: Stream> + Unpin + 'static, + S: Stream> + 'static, E: Into + 'static, { fn from(s: BodyStream) -> Body { @@ -350,7 +357,9 @@ impl MessageBody for String { /// Type represent streaming body. /// Response does not contain `content-length` header and appropriate transfer encoding is used. +#[pin_project] pub struct BodyStream { + #[pin] stream: S, _t: PhantomData, } @@ -368,16 +377,9 @@ where } } -impl Unpin for BodyStream -where - S: Stream> + Unpin, - E: Into, -{ -} - impl MessageBody for BodyStream where - S: Stream> + Unpin, + S: Stream>, E: Into, { fn size(&self) -> BodySize { @@ -385,7 +387,9 @@ where } fn poll_next(&mut self, cx: &mut Context) -> Poll>> { - Pin::new(&mut self.stream) + unsafe { Pin::new_unchecked(self) } + .project() + .stream .poll_next(cx) .map(|res| res.map(|res| res.map_err(std::convert::Into::into))) } @@ -393,8 +397,10 @@ where /// Type represent streaming body. This body implementation should be used /// if total size of stream is known. Data get sent as is without using transfer encoding. +#[pin_project] pub struct SizedStream { size: u64, + #[pin] stream: S, } @@ -409,20 +415,25 @@ where impl MessageBody for SizedStream where - S: Stream> + Unpin, + S: Stream>, { fn size(&self) -> BodySize { BodySize::Sized64(self.size) } fn poll_next(&mut self, cx: &mut Context) -> Poll>> { - Pin::new(&mut self.stream).poll_next(cx) + unsafe { Pin::new_unchecked(self) } + .project() + .stream + .poll_next(cx) } } #[cfg(test)] mod tests { use super::*; + use actix_http_test::block_on; + use futures::future::{lazy, poll_fn}; impl Body { pub(crate) fn get_ref(&self) -> &[u8] { @@ -450,8 +461,8 @@ mod tests { assert_eq!("test".size(), BodySize::Sized(4)); assert_eq!( - "test".poll_next().unwrap(), - Async::Ready(Some(Bytes::from("test"))) + block_on(poll_fn(|cx| "test".poll_next(cx))).unwrap().ok(), + Some(Bytes::from("test")) ); } @@ -467,8 +478,10 @@ mod tests { assert_eq!((&b"test"[..]).size(), BodySize::Sized(4)); assert_eq!( - (&b"test"[..]).poll_next().unwrap(), - Async::Ready(Some(Bytes::from("test"))) + block_on(poll_fn(|cx| (&b"test"[..]).poll_next(cx))) + .unwrap() + .ok(), + Some(Bytes::from("test")) ); } @@ -479,8 +492,10 @@ mod tests { assert_eq!(Vec::from("test").size(), BodySize::Sized(4)); assert_eq!( - Vec::from("test").poll_next().unwrap(), - Async::Ready(Some(Bytes::from("test"))) + block_on(poll_fn(|cx| Vec::from("test").poll_next(cx))) + .unwrap() + .ok(), + Some(Bytes::from("test")) ); } @@ -492,8 +507,8 @@ mod tests { assert_eq!(b.size(), BodySize::Sized(4)); assert_eq!( - b.poll_next().unwrap(), - Async::Ready(Some(Bytes::from("test"))) + block_on(poll_fn(|cx| b.poll_next(cx))).unwrap().ok(), + Some(Bytes::from("test")) ); } @@ -505,8 +520,8 @@ mod tests { assert_eq!(b.size(), BodySize::Sized(4)); assert_eq!( - b.poll_next().unwrap(), - Async::Ready(Some(Bytes::from("test"))) + block_on(poll_fn(|cx| b.poll_next(cx))).unwrap().ok(), + Some(Bytes::from("test")) ); } @@ -520,22 +535,22 @@ mod tests { assert_eq!(b.size(), BodySize::Sized(4)); assert_eq!( - b.poll_next().unwrap(), - Async::Ready(Some(Bytes::from("test"))) + block_on(poll_fn(|cx| b.poll_next(cx))).unwrap().ok(), + Some(Bytes::from("test")) ); } #[test] fn test_unit() { assert_eq!(().size(), BodySize::Empty); - assert_eq!(().poll_next().unwrap(), Async::Ready(None)); + assert!(block_on(poll_fn(|cx| ().poll_next(cx))).is_none()); } #[test] fn test_box() { let mut val = Box::new(()); assert_eq!(val.size(), BodySize::Empty); - assert_eq!(val.poll_next().unwrap(), Async::Ready(None)); + assert!(block_on(poll_fn(|cx| val.poll_next(cx))).is_none()); } #[test] diff --git a/actix-http/src/builder.rs b/actix-http/src/builder.rs index 8997d720..8fa35fea 100644 --- a/actix-http/src/builder.rs +++ b/actix-http/src/builder.rs @@ -33,11 +33,9 @@ pub struct HttpServiceBuilder> { impl HttpServiceBuilder> where S: ServiceFactory, - S::Error: Into + Unpin + 'static, + S::Error: Into + 'static, S::InitError: fmt::Debug, - S::Future: Unpin, - S::Service: Unpin, - ::Future: Unpin + 'static, + ::Future: 'static, { /// Create instance of `ServiceConfigBuilder` pub fn new() -> Self { @@ -56,17 +54,13 @@ where impl HttpServiceBuilder where S: ServiceFactory, - S::Error: Into + Unpin + 'static, + S::Error: Into + 'static, S::InitError: fmt::Debug, - S::Future: Unpin, - S::Service: Unpin, - ::Future: Unpin + 'static, + ::Future: 'static, X: ServiceFactory, X::Error: Into, X::InitError: fmt::Debug, - X::Future: Unpin, - X::Service: Unpin, - ::Future: Unpin + 'static, + ::Future: 'static, U: ServiceFactory< Config = SrvConfig, Request = (Request, Framed), @@ -74,9 +68,7 @@ where >, U::Error: fmt::Display, U::InitError: fmt::Debug, - U::Future: Unpin, - U::Service: Unpin, - ::Future: Unpin + 'static, + ::Future: 'static, { /// Set server keep-alive setting. /// @@ -124,9 +116,7 @@ where X1: ServiceFactory, X1::Error: Into, X1::InitError: fmt::Debug, - X1::Future: Unpin, - X1::Service: Unpin, - ::Future: Unpin + 'static, + ::Future: 'static, { HttpServiceBuilder { keep_alive: self.keep_alive, @@ -153,9 +143,7 @@ where >, U1::Error: fmt::Display, U1::InitError: fmt::Debug, - U1::Future: Unpin, - U1::Service: Unpin, - ::Future: Unpin + 'static, + ::Future: 'static, { HttpServiceBuilder { keep_alive: self.keep_alive, @@ -186,13 +174,10 @@ where where B: MessageBody + 'static, F: IntoServiceFactory, - S::Future: Unpin, - S::Error: Into + Unpin + 'static, + S::Error: Into + 'static, S::InitError: fmt::Debug, - S::Response: Into> + Unpin + 'static, - S::Service: Unpin, - ::Future: Unpin + 'static, - P: Unpin, + S::Response: Into> + 'static, + ::Future: 'static, { let cfg = ServiceConfig::new( self.keep_alive, @@ -210,13 +195,10 @@ where where B: MessageBody + 'static, F: IntoServiceFactory, - S::Error: Into + Unpin + 'static, + S::Error: Into + 'static, S::InitError: fmt::Debug, - S::Response: Into> + Unpin + 'static, - S::Future: Unpin, - S::Service: Unpin, - ::Future: Unpin + 'static, - P: Unpin, + S::Response: Into> + 'static, + ::Future: 'static, { let cfg = ServiceConfig::new( self.keep_alive, @@ -231,13 +213,10 @@ where where B: MessageBody + 'static, F: IntoServiceFactory, - S::Error: Into + Unpin + 'static, + S::Error: Into + 'static, S::InitError: fmt::Debug, - S::Response: Into> + Unpin + 'static, - S::Future: Unpin, - S::Service: Unpin, - ::Future: Unpin + 'static, - P: Unpin, + S::Response: Into> + 'static, + ::Future: 'static, { let cfg = ServiceConfig::new( self.keep_alive, diff --git a/actix-http/src/client/connection.rs b/actix-http/src/client/connection.rs index 0901fdb2..75d393b1 100644 --- a/actix-http/src/client/connection.rs +++ b/actix-http/src/client/connection.rs @@ -6,6 +6,7 @@ use actix_codec::{AsyncRead, AsyncWrite, Framed}; use bytes::{Buf, Bytes}; use futures::future::{err, Either, Future, FutureExt, LocalBoxFuture, Ready}; use h2::client::SendRequest; +use pin_project::{pin_project, project}; use crate::body::MessageBody; use crate::h1::ClientCodec; @@ -42,9 +43,7 @@ pub trait Connection { fn open_tunnel>(self, head: H) -> Self::TunnelFuture; } -pub(crate) trait ConnectionLifetime: - AsyncRead + AsyncWrite + Unpin + 'static -{ +pub(crate) trait ConnectionLifetime: AsyncRead + AsyncWrite + 'static { /// Close connection fn close(&mut self); @@ -73,7 +72,7 @@ where } } -impl IoConnection { +impl IoConnection { pub(crate) fn new( io: ConnectionType, created: time::Instant, @@ -205,24 +204,27 @@ where } } +#[pin_project] pub enum EitherIo { - A(A), - B(B), + A(#[pin] A), + B(#[pin] B), } impl AsyncRead for EitherIo where - A: AsyncRead + Unpin, - B: AsyncRead + Unpin, + A: AsyncRead, + B: AsyncRead, { + #[project] fn poll_read( self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { - match self.get_mut() { - EitherIo::A(ref mut val) => Pin::new(val).poll_read(cx, buf), - EitherIo::B(ref mut val) => Pin::new(val).poll_read(cx, buf), + #[project] + match self.project() { + EitherIo::A(val) => val.poll_read(cx, buf), + EitherIo::B(val) => val.poll_read(cx, buf), } } @@ -236,37 +238,44 @@ where impl AsyncWrite for EitherIo where - A: AsyncWrite + Unpin, - B: AsyncWrite + Unpin, + A: AsyncWrite, + B: AsyncWrite, { + #[project] fn poll_write( self: Pin<&mut Self>, cx: &mut Context, buf: &[u8], ) -> Poll> { - match self.get_mut() { - EitherIo::A(ref mut val) => Pin::new(val).poll_write(cx, buf), - EitherIo::B(ref mut val) => Pin::new(val).poll_write(cx, buf), + #[project] + match self.project() { + EitherIo::A(val) => val.poll_write(cx, buf), + EitherIo::B(val) => val.poll_write(cx, buf), } } + #[project] fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - match self.get_mut() { - EitherIo::A(ref mut val) => Pin::new(val).poll_flush(cx), - EitherIo::B(ref mut val) => Pin::new(val).poll_flush(cx), + #[project] + match self.project() { + EitherIo::A(val) => val.poll_flush(cx), + EitherIo::B(val) => val.poll_flush(cx), } } + #[project] fn poll_shutdown( self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - match self.get_mut() { - EitherIo::A(ref mut val) => Pin::new(val).poll_shutdown(cx), - EitherIo::B(ref mut val) => Pin::new(val).poll_shutdown(cx), + #[project] + match self.project() { + EitherIo::A(val) => val.poll_shutdown(cx), + EitherIo::B(val) => val.poll_shutdown(cx), } } + #[project] fn poll_write_buf( self: Pin<&mut Self>, cx: &mut Context<'_>, @@ -275,9 +284,10 @@ where where Self: Sized, { - match self.get_mut() { - EitherIo::A(ref mut val) => Pin::new(val).poll_write_buf(cx, buf), - EitherIo::B(ref mut val) => Pin::new(val).poll_write_buf(cx, buf), + #[project] + match self.project() { + EitherIo::A(val) => val.poll_write_buf(cx, buf), + EitherIo::B(val) => val.poll_write_buf(cx, buf), } } } diff --git a/actix-http/src/client/connector.rs b/actix-http/src/client/connector.rs index 45625ca9..1895f530 100644 --- a/actix-http/src/client/connector.rs +++ b/actix-http/src/client/connector.rs @@ -62,8 +62,8 @@ pub struct Connector { _t: PhantomData, } -trait Io: AsyncRead + AsyncWrite {} -impl Io for T {} +trait Io: AsyncRead + AsyncWrite + Unpin {} +impl Io for T {} impl Connector<(), ()> { #[allow(clippy::new_ret_no_self)] @@ -123,7 +123,6 @@ impl Connector { Response = TcpConnection, Error = actix_connect::ConnectError, > + Clone, - T1::Future: Unpin, { Connector { connector, @@ -222,7 +221,7 @@ where { let connector = TimeoutService::new( self.timeout, - apply_fn(UnpinWrapper(self.connector), |msg: Connect, srv| { + apply_fn(self.connector, |msg: Connect, srv| { srv.call(TcpConnect::new(msg.uri).set_addr(msg.addr)) }) .map_err(ConnectError::from) @@ -257,35 +256,33 @@ where let ssl_service = TimeoutService::new( self.timeout, pipeline( - apply_fn( - UnpinWrapper(self.connector.clone()), - |msg: Connect, srv| { - srv.call(TcpConnect::new(msg.uri).set_addr(msg.addr)) - }, - ) + apply_fn(self.connector.clone(), |msg: Connect, srv| { + srv.call(TcpConnect::new(msg.uri).set_addr(msg.addr)) + }) .map_err(ConnectError::from), ) .and_then(match self.ssl { #[cfg(feature = "openssl")] - SslConnector::Openssl(ssl) => OpensslConnector::service(ssl) - .map(|stream| { - let sock = stream.into_parts().0; - let h2 = sock - .ssl() - .selected_alpn_protocol() - .map(|protos| protos.windows(2).any(|w| w == H2)) - .unwrap_or(false); - if h2 { - (Box::new(sock) as Box, Protocol::Http2) - } else { - (Box::new(sock) as Box, Protocol::Http1) - } - }) - .map_err(ConnectError::from), - + SslConnector::Openssl(ssl) => service( + OpensslConnector::service(ssl) + .map(|stream| { + let sock = stream.into_parts().0; + let h2 = sock + .ssl() + .selected_alpn_protocol() + .map(|protos| protos.windows(2).any(|w| w == H2)) + .unwrap_or(false); + if h2 { + (Box::new(sock) as Box, Protocol::Http2) + } else { + (Box::new(sock) as Box, Protocol::Http1) + } + }) + .map_err(ConnectError::from), + ), #[cfg(feature = "rustls")] SslConnector::Rustls(ssl) => service( - UnpinWrapper(RustlsConnector::service(ssl)) + RustlsConnector::service(ssl) .map_err(ConnectError::from) .map(|stream| { let sock = stream.into_parts().0; @@ -296,15 +293,9 @@ where .map(|protos| protos.windows(2).any(|w| w == H2)) .unwrap_or(false); if h2 { - ( - Box::new(sock) as Box, - Protocol::Http2, - ) + (Box::new(sock) as Box, Protocol::Http2) } else { - ( - Box::new(sock) as Box, - Protocol::Http1, - ) + (Box::new(sock) as Box, Protocol::Http1) } }), ), @@ -317,7 +308,7 @@ where let tcp_service = TimeoutService::new( self.timeout, - apply_fn(UnpinWrapper(self.connector), |msg: Connect, srv| { + apply_fn(self.connector, |msg: Connect, srv| { srv.call(TcpConnect::new(msg.uri).set_addr(msg.addr)) }) .map_err(ConnectError::from) @@ -348,42 +339,6 @@ where } } -#[derive(Clone)] -struct UnpinWrapper(T); - -impl Unpin for UnpinWrapper {} - -impl Service for UnpinWrapper { - type Request = T::Request; - type Response = T::Response; - type Error = T::Error; - type Future = UnpinWrapperFut; - - fn poll_ready(&mut self, cx: &mut Context) -> Poll> { - self.0.poll_ready(cx) - } - - fn call(&mut self, req: T::Request) -> Self::Future { - UnpinWrapperFut { - fut: self.0.call(req), - } - } -} - -struct UnpinWrapperFut { - fut: T::Future, -} - -impl Unpin for UnpinWrapperFut {} - -impl Future for UnpinWrapperFut { - type Output = Result; - - fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { - unsafe { Pin::new_unchecked(&mut self.get_mut().fut) }.poll(cx) - } -} - #[cfg(not(any(feature = "openssl", feature = "rustls")))] mod connect_impl { use std::task::{Context, Poll}; @@ -396,9 +351,8 @@ mod connect_impl { pub(crate) struct InnerConnector where - Io: AsyncRead + AsyncWrite + 'static, + Io: AsyncRead + AsyncWrite + Unpin + 'static, T: Service - + Unpin + 'static, { pub(crate) tcp_pool: ConnectionPool, @@ -406,9 +360,8 @@ mod connect_impl { impl Clone for InnerConnector where - Io: AsyncRead + AsyncWrite + 'static, + Io: AsyncRead + AsyncWrite + Unpin + 'static, T: Service - + Unpin + 'static, { fn clone(&self) -> Self { @@ -422,9 +375,7 @@ mod connect_impl { where Io: AsyncRead + AsyncWrite + Unpin + 'static, T: Service - + Unpin + 'static, - T::Future: Unpin, { type Request = Connect; type Response = IoConnection; @@ -465,8 +416,6 @@ mod connect_impl { Io2: AsyncRead + AsyncWrite + Unpin + 'static, T1: Service, T2: Service, - T1::Future: Unpin, - T2::Future: Unpin, { pub(crate) tcp_pool: ConnectionPool, pub(crate) ssl_pool: ConnectionPool, @@ -477,13 +426,9 @@ mod connect_impl { Io1: AsyncRead + AsyncWrite + Unpin + 'static, Io2: AsyncRead + AsyncWrite + Unpin + 'static, T1: Service - + Unpin + 'static, T2: Service - + Unpin + 'static, - T1::Future: Unpin, - T2::Future: Unpin, { fn clone(&self) -> Self { InnerConnector { @@ -498,13 +443,9 @@ mod connect_impl { Io1: AsyncRead + AsyncWrite + Unpin + 'static, Io2: AsyncRead + AsyncWrite + Unpin + 'static, T1: Service - + Unpin + 'static, T2: Service - + Unpin + 'static, - T1::Future: Unpin, - T2::Future: Unpin, { type Request = Connect; type Response = EitherConnection; @@ -532,14 +473,14 @@ mod connect_impl { } } + #[pin_project::pin_project] pub(crate) struct InnerConnectorResponseA where Io1: AsyncRead + AsyncWrite + Unpin + 'static, T: Service - + Unpin + 'static, - T::Future: Unpin, { + #[pin] fut: as Service>::Future, _t: PhantomData, } @@ -547,9 +488,7 @@ mod connect_impl { impl Future for InnerConnectorResponseA where T: Service - + Unpin + 'static, - T::Future: Unpin, Io1: AsyncRead + AsyncWrite + Unpin + 'static, Io2: AsyncRead + AsyncWrite + Unpin + 'static, { @@ -563,14 +502,14 @@ mod connect_impl { } } + #[pin_project::pin_project] pub(crate) struct InnerConnectorResponseB where Io2: AsyncRead + AsyncWrite + Unpin + 'static, T: Service - + Unpin + 'static, - T::Future: Unpin, { + #[pin] fut: as Service>::Future, _t: PhantomData, } @@ -578,9 +517,7 @@ mod connect_impl { impl Future for InnerConnectorResponseB where T: Service - + Unpin + 'static, - T::Future: Unpin, Io1: AsyncRead + AsyncWrite + Unpin + 'static, Io2: AsyncRead + AsyncWrite + Unpin + 'static, { diff --git a/actix-http/src/client/error.rs b/actix-http/src/client/error.rs index 0ac5f30f..75f7935f 100644 --- a/actix-http/src/client/error.rs +++ b/actix-http/src/client/error.rs @@ -3,8 +3,8 @@ use std::io; use derive_more::{Display, From}; use trust_dns_resolver::error::ResolveError; -#[cfg(feature = "ssl")] -use openssl::ssl::{Error as SslError, HandshakeError}; +#[cfg(feature = "openssl")] +use open_ssl::ssl::{Error as SslError, HandshakeError}; use crate::error::{Error, ParseError, ResponseError}; use crate::http::Error as HttpError; @@ -18,7 +18,7 @@ pub enum ConnectError { SslIsNotSupported, /// SSL error - #[cfg(feature = "ssl")] + #[cfg(feature = "openssl")] #[display(fmt = "{}", _0)] SslError(SslError), @@ -63,7 +63,7 @@ impl From for ConnectError { } } -#[cfg(feature = "ssl")] +#[cfg(feature = "openssl")] impl From> for ConnectError { fn from(err: HandshakeError) -> ConnectError { match err { diff --git a/actix-http/src/client/pool.rs b/actix-http/src/client/pool.rs index ee4c4ab9..1952dca5 100644 --- a/actix-http/src/client/pool.rs +++ b/actix-http/src/client/pool.rs @@ -46,11 +46,9 @@ pub(crate) struct ConnectionPool(Rc>, Rc ConnectionPool where - Io: AsyncRead + AsyncWrite + 'static, + Io: AsyncRead + AsyncWrite + Unpin + 'static, T: Service - + Unpin + 'static, - T::Future: Unpin, { pub(crate) fn new( connector: T, @@ -89,9 +87,7 @@ impl Service for ConnectionPool where Io: AsyncRead + AsyncWrite + Unpin + 'static, T: Service - + Unpin + 'static, - T::Future: Unpin, { type Request = Connect; type Response = IoConnection; @@ -400,7 +396,7 @@ struct CloseConnection { impl CloseConnection where - T: AsyncWrite, + T: AsyncWrite + Unpin, { fn new(io: T, timeout: Duration) -> Self { CloseConnection { @@ -416,10 +412,12 @@ where { type Output = (); - fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<()> { - match Pin::new(&mut self.timeout).poll(cx) { + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<()> { + let this = self.get_mut(); + + match Pin::new(&mut this.timeout).poll(cx) { Poll::Ready(_) => Poll::Ready(()), - Poll::Pending => match Pin::new(&mut self.io).poll_shutdown(cx) { + Poll::Pending => match Pin::new(&mut this.io).poll_shutdown(cx) { Poll::Ready(_) => Poll::Ready(()), Poll::Pending => Poll::Pending, }, @@ -429,7 +427,7 @@ where struct ConnectorPoolSupport where - Io: AsyncRead + AsyncWrite + 'static, + Io: AsyncRead + AsyncWrite + Unpin + 'static, { connector: T, inner: Rc>>, @@ -438,14 +436,13 @@ where impl Future for ConnectorPoolSupport where Io: AsyncRead + AsyncWrite + Unpin + 'static, - T: Service - + Unpin, - T::Future: Unpin + 'static, + T: Service, + T::Future: 'static, { type Output = (); fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { - let this = self.get_mut(); + let this = unsafe { self.get_unchecked_mut() }; let mut inner = this.inner.as_ref().borrow_mut(); inner.waker.register(cx.waker()); @@ -512,7 +509,7 @@ where impl OpenWaitingConnection where - F: Future> + Unpin + 'static, + F: Future> + 'static, Io: AsyncRead + AsyncWrite + Unpin + 'static, { fn spawn( @@ -546,13 +543,13 @@ where impl Future for OpenWaitingConnection where - F: Future> + Unpin, + F: Future>, Io: AsyncRead + AsyncWrite + Unpin, { type Output = (); fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { - let this = self.get_mut(); + let this = unsafe { self.get_unchecked_mut() }; if let Some(ref mut h2) = this.h2 { return match Pin::new(h2).poll(cx) { @@ -577,7 +574,7 @@ where }; } - match Pin::new(&mut this.fut).poll(cx) { + match unsafe { Pin::new_unchecked(&mut this.fut) }.poll(cx) { Poll::Ready(Err(err)) => { let _ = this.inner.take(); if let Some(rx) = this.rx.take() { @@ -596,7 +593,7 @@ where Poll::Ready(()) } else { this.h2 = Some(handshake(io).boxed_local()); - Pin::new(this).poll(cx) + unsafe { Pin::new_unchecked(this) }.poll(cx) } } Poll::Pending => Poll::Pending, diff --git a/actix-http/src/config.rs b/actix-http/src/config.rs index a2dab8f0..488e4d98 100644 --- a/actix-http/src/config.rs +++ b/actix-http/src/config.rs @@ -277,7 +277,7 @@ mod tests { fn test_date() { let mut rt = System::new("test"); - let _ = rt.block_on(future::lazy(|| { + let _ = rt.block_on(future::lazy(|_| { let settings = ServiceConfig::new(KeepAlive::Os, 0, 0); let mut buf1 = BytesMut::with_capacity(DATE_VALUE_LENGTH + 10); settings.set_date(&mut buf1); diff --git a/actix-http/src/error.rs b/actix-http/src/error.rs index 82027dbe..2a683399 100644 --- a/actix-http/src/error.rs +++ b/actix-http/src/error.rs @@ -181,13 +181,13 @@ impl ResponseError for FormError {} /// `InternalServerError` for `TimerError` impl ResponseError for TimerError {} -#[cfg(feature = "ssl")] +#[cfg(feature = "openssl")] /// `InternalServerError` for `openssl::ssl::Error` -impl ResponseError for openssl::ssl::Error {} +impl ResponseError for open_ssl::ssl::Error {} -#[cfg(feature = "ssl")] +#[cfg(feature = "openssl")] /// `InternalServerError` for `openssl::ssl::HandshakeError` -impl ResponseError for openssl::ssl::HandshakeError {} +impl ResponseError for open_ssl::ssl::HandshakeError {} /// Return `BAD_REQUEST` for `de::value::Error` impl ResponseError for DeError { @@ -383,12 +383,12 @@ impl ResponseError for PayloadError { } } -// /// Return `BadRequest` for `cookie::ParseError` -// impl ResponseError for crate::cookie::ParseError { -// fn error_response(&self) -> Response { -// Response::new(StatusCode::BAD_REQUEST) -// } -// } +/// Return `BadRequest` for `cookie::ParseError` +impl ResponseError for crate::cookie::ParseError { + fn error_response(&self) -> Response { + Response::new(StatusCode::BAD_REQUEST) + } +} #[derive(Debug, Display, From)] /// A set of errors that can occur during dispatching http requests diff --git a/actix-http/src/h1/dispatcher.rs b/actix-http/src/h1/dispatcher.rs index a06b997e..8c089602 100644 --- a/actix-http/src/h1/dispatcher.rs +++ b/actix-http/src/h1/dispatcher.rs @@ -48,14 +48,11 @@ pub struct Dispatcher where S: Service, S::Error: Into, - S::Future: Unpin, B: MessageBody, X: Service, X::Error: Into, - X::Future: Unpin, U: Service), Response = ()>, U::Error: fmt::Display, - U::Future: Unpin, { inner: DispatcherState, } @@ -64,14 +61,11 @@ enum DispatcherState where S: Service, S::Error: Into, - S::Future: Unpin, B: MessageBody, X: Service, X::Error: Into, - X::Future: Unpin, U: Service), Response = ()>, U::Error: fmt::Display, - U::Future: Unpin, { Normal(InnerDispatcher), Upgrade(U::Future), @@ -82,14 +76,11 @@ struct InnerDispatcher where S: Service, S::Error: Into, - S::Future: Unpin, B: MessageBody, X: Service, X::Error: Into, - X::Future: Unpin, U: Service), Response = ()>, U::Error: fmt::Display, - U::Future: Unpin, { service: CloneableService, expect: CloneableService, @@ -181,14 +172,11 @@ where S: Service, S::Error: Into, S::Response: Into>, - S::Future: Unpin, B: MessageBody, X: Service, X::Error: Into, - X::Future: Unpin, U: Service), Response = ()>, U::Error: fmt::Display, - U::Future: Unpin, { /// Create http/1 dispatcher. pub(crate) fn new( @@ -269,14 +257,11 @@ where S: Service, S::Error: Into, S::Response: Into>, - S::Future: Unpin, B: MessageBody, X: Service, X::Error: Into, - X::Future: Unpin, U: Service), Response = ()>, U::Error: fmt::Display, - U::Future: Unpin, { fn can_read(&self, cx: &mut Context) -> bool { if self @@ -312,7 +297,9 @@ where let len = self.write_buf.len(); let mut written = 0; while written < len { - match Pin::new(&mut self.io).poll_write(cx, &self.write_buf[written..]) { + match unsafe { Pin::new_unchecked(&mut self.io) } + .poll_write(cx, &self.write_buf[written..]) + { Poll::Ready(Ok(0)) => { return Err(DispatchError::Io(io::Error::new( io::ErrorKind::WriteZero, @@ -383,32 +370,36 @@ where } None => None, }, - State::ExpectCall(ref mut fut) => match Pin::new(fut).poll(cx) { - Poll::Ready(Ok(req)) => { - self.send_continue(); - self.state = State::ServiceCall(self.service.call(req)); - continue; + State::ExpectCall(ref mut fut) => { + match unsafe { Pin::new_unchecked(fut) }.poll(cx) { + Poll::Ready(Ok(req)) => { + self.send_continue(); + self.state = State::ServiceCall(self.service.call(req)); + continue; + } + Poll::Ready(Err(e)) => { + let res: Response = e.into().into(); + let (res, body) = res.replace_body(()); + Some(self.send_response(res, body.into_body())?) + } + Poll::Pending => None, } - Poll::Ready(Err(e)) => { - let res: Response = e.into().into(); - let (res, body) = res.replace_body(()); - Some(self.send_response(res, body.into_body())?) + } + State::ServiceCall(ref mut fut) => { + match unsafe { Pin::new_unchecked(fut) }.poll(cx) { + Poll::Ready(Ok(res)) => { + let (res, body) = res.into().replace_body(()); + self.state = self.send_response(res, body)?; + continue; + } + Poll::Ready(Err(e)) => { + let res: Response = e.into().into(); + let (res, body) = res.replace_body(()); + Some(self.send_response(res, body.into_body())?) + } + Poll::Pending => None, } - Poll::Pending => None, - }, - State::ServiceCall(ref mut fut) => match Pin::new(fut).poll(cx) { - Poll::Ready(Ok(res)) => { - let (res, body) = res.into().replace_body(()); - self.state = self.send_response(res, body)?; - continue; - } - Poll::Ready(Err(e)) => { - let res: Response = e.into().into(); - let (res, body) = res.replace_body(()); - Some(self.send_response(res, body.into_body())?) - } - Poll::Pending => None, - }, + } State::SendPayload(ref mut stream) => { loop { if self.write_buf.len() < HW_BUFFER_SIZE { @@ -472,7 +463,7 @@ where // Handle `EXPECT: 100-Continue` header let req = if req.head().expect() { let mut task = self.expect.call(req); - match Pin::new(&mut task).poll(cx) { + match unsafe { Pin::new_unchecked(&mut task) }.poll(cx) { Poll::Ready(Ok(req)) => { self.send_continue(); req @@ -491,7 +482,7 @@ where // Call service let mut task = self.service.call(req); - match Pin::new(&mut task).poll(cx) { + match unsafe { Pin::new_unchecked(&mut task) }.poll(cx) { Poll::Ready(Ok(res)) => { let (res, body) = res.into().replace_body(()); self.send_response(res, body) @@ -689,26 +680,37 @@ where } } +impl Unpin for Dispatcher +where + T: IoStream, + S: Service, + S::Error: Into, + S::Response: Into>, + B: MessageBody, + X: Service, + X::Error: Into, + U: Service), Response = ()>, + U::Error: fmt::Display, +{ +} + impl Future for Dispatcher where T: IoStream, S: Service, S::Error: Into, S::Response: Into>, - S::Future: Unpin, B: MessageBody, X: Service, X::Error: Into, - X::Future: Unpin, U: Service), Response = ()>, U::Error: fmt::Display, - U::Future: Unpin, { type Output = Result<(), DispatchError>; #[inline] fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { - match self.inner { + match self.as_mut().inner { DispatcherState::Normal(ref mut inner) => { inner.poll_keepalive(cx)?; @@ -818,7 +820,7 @@ where } } DispatcherState::Upgrade(ref mut fut) => { - Pin::new(fut).poll(cx).map_err(|e| { + unsafe { Pin::new_unchecked(fut) }.poll(cx).map_err(|e| { error!("Upgrade handler error: {}", e); DispatchError::Upgrade }) @@ -894,7 +896,7 @@ mod tests { #[test] fn test_req_parse_err() { let mut sys = actix_rt::System::new("test"); - let _ = sys.block_on(lazy(|| { + let _ = sys.block_on(lazy(|cx| { let buf = TestBuffer::new("GET /test HTTP/1\r\n\r\n"); let mut h1 = Dispatcher::<_, _, _, _, UpgradeHandler>::new( @@ -907,7 +909,10 @@ mod tests { None, None, ); - assert!(h1.poll().is_err()); + match Pin::new(&mut h1).poll(cx) { + Poll::Pending => panic!(), + Poll::Ready(res) => assert!(res.is_err()), + } if let DispatcherState::Normal(ref inner) = h1.inner { assert!(inner.flags.contains(Flags::READ_DISCONNECT)); diff --git a/actix-http/src/h1/payload.rs b/actix-http/src/h1/payload.rs index 20ff830e..2b52cfd8 100644 --- a/actix-http/src/h1/payload.rs +++ b/actix-http/src/h1/payload.rs @@ -228,26 +228,23 @@ impl Inner { mod tests { use super::*; use actix_rt::Runtime; - use futures::future::{lazy, result}; + use futures::future::{poll_fn, ready}; #[test] fn test_unread_data() { - Runtime::new() - .unwrap() - .block_on(async { - let (_, mut payload) = Payload::create(false); + Runtime::new().unwrap().block_on(async { + let (_, mut payload) = Payload::create(false); - payload.unread_data(Bytes::from("data")); - assert!(!payload.is_empty()); - assert_eq!(payload.len(), 4); + payload.unread_data(Bytes::from("data")); + assert!(!payload.is_empty()); + assert_eq!(payload.len(), 4); - assert_eq!( - Poll::Ready(Some(Bytes::from("data"))), - payload.next_item().await.ok().unwrap() - ); + assert_eq!( + Bytes::from("data"), + poll_fn(|cx| payload.readany(cx)).await.unwrap().unwrap() + ); - result(()) - }) - .unwrap(); + ready(()) + }); } } diff --git a/actix-http/src/h1/service.rs b/actix-http/src/h1/service.rs index 95596af7..ce8ff662 100644 --- a/actix-http/src/h1/service.rs +++ b/actix-http/src/h1/service.rs @@ -39,11 +39,7 @@ where S::Error: Into, S::InitError: fmt::Debug, S::Response: Into>, - S::Future: Unpin, - S::Service: Unpin, - ::Future: Unpin, B: MessageBody, - P: Unpin, { /// Create new `HttpService` instance with default config. pub fn new>(service: F) -> Self { @@ -81,20 +77,13 @@ where S::Error: Into, S::Response: Into>, S::InitError: fmt::Debug, - S::Future: Unpin, - S::Service: Unpin, - ::Future: Unpin, B: MessageBody, - P: Unpin, { pub fn expect(self, expect: X1) -> H1Service where X1: ServiceFactory, X1::Error: Into, X1::InitError: fmt::Debug, - X1::Future: Unpin, - X1::Service: Unpin, - ::Future: Unpin, { H1Service { expect, @@ -111,9 +100,6 @@ where U1: ServiceFactory), Response = ()>, U1::Error: fmt::Display, U1::InitError: fmt::Debug, - U1::Future: Unpin, - U1::Service: Unpin, - ::Future: Unpin, { H1Service { upgrade, @@ -139,20 +125,13 @@ impl ServiceFactory for H1Service where T: IoStream, S: ServiceFactory, - S::Service: Unpin, S::Error: Into, S::Response: Into>, S::InitError: fmt::Debug, - S::Future: Unpin, - S::Service: Unpin, - ::Future: Unpin, B: MessageBody, X: ServiceFactory, X::Error: Into, X::InitError: fmt::Debug, - X::Future: Unpin, - X::Service: Unpin, - ::Future: Unpin, U: ServiceFactory< Config = SrvConfig, Request = (Request, Framed), @@ -160,10 +139,6 @@ where >, U::Error: fmt::Display, U::InitError: fmt::Debug, - U::Future: Unpin, - U::Service: Unpin, - ::Future: Unpin, - P: Unpin, { type Config = SrvConfig; type Request = Io; @@ -188,30 +163,24 @@ where } #[doc(hidden)] +#[pin_project::pin_project] pub struct H1ServiceResponse where S: ServiceFactory, S::Error: Into, S::InitError: fmt::Debug, - S::Future: Unpin, - S::Service: Unpin, - ::Future: Unpin, X: ServiceFactory, X::Error: Into, X::InitError: fmt::Debug, - X::Future: Unpin, - X::Service: Unpin, - ::Future: Unpin, U: ServiceFactory), Response = ()>, U::Error: fmt::Display, U::InitError: fmt::Debug, - U::Future: Unpin, - U::Service: Unpin, - ::Future: Unpin, - P: Unpin, { + #[pin] fut: S::Future, + #[pin] fut_ex: Option, + #[pin] fut_upg: Option, expect: Option, upgrade: Option, @@ -227,51 +196,45 @@ where S::Error: Into, S::Response: Into>, S::InitError: fmt::Debug, - S::Future: Unpin, - S::Service: Unpin, - ::Future: Unpin, B: MessageBody, X: ServiceFactory, X::Error: Into, X::InitError: fmt::Debug, - X::Future: Unpin, - X::Service: Unpin, - ::Future: Unpin, U: ServiceFactory), Response = ()>, U::Error: fmt::Display, U::InitError: fmt::Debug, - U::Future: Unpin, - U::Service: Unpin, - ::Future: Unpin, - P: Unpin, { type Output = Result, ()>; - fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { - let this = self.get_mut(); + fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { + let mut this = self.as_mut().project(); - if let Some(ref mut fut) = this.fut_ex { - let expect = ready!(Pin::new(fut) + if let Some(fut) = this.fut_ex.as_pin_mut() { + let expect = ready!(fut .poll(cx) .map_err(|e| log::error!("Init http service error: {:?}", e)))?; - this.expect = Some(expect); - this.fut_ex.take(); + this = self.as_mut().project(); + *this.expect = Some(expect); + this.fut_ex.set(None); } - if let Some(ref mut fut) = this.fut_upg { - let upgrade = ready!(Pin::new(fut) + if let Some(fut) = this.fut_upg.as_pin_mut() { + let upgrade = ready!(fut .poll(cx) .map_err(|e| log::error!("Init http service error: {:?}", e)))?; - this.upgrade = Some(upgrade); - this.fut_ex.take(); + this = self.as_mut().project(); + *this.upgrade = Some(upgrade); + this.fut_ex.set(None); } - let result = ready!(Pin::new(&mut this.fut) + let result = ready!(this + .fut .poll(cx) .map_err(|e| log::error!("Init http service error: {:?}", e))); Poll::Ready(result.map(|service| { + let this = self.as_mut().project(); H1ServiceHandler::new( this.cfg.take().unwrap(), service, @@ -295,18 +258,14 @@ pub struct H1ServiceHandler { impl H1ServiceHandler where - S: Service + Unpin, + S: Service, S::Error: Into, S::Response: Into>, - S::Future: Unpin, B: MessageBody, - X: Service + Unpin, - X::Future: Unpin, + X: Service, X::Error: Into, - U: Service), Response = ()> + Unpin, - U::Future: Unpin, + U: Service), Response = ()>, U::Error: fmt::Display, - P: Unpin, { fn new( cfg: ServiceConfig, @@ -329,18 +288,14 @@ where impl Service for H1ServiceHandler where T: IoStream, - S: Service + Unpin, + S: Service, S::Error: Into, S::Response: Into>, - S::Future: Unpin, B: MessageBody, - X: Service + Unpin, + X: Service, X::Error: Into, - X::Future: Unpin, - U: Service), Response = ()> + Unpin, + U: Service), Response = ()>, U::Error: fmt::Display, - U::Future: Unpin, - P: Unpin, { type Request = Io; type Response = (); diff --git a/actix-http/src/h1/utils.rs b/actix-http/src/h1/utils.rs index a992089c..7057bf1c 100644 --- a/actix-http/src/h1/utils.rs +++ b/actix-http/src/h1/utils.rs @@ -11,6 +11,7 @@ use crate::h1::{Codec, Message}; use crate::response::Response; /// Send http/1 response +#[pin_project::pin_project] pub struct SendResponse { res: Option, BodySize)>>, body: Option>, @@ -34,7 +35,7 @@ where impl Future for SendResponse where - T: AsyncRead + AsyncWrite + Unpin, + T: AsyncRead + AsyncWrite, B: MessageBody, { type Output = Result, Error>; diff --git a/actix-http/src/h2/dispatcher.rs b/actix-http/src/h2/dispatcher.rs index 9b5b7e83..96775b98 100644 --- a/actix-http/src/h2/dispatcher.rs +++ b/actix-http/src/h2/dispatcher.rs @@ -35,6 +35,7 @@ use crate::response::Response; const CHUNK_SIZE: usize = 16_384; /// Dispatcher for HTTP/2 protocol +#[pin_project::pin_project] pub struct Dispatcher, B: MessageBody> { service: CloneableService, connection: Connection, @@ -46,24 +47,13 @@ pub struct Dispatcher, B: MessageBody _t: PhantomData, } -impl Unpin for Dispatcher -where - T: IoStream, - S: Service, - S::Error: Into + Unpin + 'static, - S::Future: Unpin + 'static, - S::Response: Into> + Unpin + 'static, - B: MessageBody + 'static, -{ -} - impl Dispatcher where T: IoStream, S: Service, - S::Error: Into + Unpin + 'static, - S::Future: Unpin + 'static, - S::Response: Into> + Unpin + 'static, + S::Error: Into + 'static, + S::Future: 'static, + S::Response: Into> + 'static, B: MessageBody + 'static, { pub(crate) fn new( @@ -107,9 +97,9 @@ impl Future for Dispatcher where T: IoStream, S: Service, - S::Error: Into + Unpin + 'static, - S::Future: Unpin + 'static, - S::Response: Into> + Unpin + 'static, + S::Error: Into + 'static, + S::Future: 'static, + S::Response: Into> + 'static, B: MessageBody + 'static, { type Output = Result<(), DispatchError>; @@ -122,7 +112,7 @@ where match Pin::new(&mut this.connection).poll_accept(cx) { Poll::Ready(None) => return Poll::Ready(Ok(())), Poll::Ready(Some(Err(err))) => return Poll::Ready(Err(err.into())), - Poll::Ready(Some(Ok((req, _)))) => { + Poll::Ready(Some(Ok((req, res)))) => { // update keep-alive expire if this.ka_timer.is_some() { if let Some(expire) = this.config.keep_alive_expire() { @@ -131,7 +121,6 @@ where } let (parts, body) = req.into_parts(); - // let b: () = body; let mut req = Request::with_payload(Payload::< crate::payload::PayloadStream, >::H2( @@ -150,20 +139,20 @@ where on_connect.set(&mut req.extensions_mut()); } - // tokio_executor::current_thread::spawn(ServiceResponse::< - // S::Future, - // S::Response, - // S::Error, - // B, - // > { - // state: ServiceResponseState::ServiceCall( - // this.service.call(req), - // Some(res), - // ), - // config: this.config.clone(), - // buffer: None, - // _t: PhantomData, - // }); + tokio_executor::current_thread::spawn(ServiceResponse::< + S::Future, + S::Response, + S::Error, + B, + > { + state: ServiceResponseState::ServiceCall( + this.service.call(req), + Some(res), + ), + config: this.config.clone(), + buffer: None, + _t: PhantomData, + }); } Poll::Pending => return Poll::Pending, } @@ -171,6 +160,7 @@ where } } +#[pin_project::pin_project] struct ServiceResponse { state: ServiceResponseState, config: ServiceConfig, @@ -185,9 +175,9 @@ enum ServiceResponseState { impl ServiceResponse where - F: Future> + Unpin, - E: Into + Unpin + 'static, - I: Into> + Unpin + 'static, + F: Future>, + E: Into + 'static, + I: Into> + 'static, B: MessageBody + 'static, { fn prepare_response( @@ -253,25 +243,27 @@ where impl Future for ServiceResponse where - F: Future> + Unpin, - E: Into + Unpin + 'static, - I: Into> + Unpin + 'static, + F: Future>, + E: Into + 'static, + I: Into> + 'static, B: MessageBody + 'static, { type Output = (); - fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { - let this = self.get_mut(); + fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { + let mut this = self.as_mut().project(); match this.state { ServiceResponseState::ServiceCall(ref mut call, ref mut send) => { - match Pin::new(call).poll(cx) { + match unsafe { Pin::new_unchecked(call) }.poll(cx) { Poll::Ready(Ok(res)) => { let (res, body) = res.into().replace_body(()); let mut send = send.take().unwrap(); let mut size = body.size(); - let h2_res = this.prepare_response(res.head(), &mut size); + let h2_res = + self.as_mut().prepare_response(res.head(), &mut size); + this = self.as_mut().project(); let stream = match send.send_response(h2_res, size.is_eof()) { Err(e) => { @@ -284,8 +276,9 @@ where if size.is_eof() { Poll::Ready(()) } else { - this.state = ServiceResponseState::SendPayload(stream, body); - Pin::new(this).poll(cx) + *this.state = + ServiceResponseState::SendPayload(stream, body); + self.poll(cx) } } Poll::Pending => Poll::Pending, @@ -295,7 +288,9 @@ where let mut send = send.take().unwrap(); let mut size = body.size(); - let h2_res = this.prepare_response(res.head(), &mut size); + let h2_res = + self.as_mut().prepare_response(res.head(), &mut size); + this = self.as_mut().project(); let stream = match send.send_response(h2_res, size.is_eof()) { Err(e) => { @@ -308,11 +303,11 @@ where if size.is_eof() { Poll::Ready(()) } else { - this.state = ServiceResponseState::SendPayload( + *this.state = ServiceResponseState::SendPayload( stream, body.into_body(), ); - Pin::new(this).poll(cx) + self.poll(cx) } } } @@ -356,7 +351,7 @@ where chunk.len(), CHUNK_SIZE, )); - this.buffer = Some(chunk); + *this.buffer = Some(chunk); } Poll::Ready(Some(Err(e))) => { error!("Response payload stream error: {:?}", e); diff --git a/actix-http/src/h2/service.rs b/actix-http/src/h2/service.rs index 559c9930..860a61f7 100644 --- a/actix-http/src/h2/service.rs +++ b/actix-http/src/h2/service.rs @@ -37,12 +37,10 @@ pub struct H2Service { impl H2Service where S: ServiceFactory, - S::Error: Into + Unpin + 'static, - S::Response: Into> + Unpin + 'static, - S::Future: Unpin, - ::Future: Unpin + 'static, + S::Error: Into + 'static, + S::Response: Into> + 'static, + ::Future: 'static, B: MessageBody + 'static, - P: Unpin, { /// Create new `HttpService` instance. pub fn new>(service: F) -> Self { @@ -83,12 +81,10 @@ impl ServiceFactory for H2Service where T: IoStream, S: ServiceFactory, - S::Error: Into + Unpin + 'static, - S::Response: Into> + Unpin + 'static, - S::Future: Unpin, - ::Future: Unpin + 'static, + S::Error: Into + 'static, + S::Response: Into> + 'static, + ::Future: 'static, B: MessageBody + 'static, - P: Unpin, { type Config = SrvConfig; type Request = Io; @@ -109,7 +105,9 @@ where } #[doc(hidden)] +#[pin_project::pin_project] pub struct H2ServiceResponse { + #[pin] fut: S::Future, cfg: Option, on_connect: Option Box>>, @@ -120,19 +118,18 @@ impl Future for H2ServiceResponse where T: IoStream, S: ServiceFactory, - S::Error: Into + Unpin + 'static, - S::Response: Into> + Unpin + 'static, - S::Future: Unpin, - ::Future: Unpin + 'static, + S::Error: Into + 'static, + S::Response: Into> + 'static, + ::Future: 'static, B: MessageBody + 'static, - P: Unpin, { type Output = Result, S::InitError>; - fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { - let this = self.get_mut(); + fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { + let this = self.as_mut().project(); - Poll::Ready(ready!(Pin::new(&mut this.fut).poll(cx)).map(|service| { + Poll::Ready(ready!(this.fut.poll(cx)).map(|service| { + let this = self.as_mut().project(); H2ServiceHandler::new( this.cfg.take().unwrap(), this.on_connect.clone(), @@ -153,11 +150,10 @@ pub struct H2ServiceHandler { impl H2ServiceHandler where S: Service, - S::Error: Into + Unpin + 'static, - S::Future: Unpin + 'static, - S::Response: Into> + Unpin + 'static, + S::Error: Into + 'static, + S::Future: 'static, + S::Response: Into> + 'static, B: MessageBody + 'static, - P: Unpin, { fn new( cfg: ServiceConfig, @@ -177,11 +173,10 @@ impl Service for H2ServiceHandler where T: IoStream, S: Service, - S::Error: Into + Unpin + 'static, - S::Future: Unpin + 'static, - S::Response: Into> + Unpin + 'static, + S::Error: Into + 'static, + S::Future: 'static, + S::Response: Into> + 'static, B: MessageBody + 'static, - P: Unpin, { type Request = Io; type Response = (); @@ -235,9 +230,9 @@ pub struct H2ServiceHandlerResponse where T: IoStream, S: Service, - S::Error: Into + Unpin + 'static, - S::Future: Unpin + 'static, - S::Response: Into> + Unpin + 'static, + S::Error: Into + 'static, + S::Future: 'static, + S::Response: Into> + 'static, B: MessageBody + 'static, { state: State, @@ -247,9 +242,9 @@ impl Future for H2ServiceHandlerResponse where T: IoStream, S: Service, - S::Error: Into + Unpin + 'static, - S::Future: Unpin + 'static, - S::Response: Into> + Unpin + 'static, + S::Error: Into + 'static, + S::Future: 'static, + S::Response: Into> + 'static, B: MessageBody, { type Output = Result<(), DispatchError>; diff --git a/actix-http/src/request.rs b/actix-http/src/request.rs index e9252a82..0afa45cb 100644 --- a/actix-http/src/request.rs +++ b/actix-http/src/request.rs @@ -80,6 +80,11 @@ impl

Request

{ ) } + /// Get request's payload + pub fn payload(&mut self) -> &mut Payload

{ + &mut self.payload + } + /// Get request's payload pub fn take_payload(&mut self) -> Payload

{ std::mem::replace(&mut self.payload, Payload::None) diff --git a/actix-http/src/response.rs b/actix-http/src/response.rs index 5b3d17cb..d05505d8 100644 --- a/actix-http/src/response.rs +++ b/actix-http/src/response.rs @@ -635,8 +635,8 @@ impl ResponseBuilder { /// `ResponseBuilder` can not be used after this call. pub fn streaming(&mut self, stream: S) -> Response where - S: Stream> + Unpin + 'static, - E: Into + Unpin + 'static, + S: Stream> + 'static, + E: Into + 'static, { self.body(Body::from_message(BodyStream::new(stream))) } diff --git a/actix-http/src/service.rs b/actix-http/src/service.rs index 65a0c7bd..e18b1013 100644 --- a/actix-http/src/service.rs +++ b/actix-http/src/service.rs @@ -11,6 +11,7 @@ use actix_service::{IntoServiceFactory, Service, ServiceFactory}; use bytes::{Buf, BufMut, Bytes, BytesMut}; use futures::{ready, Future}; use h2::server::{self, Handshake}; +use pin_project::{pin_project, project}; use crate::body::MessageBody; use crate::builder::HttpServiceBuilder; @@ -35,12 +36,10 @@ pub struct HttpService HttpService where S: ServiceFactory, - S::Error: Into + Unpin + 'static, + S::Error: Into + 'static, S::InitError: fmt::Debug, - S::Response: Into> + Unpin + 'static, - S::Future: Unpin, - S::Service: Unpin, - ::Future: Unpin + 'static, + S::Response: Into> + 'static, + ::Future: 'static, B: MessageBody + 'static, { /// Create builder for `HttpService` instance. @@ -52,14 +51,11 @@ where impl HttpService where S: ServiceFactory, - S::Error: Into + Unpin + 'static, + S::Error: Into + 'static, S::InitError: fmt::Debug, - S::Response: Into> + Unpin + 'static, - S::Future: Unpin, - S::Service: Unpin, - ::Future: Unpin + 'static, + S::Response: Into> + 'static, + ::Future: 'static, B: MessageBody + 'static, - P: Unpin, { /// Create new `HttpService` instance. pub fn new>(service: F) -> Self { @@ -94,14 +90,11 @@ where impl HttpService where S: ServiceFactory, - S::Error: Into + Unpin + 'static, + S::Error: Into + 'static, S::InitError: fmt::Debug, - S::Response: Into> + Unpin + 'static, - S::Future: Unpin, - S::Service: Unpin, - ::Future: Unpin + 'static, + S::Response: Into> + 'static, + ::Future: 'static, B: MessageBody, - P: Unpin, { /// Provide service for `EXPECT: 100-Continue` support. /// @@ -113,9 +106,7 @@ where X1: ServiceFactory, X1::Error: Into, X1::InitError: fmt::Debug, - X1::Future: Unpin, - X1::Service: Unpin, - ::Future: Unpin + 'static, + ::Future: 'static, { HttpService { expect, @@ -140,9 +131,7 @@ where >, U1::Error: fmt::Display, U1::InitError: fmt::Debug, - U1::Future: Unpin, - U1::Service: Unpin, - ::Future: Unpin + 'static, + ::Future: 'static, { HttpService { upgrade, @@ -166,22 +155,17 @@ where impl ServiceFactory for HttpService where - T: IoStream + Unpin, + T: IoStream, S: ServiceFactory, - S::Service: Unpin, - S::Error: Into + Unpin + 'static, + S::Error: Into + 'static, S::InitError: fmt::Debug, - S::Response: Into> + Unpin + 'static, - S::Future: Unpin, - S::Service: Unpin, - ::Future: Unpin + 'static, + S::Response: Into> + 'static, + ::Future: 'static, B: MessageBody + 'static, X: ServiceFactory, X::Error: Into, X::InitError: fmt::Debug, - X::Future: Unpin, - X::Service: Unpin, - ::Future: Unpin + 'static, + ::Future: 'static, U: ServiceFactory< Config = SrvConfig, Request = (Request, Framed), @@ -189,10 +173,7 @@ where >, U::Error: fmt::Display, U::InitError: fmt::Debug, - U::Future: Unpin, - U::Service: Unpin, - ::Future: Unpin + 'static, - P: Unpin, + ::Future: 'static, { type Config = SrvConfig; type Request = ServerIo; @@ -217,6 +198,7 @@ where } #[doc(hidden)] +#[pin_project] pub struct HttpServiceResponse< T, P, @@ -225,8 +207,11 @@ pub struct HttpServiceResponse< X: ServiceFactory, U: ServiceFactory, > { + #[pin] fut: S::Future, + #[pin] fut_ex: Option, + #[pin] fut_upg: Option, expect: Option, upgrade: Option, @@ -239,53 +224,50 @@ impl Future for HttpServiceResponse where T: IoStream, S: ServiceFactory, - S::Error: Into + Unpin + 'static, + S::Error: Into + 'static, S::InitError: fmt::Debug, - S::Response: Into> + Unpin + 'static, - S::Future: Unpin, - S::Service: Unpin, - ::Future: Unpin + 'static, + S::Response: Into> + 'static, + ::Future: 'static, B: MessageBody + 'static, X: ServiceFactory, X::Error: Into, X::InitError: fmt::Debug, - X::Future: Unpin, - X::Service: Unpin, - ::Future: Unpin + 'static, + ::Future: 'static, U: ServiceFactory), Response = ()>, U::Error: fmt::Display, U::InitError: fmt::Debug, - U::Future: Unpin, - U::Service: Unpin, - ::Future: Unpin + 'static, - P: Unpin, + ::Future: 'static, { type Output = Result, ()>; - fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { - let this = self.get_mut(); + fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { + let mut this = self.as_mut().project(); - if let Some(ref mut fut) = this.fut_ex { - let expect = ready!(Pin::new(fut) + if let Some(fut) = this.fut_ex.as_pin_mut() { + let expect = ready!(fut .poll(cx) .map_err(|e| log::error!("Init http service error: {:?}", e)))?; - this.expect = Some(expect); - this.fut_ex.take(); + this = self.as_mut().project(); + *this.expect = Some(expect); + this.fut_ex.set(None); } - if let Some(ref mut fut) = this.fut_upg { - let upgrade = ready!(Pin::new(fut) + if let Some(fut) = this.fut_upg.as_pin_mut() { + let upgrade = ready!(fut .poll(cx) .map_err(|e| log::error!("Init http service error: {:?}", e)))?; - this.upgrade = Some(upgrade); - this.fut_ex.take(); + this = self.as_mut().project(); + *this.upgrade = Some(upgrade); + this.fut_ex.set(None); } - let result = ready!(Pin::new(&mut this.fut) + let result = ready!(this + .fut .poll(cx) .map_err(|e| log::error!("Init http service error: {:?}", e))); Poll::Ready(result.map(|service| { + let this = self.as_mut().project(); HttpServiceHandler::new( this.cfg.take().unwrap(), service, @@ -309,19 +291,15 @@ pub struct HttpServiceHandler { impl HttpServiceHandler where - S: Service + Unpin, - S::Error: Into + Unpin + 'static, + S: Service, + S::Error: Into + 'static, S::Future: 'static, - S::Response: Into> + Unpin + 'static, - S::Future: Unpin, + S::Response: Into> + 'static, B: MessageBody + 'static, - X: Service + Unpin, - X::Future: Unpin, + X: Service, X::Error: Into, - U: Service), Response = ()> + Unpin, - U::Future: Unpin, + U: Service), Response = ()>, U::Error: fmt::Display, - P: Unpin, { fn new( cfg: ServiceConfig, @@ -343,19 +321,16 @@ where impl Service for HttpServiceHandler where - T: IoStream + Unpin, - S: Service + Unpin, - S::Error: Into + Unpin + 'static, - S::Future: Unpin + 'static, - S::Response: Into> + Unpin + 'static, + T: IoStream, + S: Service, + S::Error: Into + 'static, + S::Future: 'static, + S::Response: Into> + 'static, B: MessageBody + 'static, - X: Service + Unpin, + X: Service, X::Error: Into, - X::Future: Unpin, - U: Service), Response = ()> + Unpin, + U: Service), Response = ()>, U::Error: fmt::Display, - U::Future: Unpin, - P: Unpin, { type Request = ServerIo; type Response = (); @@ -442,22 +417,21 @@ where } } +#[pin_project] enum State where - S: Service + Unpin, - S::Future: Unpin + 'static, + S: Service, + S::Future: 'static, S::Error: Into, - T: IoStream + Unpin, + T: IoStream, B: MessageBody, - X: Service + Unpin, + X: Service, X::Error: Into, - X::Future: Unpin, - U: Service), Response = ()> + Unpin, + U: Service), Response = ()>, U::Error: fmt::Display, - U::Future: Unpin, { - H1(h1::Dispatcher), - H2(Dispatcher, S, B>), + H1(#[pin] h1::Dispatcher), + H2(#[pin] Dispatcher, S, B>), Unknown( Option<( T, @@ -480,21 +454,21 @@ where ), } +#[pin_project] pub struct HttpServiceHandlerResponse where - T: IoStream + Unpin, - S: Service + Unpin, - S::Error: Into + Unpin + 'static, - S::Future: Unpin + 'static, - S::Response: Into> + Unpin + 'static, + T: IoStream, + S: Service, + S::Error: Into + 'static, + S::Future: 'static, + S::Response: Into> + 'static, B: MessageBody + 'static, - X: Service + Unpin, + X: Service, X::Error: Into, - X::Future: Unpin, - U: Service), Response = ()> + Unpin, + U: Service), Response = ()>, U::Error: fmt::Display, - U::Future: Unpin, { + #[pin] state: State, } @@ -502,25 +476,45 @@ const HTTP2_PREFACE: [u8; 14] = *b"PRI * HTTP/2.0"; impl Future for HttpServiceHandlerResponse where - T: IoStream + Unpin, - S: Service + Unpin, - S::Error: Into + Unpin + 'static, - S::Future: Unpin + 'static, - S::Response: Into> + Unpin + 'static, + T: IoStream, + S: Service, + S::Error: Into + 'static, + S::Future: 'static, + S::Response: Into> + 'static, B: MessageBody, - X: Service + Unpin, - X::Future: Unpin, + X: Service, X::Error: Into, - U: Service), Response = ()> + Unpin, - U::Future: Unpin, + U: Service), Response = ()>, U::Error: fmt::Display, { type Output = Result<(), DispatchError>; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { - match self.state { - State::H1(ref mut disp) => Pin::new(disp).poll(cx), - State::H2(ref mut disp) => Pin::new(disp).poll(cx), + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + self.project().state.poll(cx) + } +} + +impl State +where + T: IoStream, + S: Service, + S::Error: Into + 'static, + S::Response: Into> + 'static, + B: MessageBody + 'static, + X: Service, + X::Error: Into, + U: Service), Response = ()>, + U::Error: fmt::Display, +{ + #[project] + fn poll( + mut self: Pin<&mut Self>, + cx: &mut Context, + ) -> Poll> { + #[project] + match self.as_mut().project() { + State::H1(disp) => disp.poll(cx), + State::H2(disp) => disp.poll(cx), State::Unknown(ref mut data) => { if let Some(ref mut item) = data { loop { @@ -549,15 +543,15 @@ where inner: io, unread: Some(buf), }; - self.state = State::Handshake(Some(( + self.set(State::Handshake(Some(( server::handshake(io), cfg, srv, peer_addr, on_connect, - ))); + )))); } else { - self.state = State::H1(h1::Dispatcher::with_timeout( + self.set(State::H1(h1::Dispatcher::with_timeout( io, h1::Codec::new(cfg.clone()), cfg, @@ -567,7 +561,7 @@ where expect, upgrade, on_connect, - )) + ))) } self.poll(cx) } @@ -585,9 +579,9 @@ where panic!() }; let (_, cfg, srv, peer_addr, on_connect) = data.take().unwrap(); - self.state = State::H2(Dispatcher::new( + self.set(State::H2(Dispatcher::new( srv, conn, on_connect, cfg, None, peer_addr, - )); + ))); self.poll(cx) } } @@ -595,13 +589,13 @@ where } /// Wrapper for `AsyncRead + AsyncWrite` types +#[pin_project::pin_project] struct Io { unread: Option, + #[pin] inner: T, } -impl Unpin for Io {} - impl io::Read for Io { fn read(&mut self, buf: &mut [u8]) -> io::Result { if let Some(mut bytes) = self.unread.take() { @@ -627,7 +621,7 @@ impl io::Write for Io { } } -impl AsyncRead for Io { +impl AsyncRead for Io { // unsafe fn initializer(&self) -> io::Initializer { // self.get_mut().inner.initializer() // } @@ -641,7 +635,19 @@ impl AsyncRead for Io { cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { - Pin::new(&mut self.get_mut().inner).poll_read(cx, buf) + let this = self.project(); + + if let Some(mut bytes) = this.unread.take() { + let size = std::cmp::min(buf.len(), bytes.len()); + buf[..size].copy_from_slice(&bytes[..size]); + if bytes.len() > size { + bytes.split_to(size); + *this.unread = Some(bytes); + } + Poll::Ready(Ok(size)) + } else { + this.inner.poll_read(cx, buf) + } } // fn poll_read_vectored( @@ -653,32 +659,24 @@ impl AsyncRead for Io { // } } -impl tokio_io::AsyncWrite for Io { +impl tokio_io::AsyncWrite for Io { fn poll_write( self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { - Pin::new(&mut self.get_mut().inner).poll_write(cx, buf) + self.project().inner.poll_write(cx, buf) } - // fn poll_write_vectored( - // self: Pin<&mut Self>, - // cx: &mut Context<'_>, - // bufs: &[io::IoSlice<'_>], - // ) -> Poll> { - // self.get_mut().inner.poll_write_vectored(cx, bufs) - // } - fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - Pin::new(&mut self.get_mut().inner).poll_flush(cx) + self.project().inner.poll_flush(cx) } fn poll_shutdown( self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - Pin::new(&mut self.get_mut().inner).poll_shutdown(cx) + self.project().inner.poll_shutdown(cx) } } diff --git a/actix-http/src/ws/transport.rs b/actix-http/src/ws/transport.rs index c55e2eeb..58ba3160 100644 --- a/actix-http/src/ws/transport.rs +++ b/actix-http/src/ws/transport.rs @@ -11,17 +11,17 @@ use super::{Codec, Frame, Message}; pub struct Transport where S: Service + 'static, - T: AsyncRead + AsyncWrite + Unpin, + T: AsyncRead + AsyncWrite, { inner: FramedTransport, } impl Transport where - T: AsyncRead + AsyncWrite + Unpin, - S: Service + Unpin, + T: AsyncRead + AsyncWrite, + S: Service, S::Future: 'static, - S::Error: Unpin + 'static, + S::Error: 'static, { pub fn new>(io: T, service: F) -> Self { Transport { @@ -38,10 +38,10 @@ where impl Future for Transport where - T: AsyncRead + AsyncWrite + Unpin, - S: Service + Unpin, + T: AsyncRead + AsyncWrite, + S: Service, S::Future: 'static, - S::Error: Unpin + 'static, + S::Error: 'static, { type Output = Result<(), FramedTransportError>; diff --git a/actix-http/tests/test_client.rs b/actix-http/tests/test_client.rs index a4f1569c..05248966 100644 --- a/actix-http/tests/test_client.rs +++ b/actix-http/tests/test_client.rs @@ -1,9 +1,9 @@ -use actix_service::NewService; +use actix_service::ServiceFactory; use bytes::Bytes; use futures::future::{self, ok}; use actix_http::{http, HttpService, Request, Response}; -use actix_http_test::TestServer; +use actix_http_test::{block_on, TestServer}; const STR: &str = "Hello World Hello World Hello World Hello World Hello World \ Hello World Hello World Hello World Hello World Hello World \ @@ -29,55 +29,63 @@ const STR: &str = "Hello World Hello World Hello World Hello World Hello World \ #[test] fn test_h1_v2() { - env_logger::init(); - let mut srv = TestServer::new(move || { - HttpService::build().finish(|_| future::ok::<_, ()>(Response::Ok().body(STR))) - }); - let response = srv.block_on(srv.get("/").send()).unwrap(); - assert!(response.status().is_success()); + block_on(async { + let srv = TestServer::start(move || { + HttpService::build() + .finish(|_| future::ok::<_, ()>(Response::Ok().body(STR))) + }); - let request = srv.get("/").header("x-test", "111").send(); - let response = srv.block_on(request).unwrap(); - assert!(response.status().is_success()); + let response = srv.get("/").send().await.unwrap(); + assert!(response.status().is_success()); - // read response - let bytes = srv.load_body(response).unwrap(); - assert_eq!(bytes, Bytes::from_static(STR.as_ref())); + let request = srv.get("/").header("x-test", "111").send(); + let mut response = request.await.unwrap(); + assert!(response.status().is_success()); - let response = srv.block_on(srv.post("/").send()).unwrap(); - assert!(response.status().is_success()); + // read response + let bytes = response.body().await.unwrap(); + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); - // read response - let bytes = srv.load_body(response).unwrap(); - assert_eq!(bytes, Bytes::from_static(STR.as_ref())); + let mut response = srv.post("/").send().await.unwrap(); + assert!(response.status().is_success()); + + // read response + let bytes = response.body().await.unwrap(); + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); + }) } #[test] fn test_connection_close() { - let mut srv = TestServer::new(move || { - HttpService::build() - .finish(|_| ok::<_, ()>(Response::Ok().body(STR))) - .map(|_| ()) - }); - let response = srv.block_on(srv.get("/").force_close().send()).unwrap(); - assert!(response.status().is_success()); + block_on(async { + let srv = TestServer::start(move || { + HttpService::build() + .finish(|_| ok::<_, ()>(Response::Ok().body(STR))) + .map(|_| ()) + }); + + let response = srv.get("/").force_close().send().await.unwrap(); + assert!(response.status().is_success()); + }) } #[test] fn test_with_query_parameter() { - let mut srv = TestServer::new(move || { - HttpService::build() - .finish(|req: Request| { - if req.uri().query().unwrap().contains("qp=") { - ok::<_, ()>(Response::Ok().finish()) - } else { - ok::<_, ()>(Response::BadRequest().finish()) - } - }) - .map(|_| ()) - }); + block_on(async { + let srv = TestServer::start(move || { + HttpService::build() + .finish(|req: Request| { + if req.uri().query().unwrap().contains("qp=") { + ok::<_, ()>(Response::Ok().finish()) + } else { + ok::<_, ()>(Response::BadRequest().finish()) + } + }) + .map(|_| ()) + }); - let request = srv.request(http::Method::GET, srv.url("/?qp=5")).send(); - let response = srv.block_on(request).unwrap(); - assert!(response.status().is_success()); + let request = srv.request(http::Method::GET, srv.url("/?qp=5")); + let response = request.send().await.unwrap(); + assert!(response.status().is_success()); + }) } diff --git a/actix-http/tests/test_openssl.rs b/actix-http/tests/test_openssl.rs new file mode 100644 index 00000000..7eaa8e2a --- /dev/null +++ b/actix-http/tests/test_openssl.rs @@ -0,0 +1,545 @@ +#![cfg(feature = "openssl")] +use std::io; + +use actix_codec::{AsyncRead, AsyncWrite}; +use actix_http_test::{block_on, TestServer}; +use actix_server::ssl::OpensslAcceptor; +use actix_server_config::ServerConfig; +use actix_service::{factory_fn_cfg, pipeline_factory, service_fn2, ServiceFactory}; + +use bytes::{Bytes, BytesMut}; +use futures::future::{err, ok, ready}; +use futures::stream::{once, Stream, StreamExt}; +use open_ssl::ssl::{AlpnError, SslAcceptor, SslFiletype, SslMethod}; + +use actix_http::error::{ErrorBadRequest, PayloadError}; +use actix_http::http::header::{self, HeaderName, HeaderValue}; +use actix_http::http::{Method, StatusCode, Version}; +use actix_http::httpmessage::HttpMessage; +use actix_http::{body, Error, HttpService, Request, Response}; + +async fn load_body(stream: S) -> Result +where + S: Stream>, +{ + let body = stream + .map(|res| match res { + Ok(chunk) => chunk, + Err(_) => panic!(), + }) + .fold(BytesMut::new(), move |mut body, chunk| { + body.extend_from_slice(&chunk); + ready(body) + }) + .await; + + Ok(body) +} + +fn ssl_acceptor() -> io::Result> { + // 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() -> io::Result<()> { + block_on(async { + let openssl = ssl_acceptor()?; + let srv = TestServer::start(move || { + pipeline_factory( + openssl + .clone() + .map_err(|e| println!("Openssl error: {}", e)), + ) + .and_then( + HttpService::build() + .h2(|_| ok::<_, Error>(Response::Ok().finish())) + .map_err(|_| ()), + ) + }); + + let response = srv.sget("/").send().await.unwrap(); + assert!(response.status().is_success()); + Ok(()) + }) +} + +#[test] +fn test_h2_1() -> io::Result<()> { + block_on(async { + let openssl = ssl_acceptor()?; + let srv = TestServer::start(move || { + pipeline_factory( + 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.sget("/").send().await.unwrap(); + assert!(response.status().is_success()); + Ok(()) + }) +} + +#[test] +fn test_h2_body() -> io::Result<()> { + block_on(async { + let data = "HELLOWORLD".to_owned().repeat(64 * 1024); + let openssl = ssl_acceptor()?; + let mut srv = TestServer::start(move || { + pipeline_factory( + openssl + .clone() + .map_err(|e| println!("Openssl error: {}", e)), + ) + .and_then( + HttpService::build() + .h2(|mut req: Request<_>| { + async move { + let body = load_body(req.take_payload()).await?; + Ok::<_, Error>(Response::Ok().body(body)) + } + }) + .map_err(|_| ()), + ) + }); + + let response = srv.sget("/").send_body(data.clone()).await.unwrap(); + assert!(response.status().is_success()); + + let body = srv.load_body(response).await.unwrap(); + assert_eq!(&body, data.as_bytes()); + Ok(()) + }) +} + +#[test] +fn test_h2_content_length() { + block_on(async { + let openssl = ssl_acceptor().unwrap(); + + let srv = TestServer::start(move || { + pipeline_factory( + 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 = req.await.unwrap(); + assert_eq!(response.headers().get(&header), None); + + let req = srv + .request(Method::HEAD, srv.surl(&format!("/{}", i))) + .send(); + let response = req.await.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 = req.await.unwrap(); + assert_eq!(response.headers().get(&header), Some(&value)); + } + } + }) +} + +#[test] +fn test_h2_headers() { + block_on(async { + let data = STR.repeat(10); + let data2 = data.clone(); + let openssl = ssl_acceptor().unwrap(); + + let mut srv = TestServer::start(move || { + let data = data.clone(); + pipeline_factory(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.sget("/").send().await.unwrap(); + assert!(response.status().is_success()); + + // read response + let bytes = srv.load_body(response).await.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() { + block_on(async { + let openssl = ssl_acceptor().unwrap(); + let mut srv = TestServer::start(move || { + pipeline_factory( + openssl + .clone() + .map_err(|e| println!("Openssl error: {}", e)), + ) + .and_then( + HttpService::build() + .h2(|_| ok::<_, ()>(Response::Ok().body(STR))) + .map_err(|_| ()), + ) + }); + + let response = srv.sget("/").send().await.unwrap(); + assert!(response.status().is_success()); + + // read response + let bytes = srv.load_body(response).await.unwrap(); + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); + }) +} + +#[test] +fn test_h2_head_empty() { + block_on(async { + let openssl = ssl_acceptor().unwrap(); + let mut srv = TestServer::start(move || { + pipeline_factory( + openssl + .clone() + .map_err(|e| println!("Openssl error: {}", e)), + ) + .and_then( + HttpService::build() + .finish(|_| ok::<_, ()>(Response::Ok().body(STR))) + .map_err(|_| ()), + ) + }); + + let response = srv.shead("/").send().await.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).await.unwrap(); + assert!(bytes.is_empty()); + }) +} + +#[test] +fn test_h2_head_binary() { + block_on(async { + let openssl = ssl_acceptor().unwrap(); + let mut srv = TestServer::start(move || { + pipeline_factory( + 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.shead("/").send().await.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).await.unwrap(); + assert!(bytes.is_empty()); + }) +} + +#[test] +fn test_h2_head_binary2() { + block_on(async { + let openssl = ssl_acceptor().unwrap(); + let srv = TestServer::start(move || { + pipeline_factory( + openssl + .clone() + .map_err(|e| println!("Openssl error: {}", e)), + ) + .and_then( + HttpService::build() + .h2(|_| ok::<_, ()>(Response::Ok().body(STR))) + .map_err(|_| ()), + ) + }); + + let response = srv.shead("/").send().await.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() { + block_on(async { + let openssl = ssl_acceptor().unwrap(); + let mut srv = TestServer::start(move || { + pipeline_factory( + 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.sget("/").send().await.unwrap(); + assert!(response.status().is_success()); + + // read response + let bytes = srv.load_body(response).await.unwrap(); + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); + }) +} + +#[test] +fn test_h2_body_chunked_explicit() { + block_on(async { + let openssl = ssl_acceptor().unwrap(); + let mut srv = TestServer::start(move || { + pipeline_factory( + openssl + .clone() + .map_err(|e| println!("Openssl error: {}", e)), + ) + .and_then( + HttpService::build() + .h2(|_| { + let body = + once(ok::<_, Error>(Bytes::from_static(STR.as_ref()))); + ok::<_, ()>( + Response::Ok() + .header(header::TRANSFER_ENCODING, "chunked") + .streaming(body), + ) + }) + .map_err(|_| ()), + ) + }); + + let response = srv.sget("/").send().await.unwrap(); + assert!(response.status().is_success()); + assert!(!response.headers().contains_key(header::TRANSFER_ENCODING)); + + // read response + let bytes = srv.load_body(response).await.unwrap(); + + // decode + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); + }) +} + +#[test] +fn test_h2_response_http_error_handling() { + block_on(async { + let openssl = ssl_acceptor().unwrap(); + + let mut srv = TestServer::start(move || { + pipeline_factory( + openssl + .clone() + .map_err(|e| println!("Openssl error: {}", e)), + ) + .and_then( + HttpService::build() + .h2(factory_fn_cfg(|_: &ServerConfig| { + ok::<_, ()>(service_fn2(|_| { + 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.sget("/").send().await.unwrap(); + assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR); + + // read response + let bytes = srv.load_body(response).await.unwrap(); + assert_eq!(bytes, Bytes::from_static(b"failed to parse header value")); + }) +} + +#[test] +fn test_h2_service_error() { + block_on(async { + let openssl = ssl_acceptor().unwrap(); + + let mut srv = TestServer::start(move || { + pipeline_factory( + openssl + .clone() + .map_err(|e| println!("Openssl error: {}", e)), + ) + .and_then( + HttpService::build() + .h2(|_| err::(ErrorBadRequest("error"))) + .map_err(|_| ()), + ) + }); + + let response = srv.sget("/").send().await.unwrap(); + assert_eq!(response.status(), StatusCode::BAD_REQUEST); + + // read response + let bytes = srv.load_body(response).await.unwrap(); + assert_eq!(bytes, Bytes::from_static(b"error")); + }) +} + +#[test] +fn test_h2_on_connect() { + block_on(async { + let openssl = ssl_acceptor().unwrap(); + + let srv = TestServer::start(move || { + pipeline_factory( + openssl + .clone() + .map_err(|e| println!("Openssl error: {}", e)), + ) + .and_then( + HttpService::build() + .on_connect(|_| 10usize) + .h2(|req: Request| { + assert!(req.extensions().contains::()); + ok::<_, ()>(Response::Ok().finish()) + }) + .map_err(|_| ()), + ) + }); + + let response = srv.sget("/").send().await.unwrap(); + assert!(response.status().is_success()); + }) +} diff --git a/actix-http/tests/test_rustls.rs b/actix-http/tests/test_rustls.rs new file mode 100644 index 00000000..c36d0579 --- /dev/null +++ b/actix-http/tests/test_rustls.rs @@ -0,0 +1,474 @@ +#![cfg(feature = "rustls")] +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::{block_on, TestServer}; +use actix_server::ssl::RustlsAcceptor; +use actix_server_config::ServerConfig; +use actix_service::{factory_fn_cfg, pipeline_factory, service_fn2, ServiceFactory}; + +use bytes::{Bytes, BytesMut}; +use futures::future::{self, err, ok}; +use futures::stream::{once, Stream, StreamExt}; +use rust_tls::{ + internal::pemfile::{certs, pkcs8_private_keys}, + NoClientAuth, ServerConfig as RustlsServerConfig, +}; + +use std::fs::File; +use std::io::{self, BufReader}; + +async fn load_body(mut stream: S) -> Result +where + S: Stream> + Unpin, +{ + let mut body = BytesMut::new(); + while let Some(item) = stream.next().await { + body.extend_from_slice(&item?) + } + Ok(body) +} + +fn ssl_acceptor() -> io::Result> { + // 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() -> io::Result<()> { + block_on(async { + let rustls = ssl_acceptor()?; + let srv = TestServer::start(move || { + pipeline_factory(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.sget("/").send().await.unwrap(); + assert!(response.status().is_success()); + Ok(()) + }) +} + +#[test] +fn test_h2_1() -> io::Result<()> { + block_on(async { + let rustls = ssl_acceptor()?; + let srv = TestServer::start(move || { + pipeline_factory(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.sget("/").send().await.unwrap(); + assert!(response.status().is_success()); + Ok(()) + }) +} + +#[test] +fn test_h2_body1() -> io::Result<()> { + block_on(async { + let data = "HELLOWORLD".to_owned().repeat(64 * 1024); + let rustls = ssl_acceptor()?; + let mut srv = TestServer::start(move || { + pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) + .and_then( + HttpService::build() + .h2(|mut req: Request<_>| { + async move { + let body = load_body(req.take_payload()).await?; + Ok::<_, Error>(Response::Ok().body(body)) + } + }) + .map_err(|_| ()), + ) + }); + + let response = srv.sget("/").send_body(data.clone()).await.unwrap(); + assert!(response.status().is_success()); + + let body = srv.load_body(response).await.unwrap(); + assert_eq!(&body, data.as_bytes()); + Ok(()) + }) +} + +#[test] +fn test_h2_content_length() { + block_on(async { + let rustls = ssl_acceptor().unwrap(); + + let srv = TestServer::start(move || { + pipeline_factory(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 = req.await.unwrap(); + assert_eq!(response.headers().get(&header), None); + + let req = srv + .request(Method::HEAD, srv.surl(&format!("/{}", i))) + .send(); + let response = req.await.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 = req.await.unwrap(); + assert_eq!(response.headers().get(&header), Some(&value)); + } + } + }) +} + +#[test] +fn test_h2_headers() { + block_on(async { + let data = STR.repeat(10); + let data2 = data.clone(); + let rustls = ssl_acceptor().unwrap(); + + let mut srv = TestServer::start(move || { + let data = data.clone(); + pipeline_factory(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.sget("/").send().await.unwrap(); + assert!(response.status().is_success()); + + // read response + let bytes = srv.load_body(response).await.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() { + block_on(async { + let rustls = ssl_acceptor().unwrap(); + let mut srv = TestServer::start(move || { + pipeline_factory(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.sget("/").send().await.unwrap(); + assert!(response.status().is_success()); + + // read response + let bytes = srv.load_body(response).await.unwrap(); + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); + }) +} + +#[test] +fn test_h2_head_empty() { + block_on(async { + let rustls = ssl_acceptor().unwrap(); + let mut srv = TestServer::start(move || { + pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) + .and_then( + HttpService::build() + .finish(|_| ok::<_, ()>(Response::Ok().body(STR))) + .map_err(|_| ()), + ) + }); + + let response = srv.shead("/").send().await.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).await.unwrap(); + assert!(bytes.is_empty()); + }) +} + +#[test] +fn test_h2_head_binary() { + block_on(async { + let rustls = ssl_acceptor().unwrap(); + let mut srv = TestServer::start(move || { + pipeline_factory(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.shead("/").send().await.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).await.unwrap(); + assert!(bytes.is_empty()); + }) +} + +#[test] +fn test_h2_head_binary2() { + block_on(async { + let rustls = ssl_acceptor().unwrap(); + let srv = TestServer::start(move || { + pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) + .and_then( + HttpService::build() + .h2(|_| ok::<_, ()>(Response::Ok().body(STR))) + .map_err(|_| ()), + ) + }); + + let response = srv.shead("/").send().await.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() { + block_on(async { + let rustls = ssl_acceptor().unwrap(); + let mut srv = TestServer::start(move || { + pipeline_factory(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.sget("/").send().await.unwrap(); + assert!(response.status().is_success()); + + // read response + let bytes = srv.load_body(response).await.unwrap(); + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); + }) +} + +#[test] +fn test_h2_body_chunked_explicit() { + block_on(async { + let rustls = ssl_acceptor().unwrap(); + let mut srv = TestServer::start(move || { + pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) + .and_then( + HttpService::build() + .h2(|_| { + let body = + once(ok::<_, Error>(Bytes::from_static(STR.as_ref()))); + ok::<_, ()>( + Response::Ok() + .header(header::TRANSFER_ENCODING, "chunked") + .streaming(body), + ) + }) + .map_err(|_| ()), + ) + }); + + let response = srv.sget("/").send().await.unwrap(); + assert!(response.status().is_success()); + assert!(!response.headers().contains_key(header::TRANSFER_ENCODING)); + + // read response + let bytes = srv.load_body(response).await.unwrap(); + + // decode + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); + }) +} + +#[test] +fn test_h2_response_http_error_handling() { + block_on(async { + let rustls = ssl_acceptor().unwrap(); + + let mut srv = TestServer::start(move || { + pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) + .and_then( + HttpService::build() + .h2(factory_fn_cfg(|_: &ServerConfig| { + ok::<_, ()>(service_fn2(|_| { + 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.sget("/").send().await.unwrap(); + assert_eq!(response.status(), http::StatusCode::INTERNAL_SERVER_ERROR); + + // read response + let bytes = srv.load_body(response).await.unwrap(); + assert_eq!(bytes, Bytes::from_static(b"failed to parse header value")); + }) +} + +#[test] +fn test_h2_service_error() { + block_on(async { + let rustls = ssl_acceptor().unwrap(); + + let mut srv = TestServer::start(move || { + pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) + .and_then( + HttpService::build() + .h2(|_| err::(error::ErrorBadRequest("error"))) + .map_err(|_| ()), + ) + }); + + let response = srv.sget("/").send().await.unwrap(); + assert_eq!(response.status(), http::StatusCode::BAD_REQUEST); + + // read response + let bytes = srv.load_body(response).await.unwrap(); + assert_eq!(bytes, Bytes::from_static(b"error")); + }) +} diff --git a/actix-http/tests/test_rustls_server.rs b/actix-http/tests/test_rustls_server.rs deleted file mode 100644 index b74fd07b..00000000 --- a/actix-http/tests/test_rustls_server.rs +++ /dev/null @@ -1,462 +0,0 @@ -#![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(stream: S) -> impl Future -where - S: Stream, -{ - stream.fold(BytesMut::new(), move |mut body, chunk| { - body.extend_from_slice(&chunk); - Ok::<_, PayloadError>(body) - }) -} - -fn ssl_acceptor() -> Result> { - // 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::(error::ErrorBadRequest("error"))) - .map_err(|_| ()), - ) - }); - - let response = srv.block_on(srv.sget("/").send()).unwrap(); - assert_eq!(response.status(), http::StatusCode::BAD_REQUEST); - - // read response - let bytes = srv.load_body(response).unwrap(); - assert_eq!(bytes, Bytes::from_static(b"error")); -} diff --git a/actix-http/tests/test_server.rs b/actix-http/tests/test_server.rs index ef861b30..c37e8fad 100644 --- a/actix-http/tests/test_server.rs +++ b/actix-http/tests/test_server.rs @@ -2,7 +2,7 @@ use std::io::{Read, Write}; use std::time::Duration; use std::{net, thread}; -use actix_http_test::{block_fn, TestServer}; +use actix_http_test::{block_on, TestServer}; use actix_server_config::ServerConfig; use actix_service::{factory_fn_cfg, pipeline, service_fn, ServiceFactory}; use bytes::Bytes; @@ -18,345 +18,379 @@ use actix_http::{ #[test] fn test_h1() { - let srv = TestServer::new(|| { - HttpService::build() - .keep_alive(KeepAlive::Disabled) - .client_timeout(1000) - .client_disconnect(1000) - .h1(|req: Request| { - assert!(req.peer_addr().is_some()); - future::ok::<_, ()>(Response::Ok().finish()) - }) - }); + block_on(async { + let srv = TestServer::start(|| { + HttpService::build() + .keep_alive(KeepAlive::Disabled) + .client_timeout(1000) + .client_disconnect(1000) + .h1(|req: Request| { + assert!(req.peer_addr().is_some()); + future::ok::<_, ()>(Response::Ok().finish()) + }) + }); - let response = block_fn(|| srv.get("/").send()).unwrap(); - assert!(response.status().is_success()); + let response = srv.get("/").send().await.unwrap(); + assert!(response.status().is_success()); + }) } #[test] fn test_h1_2() { - let srv = TestServer::new(|| { - HttpService::build() - .keep_alive(KeepAlive::Disabled) - .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()) - }) - .map(|_| ()) - }); + block_on(async { + let srv = TestServer::start(|| { + HttpService::build() + .keep_alive(KeepAlive::Disabled) + .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()) + }) + .map(|_| ()) + }); - let response = block_fn(|| srv.get("/").send()).unwrap(); - assert!(response.status().is_success()); + let response = srv.get("/").send().await.unwrap(); + assert!(response.status().is_success()); + }) } #[test] fn test_expect_continue() { - let srv = TestServer::new(|| { - HttpService::build() - .expect(service_fn(|req: Request| { - if req.head().uri.query() == Some("yes=") { - ok(req) - } else { - err(error::ErrorPreconditionFailed("error")) - } - })) - .finish(|_| future::ok::<_, ()>(Response::Ok().finish())) - }); - - let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); - let _ = stream.write_all(b"GET /test HTTP/1.1\r\nexpect: 100-continue\r\n\r\n"); - let mut data = String::new(); - let _ = stream.read_to_string(&mut data); - assert!(data.starts_with("HTTP/1.1 412 Precondition Failed\r\ncontent-length")); - - let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); - let _ = stream.write_all(b"GET /test?yes= HTTP/1.1\r\nexpect: 100-continue\r\n\r\n"); - let mut data = String::new(); - let _ = stream.read_to_string(&mut data); - assert!(data.starts_with("HTTP/1.1 100 Continue\r\n\r\nHTTP/1.1 200 OK\r\n")); -} - -#[test] -fn test_expect_continue_h1() { - let srv = TestServer::new(|| { - HttpService::build() - .expect(service_fn(|req: Request| { - delay_for(Duration::from_millis(20)).then(move |_| { + block_on(async { + let srv = TestServer::start(|| { + HttpService::build() + .expect(service_fn(|req: Request| { if req.head().uri.query() == Some("yes=") { ok(req) } else { err(error::ErrorPreconditionFailed("error")) } - }) - })) - .h1(|_| future::ok::<_, ()>(Response::Ok().finish())) - }); + })) + .finish(|_| future::ok::<_, ()>(Response::Ok().finish())) + }); - let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); - let _ = stream.write_all(b"GET /test HTTP/1.1\r\nexpect: 100-continue\r\n\r\n"); - let mut data = String::new(); - let _ = stream.read_to_string(&mut data); - assert!(data.starts_with("HTTP/1.1 412 Precondition Failed\r\ncontent-length")); + let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); + let _ = stream.write_all(b"GET /test HTTP/1.1\r\nexpect: 100-continue\r\n\r\n"); + let mut data = String::new(); + let _ = stream.read_to_string(&mut data); + assert!(data.starts_with("HTTP/1.1 412 Precondition Failed\r\ncontent-length")); - let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); - let _ = stream.write_all(b"GET /test?yes= HTTP/1.1\r\nexpect: 100-continue\r\n\r\n"); - let mut data = String::new(); - let _ = stream.read_to_string(&mut data); - assert!(data.starts_with("HTTP/1.1 100 Continue\r\n\r\nHTTP/1.1 200 OK\r\n")); + let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); + let _ = + stream.write_all(b"GET /test?yes= HTTP/1.1\r\nexpect: 100-continue\r\n\r\n"); + let mut data = String::new(); + let _ = stream.read_to_string(&mut data); + assert!(data.starts_with("HTTP/1.1 100 Continue\r\n\r\nHTTP/1.1 200 OK\r\n")); + }) +} + +#[test] +fn test_expect_continue_h1() { + block_on(async { + let srv = TestServer::start(|| { + HttpService::build() + .expect(service_fn(|req: Request| { + delay_for(Duration::from_millis(20)).then(move |_| { + if req.head().uri.query() == Some("yes=") { + ok(req) + } else { + err(error::ErrorPreconditionFailed("error")) + } + }) + })) + .h1(|_| future::ok::<_, ()>(Response::Ok().finish())) + }); + + let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); + let _ = stream.write_all(b"GET /test HTTP/1.1\r\nexpect: 100-continue\r\n\r\n"); + let mut data = String::new(); + let _ = stream.read_to_string(&mut data); + assert!(data.starts_with("HTTP/1.1 412 Precondition Failed\r\ncontent-length")); + + let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); + let _ = + stream.write_all(b"GET /test?yes= HTTP/1.1\r\nexpect: 100-continue\r\n\r\n"); + let mut data = String::new(); + let _ = stream.read_to_string(&mut data); + assert!(data.starts_with("HTTP/1.1 100 Continue\r\n\r\nHTTP/1.1 200 OK\r\n")); + }) } #[test] fn test_chunked_payload() { - let chunk_sizes = vec![32768, 32, 32768]; - let total_size: usize = chunk_sizes.iter().sum(); + block_on(async { + let chunk_sizes = vec![32768, 32, 32768]; + let total_size: usize = chunk_sizes.iter().sum(); - let srv = TestServer::new(|| { - HttpService::build().h1(service_fn(|mut request: Request| { - request - .take_payload() - .map(|res| match res { - Ok(pl) => pl, - Err(e) => panic!(format!("Error reading payload: {}", e)), - }) - .fold(0usize, |acc, chunk| ready(acc + chunk.len())) - .map(|req_size| { - Ok::<_, Error>(Response::Ok().body(format!("size={}", req_size))) - }) - })) - }); + let srv = TestServer::start(|| { + HttpService::build().h1(service_fn(|mut request: Request| { + request + .take_payload() + .map(|res| match res { + Ok(pl) => pl, + Err(e) => panic!(format!("Error reading payload: {}", e)), + }) + .fold(0usize, |acc, chunk| ready(acc + chunk.len())) + .map(|req_size| { + Ok::<_, Error>(Response::Ok().body(format!("size={}", req_size))) + }) + })) + }); - let returned_size = { - let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); - let _ = stream - .write_all(b"POST /test HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\n"); + let returned_size = { + let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); + let _ = stream + .write_all(b"POST /test HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\n"); - for chunk_size in chunk_sizes.iter() { - let mut bytes = Vec::new(); - let random_bytes: Vec = - (0..*chunk_size).map(|_| rand::random::()).collect(); + for chunk_size in chunk_sizes.iter() { + let mut bytes = Vec::new(); + let random_bytes: Vec = + (0..*chunk_size).map(|_| rand::random::()).collect(); - bytes.extend(format!("{:X}\r\n", chunk_size).as_bytes()); - bytes.extend(&random_bytes[..]); - bytes.extend(b"\r\n"); - let _ = stream.write_all(&bytes); - } + bytes.extend(format!("{:X}\r\n", chunk_size).as_bytes()); + bytes.extend(&random_bytes[..]); + bytes.extend(b"\r\n"); + let _ = stream.write_all(&bytes); + } - let _ = stream.write_all(b"0\r\n\r\n"); - stream.shutdown(net::Shutdown::Write).unwrap(); + let _ = stream.write_all(b"0\r\n\r\n"); + stream.shutdown(net::Shutdown::Write).unwrap(); - let mut data = String::new(); - let _ = stream.read_to_string(&mut data); + let mut data = String::new(); + let _ = stream.read_to_string(&mut data); - let re = Regex::new(r"size=(\d+)").unwrap(); - let size: usize = match re.captures(&data) { - Some(caps) => caps.get(1).unwrap().as_str().parse().unwrap(), - None => panic!(format!("Failed to find size in HTTP Response: {}", data)), + let re = Regex::new(r"size=(\d+)").unwrap(); + let size: usize = match re.captures(&data) { + Some(caps) => caps.get(1).unwrap().as_str().parse().unwrap(), + None => { + panic!(format!("Failed to find size in HTTP Response: {}", data)) + } + }; + size }; - size - }; - assert_eq!(returned_size, total_size); + assert_eq!(returned_size, total_size); + }) } #[test] fn test_slow_request() { - let srv = TestServer::new(|| { - HttpService::build() - .client_timeout(100) - .finish(|_| future::ok::<_, ()>(Response::Ok().finish())) - }); + block_on(async { + let srv = TestServer::start(|| { + HttpService::build() + .client_timeout(100) + .finish(|_| future::ok::<_, ()>(Response::Ok().finish())) + }); - let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); - let _ = stream.write_all(b"GET /test/tests/test HTTP/1.1\r\n"); - let mut data = String::new(); - let _ = stream.read_to_string(&mut data); - assert!(data.starts_with("HTTP/1.1 408 Request Timeout")); + let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); + let _ = stream.write_all(b"GET /test/tests/test HTTP/1.1\r\n"); + let mut data = String::new(); + let _ = stream.read_to_string(&mut data); + assert!(data.starts_with("HTTP/1.1 408 Request Timeout")); + }) } #[test] fn test_http1_malformed_request() { - let srv = TestServer::new(|| { - HttpService::build().h1(|_| future::ok::<_, ()>(Response::Ok().finish())) - }); + block_on(async { + let srv = TestServer::start(|| { + HttpService::build().h1(|_| future::ok::<_, ()>(Response::Ok().finish())) + }); - let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); - let _ = stream.write_all(b"GET /test/tests/test HTTP1.1\r\n"); - let mut data = String::new(); - let _ = stream.read_to_string(&mut data); - assert!(data.starts_with("HTTP/1.1 400 Bad Request")); + let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); + let _ = stream.write_all(b"GET /test/tests/test HTTP1.1\r\n"); + let mut data = String::new(); + let _ = stream.read_to_string(&mut data); + assert!(data.starts_with("HTTP/1.1 400 Bad Request")); + }) } #[test] fn test_http1_keepalive() { - let srv = TestServer::new(|| { - HttpService::build().h1(|_| future::ok::<_, ()>(Response::Ok().finish())) - }); + block_on(async { + let srv = TestServer::start(|| { + HttpService::build().h1(|_| future::ok::<_, ()>(Response::Ok().finish())) + }); - let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); - let _ = stream.write_all(b"GET /test/tests/test HTTP/1.1\r\n\r\n"); - let mut data = vec![0; 1024]; - let _ = stream.read(&mut data); - assert_eq!(&data[..17], b"HTTP/1.1 200 OK\r\n"); + let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); + let _ = stream.write_all(b"GET /test/tests/test HTTP/1.1\r\n\r\n"); + let mut data = vec![0; 1024]; + let _ = stream.read(&mut data); + assert_eq!(&data[..17], b"HTTP/1.1 200 OK\r\n"); - let _ = stream.write_all(b"GET /test/tests/test HTTP/1.1\r\n\r\n"); - let mut data = vec![0; 1024]; - let _ = stream.read(&mut data); - assert_eq!(&data[..17], b"HTTP/1.1 200 OK\r\n"); + let _ = stream.write_all(b"GET /test/tests/test HTTP/1.1\r\n\r\n"); + let mut data = vec![0; 1024]; + let _ = stream.read(&mut data); + assert_eq!(&data[..17], b"HTTP/1.1 200 OK\r\n"); + }) } #[test] fn test_http1_keepalive_timeout() { - let srv = TestServer::new(|| { - HttpService::build() - .keep_alive(1) - .h1(|_| future::ok::<_, ()>(Response::Ok().finish())) - }); + block_on(async { + let srv = TestServer::start(|| { + HttpService::build() + .keep_alive(1) + .h1(|_| future::ok::<_, ()>(Response::Ok().finish())) + }); - let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); - let _ = stream.write_all(b"GET /test/tests/test HTTP/1.1\r\n\r\n"); - let mut data = vec![0; 1024]; - let _ = stream.read(&mut data); - assert_eq!(&data[..17], b"HTTP/1.1 200 OK\r\n"); - thread::sleep(Duration::from_millis(1100)); + let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); + let _ = stream.write_all(b"GET /test/tests/test HTTP/1.1\r\n\r\n"); + let mut data = vec![0; 1024]; + let _ = stream.read(&mut data); + assert_eq!(&data[..17], b"HTTP/1.1 200 OK\r\n"); + thread::sleep(Duration::from_millis(1100)); - let mut data = vec![0; 1024]; - let res = stream.read(&mut data).unwrap(); - assert_eq!(res, 0); + let mut data = vec![0; 1024]; + let res = stream.read(&mut data).unwrap(); + assert_eq!(res, 0); + }) } #[test] fn test_http1_keepalive_close() { - let srv = TestServer::new(|| { - HttpService::build().h1(|_| future::ok::<_, ()>(Response::Ok().finish())) - }); + block_on(async { + let srv = TestServer::start(|| { + HttpService::build().h1(|_| future::ok::<_, ()>(Response::Ok().finish())) + }); - let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); - let _ = - stream.write_all(b"GET /test/tests/test HTTP/1.1\r\nconnection: close\r\n\r\n"); - let mut data = vec![0; 1024]; - let _ = stream.read(&mut data); - assert_eq!(&data[..17], b"HTTP/1.1 200 OK\r\n"); + let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); + let _ = stream + .write_all(b"GET /test/tests/test HTTP/1.1\r\nconnection: close\r\n\r\n"); + let mut data = vec![0; 1024]; + let _ = stream.read(&mut data); + assert_eq!(&data[..17], b"HTTP/1.1 200 OK\r\n"); - let mut data = vec![0; 1024]; - let res = stream.read(&mut data).unwrap(); - assert_eq!(res, 0); + let mut data = vec![0; 1024]; + let res = stream.read(&mut data).unwrap(); + assert_eq!(res, 0); + }) } #[test] fn test_http10_keepalive_default_close() { - let srv = TestServer::new(|| { - HttpService::build().h1(|_| future::ok::<_, ()>(Response::Ok().finish())) - }); + block_on(async { + let srv = TestServer::start(|| { + HttpService::build().h1(|_| future::ok::<_, ()>(Response::Ok().finish())) + }); - let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); - let _ = stream.write_all(b"GET /test/tests/test HTTP/1.0\r\n\r\n"); - let mut data = vec![0; 1024]; - let _ = stream.read(&mut data); - assert_eq!(&data[..17], b"HTTP/1.0 200 OK\r\n"); + let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); + let _ = stream.write_all(b"GET /test/tests/test HTTP/1.0\r\n\r\n"); + let mut data = vec![0; 1024]; + let _ = stream.read(&mut data); + assert_eq!(&data[..17], b"HTTP/1.0 200 OK\r\n"); - let mut data = vec![0; 1024]; - let res = stream.read(&mut data).unwrap(); - assert_eq!(res, 0); + let mut data = vec![0; 1024]; + let res = stream.read(&mut data).unwrap(); + assert_eq!(res, 0); + }) } #[test] fn test_http10_keepalive() { - let srv = TestServer::new(|| { - HttpService::build().h1(|_| future::ok::<_, ()>(Response::Ok().finish())) - }); + block_on(async { + let srv = TestServer::start(|| { + HttpService::build().h1(|_| future::ok::<_, ()>(Response::Ok().finish())) + }); - let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); - let _ = stream - .write_all(b"GET /test/tests/test HTTP/1.0\r\nconnection: keep-alive\r\n\r\n"); - let mut data = vec![0; 1024]; - let _ = stream.read(&mut data); - assert_eq!(&data[..17], b"HTTP/1.0 200 OK\r\n"); + let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); + let _ = stream.write_all( + b"GET /test/tests/test HTTP/1.0\r\nconnection: keep-alive\r\n\r\n", + ); + let mut data = vec![0; 1024]; + let _ = stream.read(&mut data); + assert_eq!(&data[..17], b"HTTP/1.0 200 OK\r\n"); - let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); - let _ = stream.write_all(b"GET /test/tests/test HTTP/1.0\r\n\r\n"); - let mut data = vec![0; 1024]; - let _ = stream.read(&mut data); - assert_eq!(&data[..17], b"HTTP/1.0 200 OK\r\n"); + let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); + let _ = stream.write_all(b"GET /test/tests/test HTTP/1.0\r\n\r\n"); + let mut data = vec![0; 1024]; + let _ = stream.read(&mut data); + assert_eq!(&data[..17], b"HTTP/1.0 200 OK\r\n"); - let mut data = vec![0; 1024]; - let res = stream.read(&mut data).unwrap(); - assert_eq!(res, 0); + let mut data = vec![0; 1024]; + let res = stream.read(&mut data).unwrap(); + assert_eq!(res, 0); + }) } #[test] fn test_http1_keepalive_disabled() { - let srv = TestServer::new(|| { - HttpService::build() - .keep_alive(KeepAlive::Disabled) - .h1(|_| future::ok::<_, ()>(Response::Ok().finish())) - }); + block_on(async { + let srv = TestServer::start(|| { + HttpService::build() + .keep_alive(KeepAlive::Disabled) + .h1(|_| future::ok::<_, ()>(Response::Ok().finish())) + }); - let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); - let _ = stream.write_all(b"GET /test/tests/test HTTP/1.1\r\n\r\n"); - let mut data = vec![0; 1024]; - let _ = stream.read(&mut data); - assert_eq!(&data[..17], b"HTTP/1.1 200 OK\r\n"); + let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); + let _ = stream.write_all(b"GET /test/tests/test HTTP/1.1\r\n\r\n"); + let mut data = vec![0; 1024]; + let _ = stream.read(&mut data); + assert_eq!(&data[..17], b"HTTP/1.1 200 OK\r\n"); - let mut data = vec![0; 1024]; - let res = stream.read(&mut data).unwrap(); - assert_eq!(res, 0); + let mut data = vec![0; 1024]; + let res = stream.read(&mut data).unwrap(); + assert_eq!(res, 0); + }) } #[test] fn test_content_length() { - use actix_http::http::{ - header::{HeaderName, HeaderValue}, - StatusCode, - }; + block_on(async { + use actix_http::http::{ + header::{HeaderName, HeaderValue}, + StatusCode, + }; - let srv = TestServer::new(|| { - HttpService::build().h1(|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])) - }) - }); + let srv = TestServer::start(|| { + HttpService::build().h1(|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])) + }) + }); - let header = HeaderName::from_static("content-length"); - let value = HeaderValue::from_static("0"); + 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.url(&format!("/{}", i))); - let response = block_fn(move || req.send()).unwrap(); - assert_eq!(response.headers().get(&header), None); + { + for i in 0..4 { + let req = srv.request(http::Method::GET, srv.url(&format!("/{}", i))); + let response = req.send().await.unwrap(); + assert_eq!(response.headers().get(&header), None); - let req = srv.request(http::Method::HEAD, srv.url(&format!("/{}", i))); - let response = block_fn(move || req.send()).unwrap(); - assert_eq!(response.headers().get(&header), None); + let req = srv.request(http::Method::HEAD, srv.url(&format!("/{}", i))); + let response = req.send().await.unwrap(); + assert_eq!(response.headers().get(&header), None); + } + + for i in 4..6 { + let req = srv.request(http::Method::GET, srv.url(&format!("/{}", i))); + let response = req.send().await.unwrap(); + assert_eq!(response.headers().get(&header), Some(&value)); + } } - - for i in 4..6 { - let req = srv.request(http::Method::GET, srv.url(&format!("/{}", i))); - let response = block_fn(move || req.send()).unwrap(); - assert_eq!(response.headers().get(&header), Some(&value)); - } - } + }) } #[test] fn test_h1_headers() { - let data = STR.repeat(10); - let data2 = data.clone(); + block_on(async { + let data = STR.repeat(10); + let data2 = data.clone(); - let mut srv = TestServer::new(move || { - let data = data.clone(); - HttpService::build().h1(move |_| { + let mut srv = TestServer::start(move || { + let data = data.clone(); + HttpService::build().h1(move |_| { let mut builder = Response::Ok(); for idx in 0..90 { builder.header( @@ -378,14 +412,15 @@ fn test_h1_headers() { } future::ok::<_, ()>(builder.body(data.clone())) }) - }); + }); - let response = block_fn(|| srv.get("/").send()).unwrap(); - assert!(response.status().is_success()); + let response = srv.get("/").send().await.unwrap(); + assert!(response.status().is_success()); - // read response - let bytes = srv.load_body(response).unwrap(); - assert_eq!(bytes, Bytes::from(data2)); + // read response + let bytes = srv.load_body(response).await.unwrap(); + assert_eq!(bytes, Bytes::from(data2)); + }) } const STR: &str = "Hello World Hello World Hello World Hello World Hello World \ @@ -412,208 +447,228 @@ const STR: &str = "Hello World Hello World Hello World Hello World Hello World \ #[test] fn test_h1_body() { - let mut srv = TestServer::new(|| { - HttpService::build().h1(|_| ok::<_, ()>(Response::Ok().body(STR))) - }); + block_on(async { + let mut srv = TestServer::start(|| { + HttpService::build().h1(|_| ok::<_, ()>(Response::Ok().body(STR))) + }); - let response = block_fn(|| srv.get("/").send()).unwrap(); - assert!(response.status().is_success()); + let response = srv.get("/").send().await.unwrap(); + assert!(response.status().is_success()); - // read response - let bytes = srv.load_body(response).unwrap(); - assert_eq!(bytes, Bytes::from_static(STR.as_ref())); + // read response + let bytes = srv.load_body(response).await.unwrap(); + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); + }) } #[test] fn test_h1_head_empty() { - let mut srv = TestServer::new(|| { - HttpService::build().h1(|_| ok::<_, ()>(Response::Ok().body(STR))) - }); + block_on(async { + let mut srv = TestServer::start(|| { + HttpService::build().h1(|_| ok::<_, ()>(Response::Ok().body(STR))) + }); - let response = block_fn(|| srv.head("/").send()).unwrap(); - assert!(response.status().is_success()); + let response = srv.head("/").send().await.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()); - } + { + 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()); + // read response + let bytes = srv.load_body(response).await.unwrap(); + assert!(bytes.is_empty()); + }) } #[test] fn test_h1_head_binary() { - let mut srv = TestServer::new(|| { - HttpService::build().h1(|_| { - ok::<_, ()>(Response::Ok().content_length(STR.len() as u64).body(STR)) - }) - }); + block_on(async { + let mut srv = TestServer::start(|| { + HttpService::build().h1(|_| { + ok::<_, ()>(Response::Ok().content_length(STR.len() as u64).body(STR)) + }) + }); - let response = block_fn(|| srv.head("/").send()).unwrap(); - assert!(response.status().is_success()); + let response = srv.head("/").send().await.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()); - } + { + 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()); + // read response + let bytes = srv.load_body(response).await.unwrap(); + assert!(bytes.is_empty()); + }) } #[test] fn test_h1_head_binary2() { - let srv = TestServer::new(|| { - HttpService::build().h1(|_| ok::<_, ()>(Response::Ok().body(STR))) - }); + block_on(async { + let srv = TestServer::start(|| { + HttpService::build().h1(|_| ok::<_, ()>(Response::Ok().body(STR))) + }); - let response = block_fn(|| srv.head("/").send()).unwrap(); - assert!(response.status().is_success()); + let response = srv.head("/").send().await.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()); - } + { + 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(|| { - HttpService::build().h1(|_| { - let body = once(ok(Bytes::from_static(STR.as_ref()))); - ok::<_, ()>( - Response::Ok().body(body::SizedStream::new(STR.len() as u64, body)), - ) - }) - }); + block_on(async { + let mut srv = TestServer::start(|| { + HttpService::build().h1(|_| { + let body = once(ok(Bytes::from_static(STR.as_ref()))); + ok::<_, ()>( + Response::Ok().body(body::SizedStream::new(STR.len() as u64, body)), + ) + }) + }); - let response = block_fn(|| srv.get("/").send()).unwrap(); - assert!(response.status().is_success()); + let response = srv.get("/").send().await.unwrap(); + assert!(response.status().is_success()); - // read response - let bytes = srv.load_body(response).unwrap(); - assert_eq!(bytes, Bytes::from_static(STR.as_ref())); + // read response + let bytes = srv.load_body(response).await.unwrap(); + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); + }) } #[test] fn test_h1_body_chunked_explicit() { - let mut srv = TestServer::new(|| { - HttpService::build().h1(|_| { - let body = once(ok::<_, Error>(Bytes::from_static(STR.as_ref()))); - ok::<_, ()>( - Response::Ok() - .header(header::TRANSFER_ENCODING, "chunked") - .streaming(body), - ) - }) - }); + block_on(async { + let mut srv = TestServer::start(|| { + HttpService::build().h1(|_| { + let body = once(ok::<_, Error>(Bytes::from_static(STR.as_ref()))); + ok::<_, ()>( + Response::Ok() + .header(header::TRANSFER_ENCODING, "chunked") + .streaming(body), + ) + }) + }); - let response = block_fn(|| srv.get("/").send()).unwrap(); - assert!(response.status().is_success()); - assert_eq!( - response - .headers() - .get(header::TRANSFER_ENCODING) - .unwrap() - .to_str() - .unwrap(), - "chunked" - ); + let response = srv.get("/").send().await.unwrap(); + assert!(response.status().is_success()); + assert_eq!( + response + .headers() + .get(header::TRANSFER_ENCODING) + .unwrap() + .to_str() + .unwrap(), + "chunked" + ); - // read response - let bytes = srv.load_body(response).unwrap(); + // read response + let bytes = srv.load_body(response).await.unwrap(); - // decode - assert_eq!(bytes, Bytes::from_static(STR.as_ref())); + // decode + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); + }) } #[test] fn test_h1_body_chunked_implicit() { - let mut srv = TestServer::new(|| { - HttpService::build().h1(|_| { - let body = once(ok::<_, Error>(Bytes::from_static(STR.as_ref()))); - ok::<_, ()>(Response::Ok().streaming(body)) - }) - }); + block_on(async { + let mut srv = TestServer::start(|| { + HttpService::build().h1(|_| { + let body = once(ok::<_, Error>(Bytes::from_static(STR.as_ref()))); + ok::<_, ()>(Response::Ok().streaming(body)) + }) + }); - let response = block_fn(|| srv.get("/").send()).unwrap(); - assert!(response.status().is_success()); - assert_eq!( - response - .headers() - .get(header::TRANSFER_ENCODING) - .unwrap() - .to_str() - .unwrap(), - "chunked" - ); + let response = srv.get("/").send().await.unwrap(); + assert!(response.status().is_success()); + assert_eq!( + response + .headers() + .get(header::TRANSFER_ENCODING) + .unwrap() + .to_str() + .unwrap(), + "chunked" + ); - // read response - let bytes = srv.load_body(response).unwrap(); - assert_eq!(bytes, Bytes::from_static(STR.as_ref())); + // read response + let bytes = srv.load_body(response).await.unwrap(); + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); + }) } #[test] fn test_h1_response_http_error_handling() { - let mut srv = TestServer::new(|| { - HttpService::build().h1(factory_fn_cfg(|_: &ServerConfig| { - ok::<_, ()>(pipeline(|_| { - let broken_header = Bytes::from_static(b"\0\0\0"); - ok::<_, ()>( - Response::Ok() - .header(http::header::CONTENT_TYPE, broken_header) - .body(STR), - ) + block_on(async { + let mut srv = TestServer::start(|| { + HttpService::build().h1(factory_fn_cfg(|_: &ServerConfig| { + ok::<_, ()>(pipeline(|_| { + let broken_header = Bytes::from_static(b"\0\0\0"); + ok::<_, ()>( + Response::Ok() + .header(http::header::CONTENT_TYPE, broken_header) + .body(STR), + ) + })) })) - })) - }); + }); - let response = block_fn(|| srv.get("/").send()).unwrap(); - assert_eq!(response.status(), http::StatusCode::INTERNAL_SERVER_ERROR); + let response = srv.get("/").send().await.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")); + // read response + let bytes = srv.load_body(response).await.unwrap(); + assert_eq!(bytes, Bytes::from_static(b"failed to parse header value")); + }) } #[test] fn test_h1_service_error() { - let mut srv = TestServer::new(|| { - HttpService::build() - .h1(|_| future::err::(error::ErrorBadRequest("error"))) - }); + block_on(async { + let mut srv = TestServer::start(|| { + HttpService::build() + .h1(|_| future::err::(error::ErrorBadRequest("error"))) + }); - let response = block_fn(|| srv.get("/").send()).unwrap(); - assert_eq!(response.status(), http::StatusCode::BAD_REQUEST); + let response = srv.get("/").send().await.unwrap(); + assert_eq!(response.status(), http::StatusCode::BAD_REQUEST); - // read response - let bytes = srv.load_body(response).unwrap(); - assert_eq!(bytes, Bytes::from_static(b"error")); + // read response + let bytes = srv.load_body(response).await.unwrap(); + assert_eq!(bytes, Bytes::from_static(b"error")); + }) } #[test] fn test_h1_on_connect() { - let srv = TestServer::new(|| { - HttpService::build() - .on_connect(|_| 10usize) - .h1(|req: Request| { - assert!(req.extensions().contains::()); - future::ok::<_, ()>(Response::Ok().finish()) - }) - }); + block_on(async { + let srv = TestServer::start(|| { + HttpService::build() + .on_connect(|_| 10usize) + .h1(|req: Request| { + assert!(req.extensions().contains::()); + future::ok::<_, ()>(Response::Ok().finish()) + }) + }); - let response = block_fn(|| srv.get("/").send()).unwrap(); - assert!(response.status().is_success()); + let response = srv.get("/").send().await.unwrap(); + assert!(response.status().is_success()); + }) } diff --git a/actix-http/tests/test_ssl_server.rs b/actix-http/tests/test_ssl_server.rs deleted file mode 100644 index 897d92b3..00000000 --- a/actix-http/tests/test_ssl_server.rs +++ /dev/null @@ -1,480 +0,0 @@ -#![cfg(feature = "ssl")] -use actix_codec::{AsyncRead, AsyncWrite}; -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; - -use actix_http::error::{ErrorBadRequest, PayloadError}; -use actix_http::http::header::{self, HeaderName, HeaderValue}; -use actix_http::http::{Method, StatusCode, Version}; -use actix_http::httpmessage::HttpMessage; -use actix_http::{body, Error, HttpService, Request, Response}; - -fn load_body(stream: S) -> impl Future -where - S: Stream, -{ - stream.fold(BytesMut::new(), move |mut body, chunk| { - body.extend_from_slice(&chunk); - Ok::<_, PayloadError>(body) - }) -} - -fn ssl_acceptor() -> Result> { - // 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::(ErrorBadRequest("error"))) - .map_err(|_| ()), - ) - }); - - let response = srv.block_on(srv.sget("/").send()).unwrap(); - assert_eq!(response.status(), StatusCode::BAD_REQUEST); - - // read response - let bytes = srv.load_body(response).unwrap(); - assert_eq!(bytes, Bytes::from_static(b"error")); -} - -#[test] -fn test_h2_on_connect() { - let openssl = ssl_acceptor().unwrap(); - - let mut srv = TestServer::new(move || { - openssl - .clone() - .map_err(|e| println!("Openssl error: {}", e)) - .and_then( - HttpService::build() - .on_connect(|_| 10usize) - .h2(|req: Request| { - assert!(req.extensions().contains::()); - ok::<_, ()>(Response::Ok().finish()) - }) - .map_err(|_| ()), - ) - }); - - let response = srv.block_on(srv.sget("/").send()).unwrap(); - assert!(response.status().is_success()); -} diff --git a/actix-http/tests/test_ws.rs b/actix-http/tests/test_ws.rs index 65a4d094..74c1cb40 100644 --- a/actix-http/tests/test_ws.rs +++ b/actix-http/tests/test_ws.rs @@ -1,26 +1,27 @@ use actix_codec::{AsyncRead, AsyncWrite, Framed}; use actix_http::{body, h1, ws, Error, HttpService, Request, Response}; -use actix_http_test::TestServer; +use actix_http_test::{block_on, TestServer}; use actix_utils::framed::FramedTransport; use bytes::{Bytes, BytesMut}; -use futures::future::{self, ok}; -use futures::{Future, Sink, Stream}; +use futures::future; +use futures::{SinkExt, StreamExt}; -fn ws_service( - (req, framed): (Request, Framed), -) -> impl Future { +async fn ws_service( + (req, mut framed): (Request, Framed), +) -> Result<(), Error> { let res = ws::handshake(req.head()).unwrap().message_body(()); framed .send((res, body::BodySize::None).into()) + .await + .unwrap(); + + FramedTransport::new(framed.into_framed(ws::Codec::new()), service) + .await .map_err(|_| panic!()) - .and_then(|framed| { - FramedTransport::new(framed.into_framed(ws::Codec::new()), service) - .map_err(|_| panic!()) - }) } -fn service(msg: ws::Frame) -> impl Future { +async fn service(msg: ws::Frame) -> Result { let msg = match msg { ws::Frame::Ping(msg) => ws::Message::Pong(msg), ws::Frame::Text(text) => { @@ -30,47 +31,56 @@ fn service(msg: ws::Frame) -> impl Future { ws::Frame::Close(reason) => ws::Message::Close(reason), _ => panic!(), }; - ok(msg) + Ok(msg) } #[test] fn test_simple() { - let mut srv = TestServer::new(|| { - HttpService::build() - .upgrade(ws_service) - .finish(|_| future::ok::<_, ()>(Response::NotFound())) - }); + block_on(async { + let mut srv = TestServer::start(|| { + HttpService::build() + .upgrade(actix_service::service_fn(ws_service)) + .finish(|_| future::ok::<_, ()>(Response::NotFound())) + }); - // client service - let framed = srv.ws().unwrap(); - let framed = srv - .block_on(framed.send(ws::Message::Text("text".to_string()))) - .unwrap(); - let (item, framed) = srv.block_on(framed.into_future()).map_err(|_| ()).unwrap(); - assert_eq!(item, Some(ws::Frame::Text(Some(BytesMut::from("text"))))); + // client service + let mut framed = srv.ws().await.unwrap(); + framed + .send(ws::Message::Text("text".to_string())) + .await + .unwrap(); + let (item, mut framed) = framed.into_future().await; + assert_eq!( + item.unwrap().unwrap(), + ws::Frame::Text(Some(BytesMut::from("text"))) + ); - let framed = srv - .block_on(framed.send(ws::Message::Binary("text".into()))) - .unwrap(); - let (item, framed) = srv.block_on(framed.into_future()).map_err(|_| ()).unwrap(); - assert_eq!( - item, - Some(ws::Frame::Binary(Some(Bytes::from_static(b"text").into()))) - ); + framed + .send(ws::Message::Binary("text".into())) + .await + .unwrap(); + let (item, mut framed) = framed.into_future().await; + assert_eq!( + item.unwrap().unwrap(), + ws::Frame::Binary(Some(Bytes::from_static(b"text").into())) + ); - let framed = srv - .block_on(framed.send(ws::Message::Ping("text".into()))) - .unwrap(); - let (item, framed) = srv.block_on(framed.into_future()).map_err(|_| ()).unwrap(); - assert_eq!(item, Some(ws::Frame::Pong("text".to_string().into()))); + framed.send(ws::Message::Ping("text".into())).await.unwrap(); + let (item, mut framed) = framed.into_future().await; + assert_eq!( + item.unwrap().unwrap(), + ws::Frame::Pong("text".to_string().into()) + ); - let framed = srv - .block_on(framed.send(ws::Message::Close(Some(ws::CloseCode::Normal.into())))) - .unwrap(); + framed + .send(ws::Message::Close(Some(ws::CloseCode::Normal.into()))) + .await + .unwrap(); - let (item, _framed) = srv.block_on(framed.into_future()).map_err(|_| ()).unwrap(); - assert_eq!( - item, - Some(ws::Frame::Close(Some(ws::CloseCode::Normal.into()))) - ); + let (item, _framed) = framed.into_future().await; + assert_eq!( + item.unwrap().unwrap(), + ws::Frame::Close(Some(ws::CloseCode::Normal.into())) + ); + }) } diff --git a/test-server/src/lib.rs b/test-server/src/lib.rs index 4ba123aa..bf6558b5 100644 --- a/test-server/src/lib.rs +++ b/test-server/src/lib.rs @@ -7,7 +7,7 @@ use actix_rt::{System}; use actix_server::{Server, ServiceFactory}; use awc::{error::PayloadError, ws, Client, ClientRequest, ClientResponse, Connector}; use bytes::Bytes; -use futures::{Stream, future::lazy}; +use futures::Stream; use http::Method; use net2::TcpBuilder; use tokio_net::tcp::TcpStream; @@ -55,7 +55,7 @@ pub struct TestServerRuntime { impl TestServer { #[allow(clippy::new_ret_no_self)] /// Start new test server with application factory - pub fn new>(factory: F) -> TestServerRuntime { + pub fn start>(factory: F) -> TestServerRuntime { let (tx, rx) = mpsc::channel(); // run server in separate thread @@ -76,11 +76,11 @@ impl TestServer { let (system, addr) = rx.recv().unwrap(); - let client = block_on(lazy(move |_| { + let client = { let connector = { - #[cfg(feature = "ssl")] + #[cfg(feature = "openssl")] { - use openssl::ssl::{SslConnector, SslMethod, SslVerifyMode}; + use open_ssl::ssl::{SslConnector, SslMethod, SslVerifyMode}; let mut builder = SslConnector::builder(SslMethod::tls()).unwrap(); builder.set_verify(SslVerifyMode::NONE); @@ -93,7 +93,7 @@ impl TestServer { .ssl(builder.build()) .finish() } - #[cfg(not(feature = "ssl"))] + #[cfg(not(feature = "openssl"))] { Connector::new() .conn_lifetime(time::Duration::from_secs(0)) @@ -102,14 +102,9 @@ impl TestServer { } }; - Ok::(Client::build().connector(connector).finish()) - })) - .unwrap(); - - block_on(lazy(|_| { - Ok::<_, ()>(actix_connect::start_default_resolver()) - })) - .unwrap(); + Client::build().connector(connector).finish() + }; + actix_connect::start_default_resolver(); TestServerRuntime { addr, @@ -228,35 +223,33 @@ impl TestServerRuntime { self.client.request(method, path.as_ref()) } - pub fn load_body( + pub async fn load_body( &mut self, mut response: ClientResponse, ) -> Result where S: Stream> + Unpin + 'static, { - block_on(response.body().limit(10_485_760)) + response.body().limit(10_485_760).await } /// Connect to websocket server at a given path - pub fn ws_at( + pub async fn ws_at( &mut self, path: &str, ) -> Result, awc::error::WsClientError> { let url = self.url(path); let connect = self.client.ws(url).connect(); - block_on(async move { - connect.await.map(|(_, framed)| framed) - }) + connect.await.map(|(_, framed)| framed) } /// Connect to a websocket server - pub fn ws( + pub async fn ws( &mut self, ) -> Result, awc::error::WsClientError> { - self.ws_at("/") + self.ws_at("/").await } /// Stop http server diff --git a/tests/cert.pem b/tests/cert.pem index f9bb0508..0eeb6721 100644 --- a/tests/cert.pem +++ b/tests/cert.pem @@ -1,32 +1,19 @@ -----BEGIN CERTIFICATE----- -MIIFfjCCA2agAwIBAgIJAOIBvp/w68KrMA0GCSqGSIb3DQEBCwUAMGsxCzAJBgNV -BAYTAlJVMRkwFwYDVQQIDBBTYWludC1QZXRlcnNidXJnMRkwFwYDVQQHDBBTYWlu -dC1QZXRlcnNidXJnMRIwEAYDVQQKDAlLdXBpYmlsZXQxEjAQBgNVBAMMCWxvY2Fs -aG9zdDAgFw0xOTA3MjcxODIzMTJaGA8zMDE5MDcyNzE4MjMxMlowazELMAkGA1UE -BhMCUlUxGTAXBgNVBAgMEFNhaW50LVBldGVyc2J1cmcxGTAXBgNVBAcMEFNhaW50 -LVBldGVyc2J1cmcxEjAQBgNVBAoMCUt1cGliaWxldDESMBAGA1UEAwwJbG9jYWxo -b3N0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuiQZzTO3gRRPr6ZH -wcmKqkoXig9taCCqx72Qvb9tvCLhQLE1dDPZV8I/r8bx+mM4Yz3r0Hm5LxTIhCM9 -p3/abuiJAZENC/VkxgFzBGg7KGLSFmzU+A8Ft+2mrKmj5MpIPBCxDeVg80TCQOJy -hj+NU3PpBo9nxTgxWNWO6X+ZovZohdp78fYLLtns8rxjug3FVzdPrrLnBvihkGlq -gfImkh+vZxMTj1OgtxyCOhdbO4Ol4jCbn7a5yIw+iixHOEgBQfTQopRP7z1PEUV2 -WIy2VEGzvQDlj2OyzH86T1IOFV5rz5MjdZuW0qNzeS0w3Jzgp/olSbIZLhGAaIk0 -gN7y9XvSHqs7rO0wW+467ico7+uP1ScGgPgJA5fGu7ahp7F7G3ZSoAqAcS60wYsX -kStoA3RWAuqste6aChv1tlgTt+Rhk8qjGhuh0ng2qVnTGyo2T3OCHB/c47Bcsp6L -xiyTCnQIPH3fh2iO/SC7gPw3jihPMCAQZYlkC3MhMk974rn2zs9cKtJ8ubnG2m5F -VFVYmduRqS/YQS/O802jVCFdc8KDmoeAYNuHzgRZjQv9018UUeW3jtWKnopJnWs5 -ae9pbtmYeOtc7OovOxT7J2AaVfUkHRhmlqWZMzEJTcZws0fRPTZDifFJ5LFWbZsC -zW4tCKBKvYM9eAnbb+abiHXlY1MCAwEAAaMjMCEwHwYDVR0RBBgwFoIJbG9jYWxo -b3N0ggkxMjcuMC4wLjEwDQYJKoZIhvcNAQELBQADggIBAC1EU4SVCfUKc7JbhYRf -P87F+8e13bBTLxevJdnTCH3Xw2AN8UPmwQ2vv9Mv2FMulMBQ7yLnQLGtgGUua2OE -XO+EdBBEKnglo9cwXGzU6qHhaiCeXZDM8s53qOOrD42XsDsY0nOoFYqDLW3WixP9 -f1fWbcEf6+ktlvqi/1/3R6QtQR+6LS43enbsYHq8aAP60NrpXxdXxEoUwW6Z/sje -XAQluH8jzledwJcY8bXRskAHZlE4kGlOVuGgnyI3BXyLiwB4g9smFzYIs98iAGmV -7ZBaR5IIiRCtoKBG+SngM7Log0bHphvFPjDDvgqWYiWaOHboYM60Y2Z/gRbcjuMU -WZX64jw29fa8UPFdtGTupt+iuO7iXnHnm0lBBK36rVdOvsZup76p6L4BXmFsRmFK -qJ2Zd8uWNPDq80Am0mYaAqENuIANHHJXX38SesC+QO+G2JZt6vCwkGk/Qune4GIg -1GwhvsDRfTQopSxg1rdPwPM7HWeTfUGHZ34B5p/iILA3o6PfYQU8fNAWIsCDkRX2 -MrgDgCnLZxKb6pjR4DYNAdPwkxyMFACZ2T46z6WvLWFlnkK5nbZoqsOsp+GJHole -llafhrelXEzt3zFR0q4zGcqheJDI+Wy+fBy3XawgAc4eN0T2UCzL/jKxKgzlzSU3 -+xh1SDNjFLRd6sGzZHPMgXN0 +MIIDEDCCAfgCCQCQdmIZc/Ib/jANBgkqhkiG9w0BAQsFADBKMQswCQYDVQQGEwJ1 +czELMAkGA1UECAwCY2ExCzAJBgNVBAcMAnNmMSEwHwYJKoZIhvcNAQkBFhJmYWZo +cmQ5MUBnbWFpbC5jb20wHhcNMTkxMTE5MTEwNjU1WhcNMjkxMTE2MTEwNjU1WjBK +MQswCQYDVQQGEwJ1czELMAkGA1UECAwCY2ExCzAJBgNVBAcMAnNmMSEwHwYJKoZI +hvcNAQkBFhJmYWZocmQ5MUBnbWFpbC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQDcnaz12CKzUL7248V7Axhms/O9UQXfAdw0yolEfC3P5jADa/1C ++kLWKjAc2coqDSbGsrsR6KiH2g06Kunx+tSGqUO+Sct7HEehmxndiSwx/hfMWezy +XRe/olcHFTeCk/Tllz4xGEplhPua6GLhJygLOhAMiV8cwCYrgyPqsDduExLDFCqc +K2xntIPreumXpiE3QY4+MWyteiJko4IWDFf/UwwsdCY5MlFfw1F/Uv9vz7FfOfvu +GccHd/ex8cOwotUqd6emZb+0bVE24Sv8U+yLnHIVx/tOkxgMAnJEpAnf2G3Wp3zU +b2GJosbmfGaf+xTfnGGhTLLL7kCtva+NvZr5AgMBAAEwDQYJKoZIhvcNAQELBQAD +ggEBANftoL8zDGrjCwWvct8kOOqset2ukK8vjIGwfm88CKsy0IfSochNz2qeIu9R +ZuO7c0pfjmRkir9ZQdq9vXgG3ccL9UstFsferPH9W3YJ83kgXg3fa0EmCiN/0hwz +6Ij1ZBiN1j3+d6+PJPgyYFNu2nGwox5mJ9+aRAGe0/9c63PEOY8P2TI4HsiPmYSl +fFR8k/03vr6e+rTKW85BgctjvYKe/TnFxeCQ7dZ+na7vlEtch4tNmy6O/vEk2kCt +5jW0DUxhmRsv2wGmfFRI0+LotHjoXQQZi6nN5aGL3odaGF3gYwIVlZNd3AdkwDQz +BzG0ZwXuDDV9bSs3MfWEWcy4xuU= -----END CERTIFICATE----- diff --git a/tests/key.pem b/tests/key.pem index 70153c8a..a6d30816 100644 --- a/tests/key.pem +++ b/tests/key.pem @@ -1,52 +1,28 @@ -----BEGIN PRIVATE KEY----- -MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQC6JBnNM7eBFE+v -pkfByYqqSheKD21oIKrHvZC9v228IuFAsTV0M9lXwj+vxvH6YzhjPevQebkvFMiE -Iz2nf9pu6IkBkQ0L9WTGAXMEaDsoYtIWbNT4DwW37aasqaPkykg8ELEN5WDzRMJA -4nKGP41Tc+kGj2fFODFY1Y7pf5mi9miF2nvx9gsu2ezyvGO6DcVXN0+usucG+KGQ -aWqB8iaSH69nExOPU6C3HII6F1s7g6XiMJuftrnIjD6KLEc4SAFB9NCilE/vPU8R -RXZYjLZUQbO9AOWPY7LMfzpPUg4VXmvPkyN1m5bSo3N5LTDcnOCn+iVJshkuEYBo -iTSA3vL1e9Ieqzus7TBb7jruJyjv64/VJwaA+AkDl8a7tqGnsXsbdlKgCoBxLrTB -ixeRK2gDdFYC6qy17poKG/W2WBO35GGTyqMaG6HSeDapWdMbKjZPc4IcH9zjsFyy -novGLJMKdAg8fd+HaI79ILuA/DeOKE8wIBBliWQLcyEyT3viufbOz1wq0ny5ucba -bkVUVViZ25GpL9hBL87zTaNUIV1zwoOah4Bg24fOBFmNC/3TXxRR5beO1Yqeikmd -azlp72lu2Zh461zs6i87FPsnYBpV9SQdGGaWpZkzMQlNxnCzR9E9NkOJ8UnksVZt -mwLNbi0IoEq9gz14Cdtv5puIdeVjUwIDAQABAoICAQCZVVezw+BsAjFKPi1qIv2J -HZOadO7pEc/czflHdUON8SWgxtmDqZpmQmt3/ugiHE284qs4hqzXbcVnpCgLrLRh -HEiP887NhQ3IVjVK8hmZQR5SvsAIv0c0ph3gqbWKqF8sq4tOKR/eBUwHawJwODXR -AvB4KPWQbqOny/P3wNbseRLNAJeNT+MSaw5XPnzgLKvdFoEbJeBNy847Sbsk5DaF -tHgm7n30WS1Q6bkU5VyP//hMBUKNJFaSL4TtCWB5qkbu8B5VbtsR9m0FizTb6L3h -VmYbUXvIzJXjAwMjiDJ1w9wHl+tj3BE33tEmhuVzNf+SH+tLc9xuKJighDWt2vpD -eTpZ1qest26ANLOmNXWVCVTGpcWvOu5yhG/P7En10EzjFruMfHAFdwLm1gMx1rlR -9fyNAk/0ROJ+5BUtuWgDiyytS5f2T9KGiOHni7UbBIkv0CV2H6VL39Twxf+3OHnx -JJ7OWZ8DRuLM/EJfN3C1+3eDsXOvcdvbo2TFBmCCl4Pa2pm4k3g2NBfxy/zSYWIh -ccGPZorFKNMUi29U0Ool6fxeVflbll570pWVBLAB31HdkLSESv9h+2j/IiEJcJXj -nzl2RtYB0Uxzk6SjO0z4WXjz/SXg5tQQkm/dx8kM8TvHICFq68AEnw8t9Hagsdxs -v5jNyOEeI1I5gPgZmKuboQKCAQEA7Hw6s8Xc3UtNaywMcK1Eb1O+kwRdztgtm0uE -uqsHWmGqbBxXN4jiVLh3dILIXFuVbvDSsSZtPLhOj1wqxgsTHg93b4BtvowyNBgo -X4tErMu7/6NRael/hfOEdtpfv2gV+0eQa+8KKqYJPbqpMz/r5L/3RaxS3iXkj3QM -6oC4+cRuwy/flPMIpxhDriH5yjfiMOdDsi3ZfMTJu/58DTrKV7WkJxQZmha4EoZn -IiXeRhzo+2ukMDWrr3GGPyDfjd/NB7rmY8QBdmhB5NSk+6B66JCNTIbKka/pichS -36bwSYFNji4NaHUUlYDUjfKoTNuQMEZknMGhc/433ADO7s17iQKCAQEAyYBYVG7/ -LE2IkvQy9Nwly5tRCNlSvSkloz7PUwRbzG5uF5mweWEa8YECJe9/vrFXvyBW+NR8 -XABFn4eG0POTR9nyb4n2nUlqiGugDIPgkrKCkJws5InifITZ/+Viocd4YZL5UwCU -R1/kMf0UjK2iJjWEeTPS6RmwRI2Iu7kym9BzphDyNYBQSbUE/f+4hNP6nUT/h09c -VH4/sUhubSgVKeK4onOci8bKitAkwVBYCYSyhuBCeCu8fTk2hVRWviRaJPVq2PMB -LHw1FCcfJLIPJG6MZpFAPkMQxpiewdggXIgi46ZlZcsNXEJ81ocT4GU2j+ArQXCf -lgEycyD3mx4k+wKCAQBGneohmKoVYtEheavVUcgnvkggOqOQirlDsE9YNo4hjRyI -4AWjTbrYNaVmI0+VVLvQvxULVUA1a4v5/zm+nbv9s/ykTSN4TQEI0VXtAfdl6gif -k7NR/ynXZBpgK2GAFKLLwFj+Agl1JtOHnV+9MA9O5Yv/QDAWqhYQSEU7GWkjHGc+ -3eLT5ablzrcXHooqunlOxSBP6qURPupGuv1sLewSOOllyfjDLJmW3o+ZgNlY8nUX -7tK+mqhD4ZCG9VgMU5I0BrmZfQQ6yXMz09PYV9mb7N5kxbNjwbXpMOqeYolKSdRQ -6quST7Pv2OKf6KAdI0txPvP4Y1HFA1rG1W71nGKRAoIBAHlDU+T8J3Rx9I77hu70 -zYoKnmnE35YW/R+Q3RQIu3X7vyVUyG9DkQNlr/VEfIw2Dahnve9hcLWtNDkdRnTZ -IPlMoCmfzVo6pHIU0uy1MKEX7Js6YYnnsPVevhLR6NmTQU73NDRPVOzfOGUc+RDw -LXTxIBgQqAy/+ORIiNDwUxSSDgcSi7DG14qD9c0l59WH/HpI276Cc/4lPA9kl4/5 -X0MlvheFm+BCcgG34Wa1A0Y3JXkl3NqU94oktDro1or3NYioaPTGyR4MYaUPJh7f -SV2TacsP/ql5ks7xahkeB9un0ddOfBcWa6PqH1a7U6rnPj63mVB4hpGvhrziSiB/ -s6ECggEAOp2P4Yd9Vm9/CptxS50HFF4adyLscAtsDd3S2hIAXhDovcPbvRek4pLQ -idPhHlRAfqrEztnhaVAmCK9HlhgthtiQGQX62YI4CS4QL2IhzDFo3M1a2snjFEdl -QuFk3XI7kQ0Yp8BLLG7T436JUrUkCXc4gQX2uRNut+ff34RIR2CjcQQjChxuHVeG -sP/3xFFj8OSs7ZoSPbmDBLrMOl64YHwezQUNAZiRYiaGbFiY0QUV6dHq8qX/qE1h -a/0Rq+gTqObDST0TqhMzI8V/i7R8SwVcD5ODHaZp5I2N2P/hV5OWY7ghQXhh89WM -o21xtGh0nP2Fq1TC6jFO+9cpbK8jNA== +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDcnaz12CKzUL72 +48V7Axhms/O9UQXfAdw0yolEfC3P5jADa/1C+kLWKjAc2coqDSbGsrsR6KiH2g06 +Kunx+tSGqUO+Sct7HEehmxndiSwx/hfMWezyXRe/olcHFTeCk/Tllz4xGEplhPua +6GLhJygLOhAMiV8cwCYrgyPqsDduExLDFCqcK2xntIPreumXpiE3QY4+MWyteiJk +o4IWDFf/UwwsdCY5MlFfw1F/Uv9vz7FfOfvuGccHd/ex8cOwotUqd6emZb+0bVE2 +4Sv8U+yLnHIVx/tOkxgMAnJEpAnf2G3Wp3zUb2GJosbmfGaf+xTfnGGhTLLL7kCt +va+NvZr5AgMBAAECggEBAKoU0UwzVgVCQgca8Jt2dnBvWYDhnxIfYAI/BvaKedMm +1ms87OKfB7oOiksjyI0E2JklH72dzZf2jm4CuZt5UjGC+xwPzlTaJ4s6hQVbBHyC +NRyxU1BCXtW5tThbrhD4OjxqjmLRJEIB9OunLtwAEQoeuFLB8Va7+HFhR+Zd9k3f +7aVA93pC5A50NRbZlke4miJ3Q8n7ZF0+UmxkBfm3fbqLk7aMWkoEKwLLTadjRlu1 +bBp0YDStX66I/p1kujqBOdh6VpPvxFOa1sV9pq0jeiGc9YfSkzRSKzIn8GoyviFB +fHeszQdNlcnrSDSNnMABAw+ZpxUO7SCaftjwejEmKZUCgYEA+TY43VpmV95eY7eo +WKwGepiHE0fwQLuKGELmZdZI80tFi73oZMuiB5WzwmkaKGcJmm7KGE9KEvHQCo9j +xvmktBR0VEZH8pmVfun+4h6+0H7m/NKMBBeOyv/IK8jBgHjkkB6e6nmeR7CqTxCw +tf9tbajl1QN8gNzXZSjBDT/lanMCgYEA4qANOKOSiEARtgwyXQeeSJcM2uPv6zF3 +ffM7vjSedtuEOHUSVeyBP/W8KDt7zyPppO/WNbURHS+HV0maS9yyj6zpVS2HGmbs +3fetswsQ+zYVdokW89x4oc2z4XOGHd1LcSlyhRwPt0u2g1E9L0irwTQLWU0npFmG +PRf7sN9+LeMCgYAGkDUDL2ROoB6gRa/7Vdx90hKMoXJkYgwLA4gJ2pDlR3A3c/Lw +5KQJyxmG3zm/IqeQF6be6QesZA30mT4peV2rGHbP2WH/s6fKReNelSy1VQJEWk8x +tGUgV4gwDwN5nLV4TjYlOrq+bJqvpmLhCC8bmj0jVQosYqSRl3cuICasnQKBgGlV +VO/Xb1su1EyWPK5qxRIeSxZOTYw2sMB01nbgxCqge0M2fvA6/hQ5ZlwY0cIEgits +YlcSMsMq/TAAANxz1vbaupUhlSMbZcsBvNV0Nk9c4vr2Wxm7hsJF9u66IEMvQUp2 +pkjiMxfR9CHzF4orr9EcHI5EQ0Grbq5kwFKEfoRbAoGAcWoFPILeJOlp2yW/Ds3E +g2fQdI9BAamtEZEaslJmZMmsDTg5ACPcDkOSFEQIaJ7wLPXeZy74FVk/NrY5F8Gz +bjX9OD/xzwp852yW5L9r62vYJakAlXef5jI6CFdYKDDCcarU0S7W5k6kq9n+wrBR +i1NklYmUAMr2q59uJA5zsic= -----END PRIVATE KEY-----