From 1ca6b44bae381f9042795fea63665aa9d480ccf2 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Sun, 18 Nov 2018 21:48:20 -0800 Subject: [PATCH] add TestServer --- src/lib.rs | 2 +- src/test.rs | 447 +++++++++++++++++++++---------------------- tests/test_client.rs | 113 ++++------- tests/test_server.rs | 354 ++++++++++++---------------------- tests/test_ws.rs | 128 ++++++------- 5 files changed, 425 insertions(+), 619 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index e5f50011b..5256dd190 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -171,4 +171,4 @@ pub mod http { } pub use header::ContentEncoding; pub use message::ConnectionType; -} \ No newline at end of file +} diff --git a/src/test.rs b/src/test.rs index 5442a4149..9e14129b6 100644 --- a/src/test.rs +++ b/src/test.rs @@ -1,251 +1,31 @@ //! Various helpers for Actix applications to use during testing. -use std::net; use std::str::FromStr; +use std::sync::mpsc; +use std::{net, thread}; use actix::System; +use actix_net::codec::Framed; +use actix_net::server::{Server, StreamServiceFactory}; +use actix_net::service::Service; use bytes::Bytes; use cookie::Cookie; -use futures::Future; +use futures::future::{lazy, Future}; use http::header::HeaderName; use http::{HeaderMap, HttpTryFrom, Method, Uri, Version}; use net2::TcpBuilder; use tokio::runtime::current_thread::Runtime; +use tokio_io::{AsyncRead, AsyncWrite}; +use body::MessageBody; +use client::{ + ClientRequest, ClientRequestBuilder, ClientResponse, Connect, Connection, Connector, + ConnectorError, SendRequestError, +}; use header::{Header, IntoHeaderValue}; use payload::Payload; use request::Request; -// use ws; - -/// The `TestServer` type. -/// -/// `TestServer` is very simple test server that simplify process of writing -/// integration tests cases for actix web applications. -/// -/// # Examples -/// -/// ```rust,ignore -/// # extern crate actix_web; -/// # use actix_web::*; -/// # -/// # fn my_handler(req: &HttpRequest) -> Response { -/// # Response::Ok().into() -/// # } -/// # -/// # fn main() { -/// use actix_web::test::TestServer; -/// -/// let mut srv = TestServer::new(|app| app.handler(my_handler)); -/// -/// let req = srv.get().finish().unwrap(); -/// let response = srv.execute(req.send()).unwrap(); -/// assert!(response.status().is_success()); -/// # } -/// ``` -pub struct TestServer { - addr: net::SocketAddr, - rt: Runtime, - ssl: bool, -} - -impl TestServer { - /// Start new test server - /// - /// This method accepts configuration method. You can add - /// middlewares or set handlers for test application. - pub fn new(_config: F) -> Self - where - F: Fn() + Clone + Send + 'static, - { - unimplemented!() - } - - /// Get firat available unused address - pub fn unused_addr() -> net::SocketAddr { - let addr: net::SocketAddr = "127.0.0.1:0".parse().unwrap(); - let socket = TcpBuilder::new_v4().unwrap(); - socket.bind(&addr).unwrap(); - socket.reuse_address(true).unwrap(); - let tcp = socket.to_tcp_listener().unwrap(); - tcp.local_addr().unwrap() - } - - /// Construct test server url - pub fn addr(&self) -> net::SocketAddr { - self.addr - } - - /// Construct test server url - pub fn url(&self, uri: &str) -> String { - if uri.starts_with('/') { - format!( - "{}://localhost:{}{}", - if self.ssl { "https" } else { "http" }, - self.addr.port(), - uri - ) - } else { - format!( - "{}://localhost:{}/{}", - if self.ssl { "https" } else { "http" }, - self.addr.port(), - uri - ) - } - } - - /// Stop http server - fn stop(&mut self) { - System::current().stop(); - } - - /// Execute future on current core - pub fn execute(&mut self, fut: F) -> Result - where - F: Future, - { - self.rt.block_on(fut) - } - - // /// Connect to websocket server at a given path - // pub fn ws_at( - // &mut self, path: &str, - // ) -> Result<(ws::ClientReader, ws::ClientWriter), ws::ClientError> { - // let url = self.url(path); - // self.rt - // .block_on(ws::Client::with_connector(url, self.conn.clone()).connect()) - // } - - // /// Connect to a websocket server - // pub fn ws( - // &mut self, - // ) -> Result<(ws::ClientReader, ws::ClientWriter), ws::ClientError> { - // self.ws_at("/") - // } - - // /// Create `GET` request - // pub fn get(&self) -> ClientRequestBuilder { - // ClientRequest::get(self.url("/").as_str()) - // } - - // /// Create `POST` request - // pub fn post(&self) -> ClientRequestBuilder { - // ClientRequest::post(self.url("/").as_str()) - // } - - // /// Create `HEAD` request - // pub fn head(&self) -> ClientRequestBuilder { - // ClientRequest::head(self.url("/").as_str()) - // } - - // /// Connect to test http server - // pub fn client(&self, meth: Method, path: &str) -> ClientRequestBuilder { - // ClientRequest::build() - // .method(meth) - // .uri(self.url(path).as_str()) - // .with_connector(self.conn.clone()) - // .take() - // } -} - -impl Drop for TestServer { - fn drop(&mut self) { - self.stop() - } -} - -// /// An `TestServer` builder -// /// -// /// This type can be used to construct an instance of `TestServer` through a -// /// builder-like pattern. -// pub struct TestServerBuilder -// where -// F: Fn() -> S + Send + Clone + 'static, -// { -// state: F, -// } - -// impl TestServerBuilder -// where -// F: Fn() -> S + Send + Clone + 'static, -// { -// /// Create a new test server -// pub fn new(state: F) -> TestServerBuilder { -// TestServerBuilder { state } -// } - -// #[allow(unused_mut)] -// /// Configure test application and run test server -// pub fn start(mut self, config: C) -> TestServer -// where -// C: Fn(&mut TestApp) + Clone + Send + 'static, -// { -// let (tx, rx) = mpsc::channel(); - -// let mut has_ssl = false; - -// #[cfg(any(feature = "alpn", feature = "ssl"))] -// { -// has_ssl = has_ssl || self.ssl.is_some(); -// } - -// #[cfg(feature = "rust-tls")] -// { -// has_ssl = has_ssl || self.rust_ssl.is_some(); -// } - -// // run server in separate thread -// thread::spawn(move || { -// let addr = TestServer::unused_addr(); - -// let sys = System::new("actix-test-server"); -// let state = self.state; -// let mut srv = HttpServer::new(move || { -// let mut app = TestApp::new(state()); -// config(&mut app); -// app -// }).workers(1) -// .keep_alive(5) -// .disable_signals(); - -// tx.send((System::current(), addr, TestServer::get_conn())) -// .unwrap(); - -// #[cfg(any(feature = "alpn", feature = "ssl"))] -// { -// let ssl = self.ssl.take(); -// if let Some(ssl) = ssl { -// let tcp = net::TcpListener::bind(addr).unwrap(); -// srv = srv.listen_ssl(tcp, ssl).unwrap(); -// } -// } -// #[cfg(feature = "rust-tls")] -// { -// let ssl = self.rust_ssl.take(); -// if let Some(ssl) = ssl { -// let tcp = net::TcpListener::bind(addr).unwrap(); -// srv = srv.listen_rustls(tcp, ssl); -// } -// } -// if !has_ssl { -// let tcp = net::TcpListener::bind(addr).unwrap(); -// srv = srv.listen(tcp); -// } -// srv.start(); - -// sys.run(); -// }); - -// let (system, addr, conn) = rx.recv().unwrap(); -// System::set_current(system); -// TestServer { -// addr, -// conn, -// ssl: has_ssl, -// rt: Runtime::new().unwrap(), -// } -// } -// } +use ws; /// Test `Request` builder /// @@ -486,3 +266,204 @@ impl TestRequest { // } // } } + +/// The `TestServer` type. +/// +/// `TestServer` is very simple test server that simplify process of writing +/// integration tests cases for actix web applications. +/// +/// # Examples +/// +/// ```rust +/// # extern crate actix_web; +/// # use actix_web::*; +/// # +/// # fn my_handler(req: &HttpRequest) -> HttpResponse { +/// # HttpResponse::Ok().into() +/// # } +/// # +/// # fn main() { +/// use actix_web::test::TestServer; +/// +/// let mut srv = TestServer::new(|app| app.handler(my_handler)); +/// +/// let req = srv.get().finish().unwrap(); +/// let response = srv.execute(req.send()).unwrap(); +/// assert!(response.status().is_success()); +/// # } +/// ``` +pub struct TestServer; + +/// +pub struct TestServerRuntime { + addr: net::SocketAddr, + conn: T, + rt: Runtime, +} + +impl TestServer { + /// Start new test server with application factory + pub fn with_factory( + factory: F, + ) -> TestServerRuntime< + impl Service + + Clone, + > { + let (tx, rx) = mpsc::channel(); + + // run server in separate thread + thread::spawn(move || { + let sys = System::new("actix-test-server"); + let tcp = net::TcpListener::bind("127.0.0.1:0").unwrap(); + let local_addr = tcp.local_addr().unwrap(); + + Server::default() + .listen("test", tcp, factory) + .workers(1) + .disable_signals() + .start(); + + tx.send((System::current(), local_addr)).unwrap(); + sys.run(); + }); + + let (system, addr) = rx.recv().unwrap(); + System::set_current(system); + + let mut rt = Runtime::new().unwrap(); + let conn = rt + .block_on(lazy(|| Ok::<_, ()>(TestServer::new_connector()))) + .unwrap(); + + TestServerRuntime { addr, conn, rt } + } + + fn new_connector( +) -> impl Service + + Clone { + #[cfg(feature = "ssl")] + { + use openssl::ssl::{SslConnector, SslMethod, SslVerifyMode}; + + let mut builder = SslConnector::builder(SslMethod::tls()).unwrap(); + builder.set_verify(SslVerifyMode::NONE); + Connector::default().ssl(builder.build()).service() + } + #[cfg(not(feature = "ssl"))] + { + Connector::default().service() + } + } + + /// Get firat available unused address + pub fn unused_addr() -> net::SocketAddr { + let addr: net::SocketAddr = "127.0.0.1:0".parse().unwrap(); + let socket = TcpBuilder::new_v4().unwrap(); + socket.bind(&addr).unwrap(); + socket.reuse_address(true).unwrap(); + let tcp = socket.to_tcp_listener().unwrap(); + tcp.local_addr().unwrap() + } +} + +impl TestServerRuntime { + /// Execute future on current core + pub fn block_on(&mut self, fut: F) -> Result + where + F: Future, + { + self.rt.block_on(fut) + } + + /// Construct test server url + pub fn addr(&self) -> net::SocketAddr { + self.addr + } + + /// Construct test server url + pub fn url(&self, uri: &str) -> String { + if uri.starts_with('/') { + format!("http://localhost:{}{}", self.addr.port(), uri) + } else { + format!("http://localhost:{}/{}", self.addr.port(), uri) + } + } + + /// Create `GET` request + pub fn get(&self) -> ClientRequestBuilder { + ClientRequest::get(self.url("/").as_str()) + } + + /// Create `POST` request + pub fn post(&self) -> ClientRequestBuilder { + ClientRequest::post(self.url("/").as_str()) + } + + /// Create `HEAD` request + pub fn head(&self) -> ClientRequestBuilder { + ClientRequest::head(self.url("/").as_str()) + } + + /// Connect to test http server + pub fn client(&self, meth: Method, path: &str) -> ClientRequestBuilder { + ClientRequest::build() + .method(meth) + .uri(self.url(path).as_str()) + .take() + } + + /// Http connector + pub fn connector(&mut self) -> &mut T { + &mut self.conn + } + + /// Http connector + pub fn new_connector(&mut self) -> T + where + T: Clone, + { + self.conn.clone() + } + + /// Stop http server + fn stop(&mut self) { + System::current().stop(); + } +} + +impl TestServerRuntime +where + T: Service + Clone, + T::Response: Connection, +{ + /// Connect to websocket server at a given path + pub fn ws_at( + &mut self, + path: &str, + ) -> Result, ws::ClientError> { + let url = self.url(path); + self.rt + .block_on(ws::Client::default().call(ws::Connect::new(url))) + } + + /// Connect to a websocket server + pub fn ws( + &mut self, + ) -> Result, ws::ClientError> { + self.ws_at("/") + } + + /// Send request and read response message + pub fn send_request( + &mut self, + req: ClientRequest, + ) -> Result { + self.rt.block_on(req.send(&mut self.conn)) + } +} + +impl Drop for TestServerRuntime { + fn drop(&mut self) { + self.stop() + } +} diff --git a/tests/test_client.rs b/tests/test_client.rs index 40920d1b8..4a4ccb7dd 100644 --- a/tests/test_client.rs +++ b/tests/test_client.rs @@ -4,16 +4,12 @@ extern crate actix_net; extern crate bytes; extern crate futures; -use std::{thread, time}; - -use actix::System; -use actix_net::server::Server; use actix_net::service::NewServiceExt; use bytes::Bytes; -use futures::future::{self, lazy, ok}; +use futures::future::{self, ok}; use actix_http::HttpMessage; -use actix_http::{client, h1, test, Request, Response}; +use actix_http::{client, h1, test::TestServer, Request, Response}; const STR: &str = "Hello World Hello World Hello World Hello World Hello World \ Hello World Hello World Hello World Hello World Hello World \ @@ -39,111 +35,70 @@ const STR: &str = "Hello World Hello World Hello World Hello World Hello World \ #[test] fn test_h1_v2() { - let addr = test::TestServer::unused_addr(); - thread::spawn(move || { - Server::new() - .bind("test", addr, move || { - h1::H1Service::build() - .finish(|_| future::ok::<_, ()>(Response::Ok().body(STR))) - .map(|_| ()) - }).unwrap() - .run(); + let mut srv = TestServer::with_factory(move || { + h1::H1Service::build() + .finish(|_| future::ok::<_, ()>(Response::Ok().body(STR))) + .map(|_| ()) }); - thread::sleep(time::Duration::from_millis(100)); + let mut connector = srv.new_connector(); - let mut sys = System::new("test"); - let mut connector = sys - .block_on(lazy(|| Ok::<_, ()>(client::Connector::default().service()))) - .unwrap(); - - let req = client::ClientRequest::get(format!("http://{}/", addr)) - .finish() - .unwrap(); - - let response = sys.block_on(req.send(&mut connector)).unwrap(); + let request = srv.get().finish().unwrap(); + let response = srv.block_on(request.send(&mut connector)).unwrap(); assert!(response.status().is_success()); - let request = client::ClientRequest::get(format!("http://{}/", addr)) - .header("x-test", "111") - .finish() - .unwrap(); + let request = srv.get().header("x-test", "111").finish().unwrap(); let repr = format!("{:?}", request); assert!(repr.contains("ClientRequest")); assert!(repr.contains("x-test")); - let response = sys.block_on(request.send(&mut connector)).unwrap(); + let response = srv.block_on(request.send(&mut connector)).unwrap(); assert!(response.status().is_success()); // read response - let bytes = sys.block_on(response.body()).unwrap(); + let bytes = srv.block_on(response.body()).unwrap(); assert_eq!(bytes, Bytes::from_static(STR.as_ref())); - let request = client::ClientRequest::post(format!("http://{}/", addr)) - .finish() - .unwrap(); - let response = sys.block_on(request.send(&mut connector)).unwrap(); + let request = srv.post().finish().unwrap(); + let response = srv.block_on(request.send(&mut connector)).unwrap(); assert!(response.status().is_success()); // read response - let bytes = sys.block_on(response.body()).unwrap(); + let bytes = srv.block_on(response.body()).unwrap(); assert_eq!(bytes, Bytes::from_static(STR.as_ref())); } #[test] fn test_connection_close() { - let addr = test::TestServer::unused_addr(); - thread::spawn(move || { - Server::new() - .bind("test", addr, move || { - h1::H1Service::build() - .finish(|_| ok::<_, ()>(Response::Ok().body(STR))) - .map(|_| ()) - }).unwrap() - .run(); + let mut srv = TestServer::with_factory(move || { + h1::H1Service::build() + .finish(|_| ok::<_, ()>(Response::Ok().body(STR))) + .map(|_| ()) }); - thread::sleep(time::Duration::from_millis(100)); + let mut connector = srv.new_connector(); - let mut sys = System::new("test"); - let mut connector = sys - .block_on(lazy(|| Ok::<_, ()>(client::Connector::default().service()))) - .unwrap(); - - let request = client::ClientRequest::get(format!("http://{}/", addr)) - .header("Connection", "close") - .finish() - .unwrap(); - let response = sys.block_on(request.send(&mut connector)).unwrap(); + let request = srv.get().close().finish().unwrap(); + let response = srv.block_on(request.send(&mut connector)).unwrap(); assert!(response.status().is_success()); } #[test] fn test_with_query_parameter() { - let addr = test::TestServer::unused_addr(); - thread::spawn(move || { - Server::new() - .bind("test", addr, move || { - h1::H1Service::build() - .finish(|req: Request| { - if req.uri().query().unwrap().contains("qp=") { - ok::<_, ()>(Response::Ok().finish()) - } else { - ok::<_, ()>(Response::BadRequest().finish()) - } - }).map(|_| ()) - }).unwrap() - .run(); + let mut srv = TestServer::with_factory(move || { + h1::H1Service::build() + .finish(|req: Request| { + if req.uri().query().unwrap().contains("qp=") { + ok::<_, ()>(Response::Ok().finish()) + } else { + ok::<_, ()>(Response::BadRequest().finish()) + } + }).map(|_| ()) }); - thread::sleep(time::Duration::from_millis(100)); + let mut connector = srv.new_connector(); - let mut sys = System::new("test"); - let mut connector = sys - .block_on(lazy(|| Ok::<_, ()>(client::Connector::default().service()))) - .unwrap(); - - let request = client::ClientRequest::get(format!("http://{}/?qp=5", addr)) + let request = client::ClientRequest::get(srv.url("/?qp=5")) .finish() .unwrap(); - let response = sys.block_on(request.send(&mut connector)).unwrap(); + let response = srv.block_on(request.send(&mut connector)).unwrap(); assert!(response.status().is_success()); } diff --git a/tests/test_server.rs b/tests/test_server.rs index 9d16e92e3..a01af4f0d 100644 --- a/tests/test_server.rs +++ b/tests/test_server.rs @@ -1,70 +1,48 @@ extern crate actix; extern crate actix_http; extern crate actix_net; -extern crate actix_web; extern crate bytes; extern crate futures; -use std::{io::Read, io::Write, net, thread, time}; +use std::{io::Read, io::Write, net}; -use actix::System; -use actix_net::server::Server; use actix_net::service::NewServiceExt; -use actix_web::{client, test, HttpMessage}; use bytes::Bytes; -use futures::future::{self, lazy, ok}; +use futures::future::{self, ok}; use futures::stream::once; use actix_http::{ - body, client as client2, h1, http, Body, Error, HttpMessage as HttpMessage2, - KeepAlive, Request, Response, + body, client, h1, http, test, Body, Error, HttpMessage as HttpMessage2, KeepAlive, + Request, Response, }; #[test] fn test_h1_v2() { - let addr = test::TestServer::unused_addr(); - thread::spawn(move || { - Server::new() - .bind("test", addr, move || { - h1::H1Service::build() - .keep_alive(KeepAlive::Disabled) - .client_timeout(1000) - .client_disconnect(1000) - .server_hostname("localhost") - .server_address(addr) - .finish(|_| future::ok::<_, ()>(Response::Ok().finish())) - .map(|_| ()) - }).unwrap() - .run(); + let mut srv = test::TestServer::with_factory(|| { + h1::H1Service::build() + .keep_alive(KeepAlive::Disabled) + .client_timeout(1000) + .client_disconnect(1000) + .server_hostname("localhost") + .finish(|_| future::ok::<_, ()>(Response::Ok().finish())) + .map(|_| ()) }); - thread::sleep(time::Duration::from_millis(100)); - let mut sys = System::new("test"); - { - let req = client::ClientRequest::get(format!("http://{}/", addr)) - .finish() - .unwrap(); - let response = sys.block_on(req.send()).unwrap(); - assert!(response.status().is_success()); - } + let req = client::ClientRequest::get(srv.url("/")).finish().unwrap(); + let response = srv.send_request(req).unwrap(); + assert!(response.status().is_success()); } #[test] fn test_slow_request() { - let addr = test::TestServer::unused_addr(); - thread::spawn(move || { - Server::new() - .bind("test", addr, move || { - h1::H1Service::build() - .client_timeout(100) - .finish(|_| future::ok::<_, ()>(Response::Ok().finish())) - .map(|_| ()) - }).unwrap() - .run(); + let srv = test::TestServer::with_factory(|| { + h1::H1Service::build() + .client_timeout(100) + .finish(|_| future::ok::<_, ()>(Response::Ok().finish())) + .map(|_| ()) }); - thread::sleep(time::Duration::from_millis(100)); - let mut stream = net::TcpStream::connect(addr).unwrap(); + 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); @@ -73,18 +51,11 @@ fn test_slow_request() { #[test] fn test_malformed_request() { - let addr = test::TestServer::unused_addr(); - thread::spawn(move || { - Server::new() - .bind("test", addr, move || { - h1::H1Service::new(|_| future::ok::<_, ()>(Response::Ok().finish())) - .map(|_| ()) - }).unwrap() - .run(); + let srv = test::TestServer::with_factory(|| { + h1::H1Service::new(|_| future::ok::<_, ()>(Response::Ok().finish())).map(|_| ()) }); - thread::sleep(time::Duration::from_millis(100)); - let mut stream = net::TcpStream::connect(addr).unwrap(); + 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); @@ -98,51 +69,42 @@ fn test_content_length() { StatusCode, }; - let addr = test::TestServer::unused_addr(); - thread::spawn(move || { - Server::new() - .bind("test", addr, move || { - h1::H1Service::new(|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(|_| ()) - }).unwrap() - .run(); + let mut srv = test::TestServer::with_factory(|| { + h1::H1Service::new(|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(|_| ()) }); - thread::sleep(time::Duration::from_millis(100)); let header = HeaderName::from_static("content-length"); let value = HeaderValue::from_static("0"); - let mut sys = System::new("test"); { for i in 0..4 { - let req = client::ClientRequest::get(format!("http://{}/{}", addr, i)) - .finish() - .unwrap(); - let response = sys.block_on(req.send()).unwrap(); + let req = client::ClientRequest::get(srv.url("/")).finish().unwrap(); + let response = srv.send_request(req).unwrap(); assert_eq!(response.headers().get(&header), None); - let req = client::ClientRequest::head(format!("http://{}/{}", addr, i)) + let req = client::ClientRequest::head(srv.url(&format!("/{}", i))) .finish() .unwrap(); - let response = sys.block_on(req.send()).unwrap(); + let response = srv.send_request(req).unwrap(); assert_eq!(response.headers().get(&header), None); } for i in 4..6 { - let req = client::ClientRequest::get(format!("http://{}/{}", addr, i)) + let req = client::ClientRequest::get(srv.url(&format!("/{}", i))) .finish() .unwrap(); - let response = sys.block_on(req.send()).unwrap(); + let response = srv.send_request(req).unwrap(); assert_eq!(response.headers().get(&header), Some(&value)); } } @@ -153,54 +115,41 @@ fn test_headers() { let data = STR.repeat(10); let data2 = data.clone(); - let addr = test::TestServer::unused_addr(); - thread::spawn(move || { - Server::new() - .bind("test", addr, move || { - let data = data.clone(); - h1::H1Service::new(move |_| { - let mut builder = Response::Ok(); - for idx in 0..90 { - builder.header( - format!("X-TEST-{}", idx).as_str(), - "TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ - TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ - TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ - TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ - TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ - TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ - TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ - TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ - TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ - TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ - TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ - TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ - TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST ", - ); - } - future::ok::<_, ()>(builder.body(data.clone())) - }).map(|_| ()) - }) - .unwrap() - .run() + let mut srv = test::TestServer::with_factory(move || { + let data = data.clone(); + h1::H1Service::new(move |_| { + let mut builder = Response::Ok(); + for idx in 0..90 { + builder.header( + format!("X-TEST-{}", idx).as_str(), + "TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ + TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ + TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ + TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ + TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ + TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ + TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ + TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ + TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ + TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ + TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ + TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ + TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST ", + ); + } + future::ok::<_, ()>(builder.body(data.clone())) + }).map(|_| ()) }); - thread::sleep(time::Duration::from_millis(200)); - let mut sys = System::new("test"); - let mut connector = sys - .block_on(lazy(|| { - Ok::<_, ()>(client2::Connector::default().service()) - })).unwrap(); + let mut connector = srv.new_connector(); - let req = client2::ClientRequest::get(format!("http://{}/", addr)) - .finish() - .unwrap(); + let req = srv.get().finish().unwrap(); - let response = sys.block_on(req.send(&mut connector)).unwrap(); + let response = srv.block_on(req.send(&mut connector)).unwrap(); assert!(response.status().is_success()); // read response - let bytes = sys.block_on(response.body()).unwrap(); + let bytes = srv.block_on(response.body()).unwrap(); assert_eq!(bytes, Bytes::from(data2)); } @@ -228,46 +177,27 @@ const STR: &str = "Hello World Hello World Hello World Hello World Hello World \ #[test] fn test_body() { - let addr = test::TestServer::unused_addr(); - thread::spawn(move || { - Server::new() - .bind("test", addr, move || { - h1::H1Service::new(|_| future::ok::<_, ()>(Response::Ok().body(STR))) - .map(|_| ()) - }).unwrap() - .run(); + let mut srv = test::TestServer::with_factory(|| { + h1::H1Service::new(|_| future::ok::<_, ()>(Response::Ok().body(STR))).map(|_| ()) }); - thread::sleep(time::Duration::from_millis(100)); - let mut sys = System::new("test"); - let req = client::ClientRequest::get(format!("http://{}/", addr)) - .finish() - .unwrap(); - let response = sys.block_on(req.send()).unwrap(); + let req = srv.get().finish().unwrap(); + let response = srv.send_request(req).unwrap(); assert!(response.status().is_success()); // read response - let bytes = sys.block_on(response.body()).unwrap(); + let bytes = srv.block_on(response.body()).unwrap(); assert_eq!(bytes, Bytes::from_static(STR.as_ref())); } #[test] fn test_head_empty() { - let addr = test::TestServer::unused_addr(); - thread::spawn(move || { - Server::new() - .bind("test", addr, move || { - h1::H1Service::new(|_| ok::<_, ()>(Response::Ok().body(STR))).map(|_| ()) - }).unwrap() - .run() + let mut srv = test::TestServer::with_factory(|| { + h1::H1Service::new(|_| ok::<_, ()>(Response::Ok().body(STR))).map(|_| ()) }); - thread::sleep(time::Duration::from_millis(100)); - let mut sys = System::new("test"); - let req = client::ClientRequest::head(format!("http://{}/", addr)) - .finish() - .unwrap(); - let response = sys.block_on(req.send()).unwrap(); + let req = client::ClientRequest::head(srv.url("/")).finish().unwrap(); + let response = srv.send_request(req).unwrap(); assert!(response.status().is_success()); { @@ -279,31 +209,20 @@ fn test_head_empty() { } // read response - let bytes = sys.block_on(response.body()).unwrap(); + let bytes = srv.block_on(response.body()).unwrap(); assert!(bytes.is_empty()); } #[test] fn test_head_binary() { - let addr = test::TestServer::unused_addr(); - thread::spawn(move || { - Server::new() - .bind("test", addr, move || { - h1::H1Service::new(|_| { - ok::<_, ()>( - Response::Ok().content_length(STR.len() as u64).body(STR), - ) - }).map(|_| ()) - }).unwrap() - .run() + let mut srv = test::TestServer::with_factory(|| { + h1::H1Service::new(|_| { + ok::<_, ()>(Response::Ok().content_length(STR.len() as u64).body(STR)) + }).map(|_| ()) }); - thread::sleep(time::Duration::from_millis(100)); - let mut sys = System::new("test"); - let req = client::ClientRequest::head(format!("http://{}/", addr)) - .finish() - .unwrap(); - let response = sys.block_on(req.send()).unwrap(); + let req = client::ClientRequest::head(srv.url("/")).finish().unwrap(); + let response = srv.send_request(req).unwrap(); assert!(response.status().is_success()); { @@ -315,27 +234,18 @@ fn test_head_binary() { } // read response - let bytes = sys.block_on(response.body()).unwrap(); + let bytes = srv.block_on(response.body()).unwrap(); assert!(bytes.is_empty()); } #[test] fn test_head_binary2() { - let addr = test::TestServer::unused_addr(); - thread::spawn(move || { - Server::new() - .bind("test", addr, move || { - h1::H1Service::new(|_| ok::<_, ()>(Response::Ok().body(STR))).map(|_| ()) - }).unwrap() - .run() + let mut srv = test::TestServer::with_factory(|| { + h1::H1Service::new(|_| ok::<_, ()>(Response::Ok().body(STR))).map(|_| ()) }); - thread::sleep(time::Duration::from_millis(100)); - let mut sys = System::new("test"); - let req = client::ClientRequest::head(format!("http://{}/", addr)) - .finish() - .unwrap(); - let response = sys.block_on(req.send()).unwrap(); + let req = client::ClientRequest::head(srv.url("/")).finish().unwrap(); + let response = srv.send_request(req).unwrap(); assert!(response.status().is_success()); { @@ -349,57 +259,40 @@ fn test_head_binary2() { #[test] fn test_body_length() { - let addr = test::TestServer::unused_addr(); - thread::spawn(move || { - Server::new() - .bind("test", addr, move || { - h1::H1Service::new(|_| { - let body = once(Ok(Bytes::from_static(STR.as_ref()))); - ok::<_, ()>(Response::Ok().body(Body::from_message( - body::SizedStream::new(STR.len(), body), - ))) - }).map(|_| ()) - }).unwrap() - .run() + let mut srv = test::TestServer::with_factory(|| { + h1::H1Service::new(|_| { + let body = once(Ok(Bytes::from_static(STR.as_ref()))); + ok::<_, ()>( + Response::Ok() + .body(Body::from_message(body::SizedStream::new(STR.len(), body))), + ) + }).map(|_| ()) }); - thread::sleep(time::Duration::from_millis(100)); - let mut sys = System::new("test"); - let req = client::ClientRequest::get(format!("http://{}/", addr)) - .finish() - .unwrap(); - let response = sys.block_on(req.send()).unwrap(); + let req = srv.get().finish().unwrap(); + let response = srv.send_request(req).unwrap(); assert!(response.status().is_success()); // read response - let bytes = sys.block_on(response.body()).unwrap(); + let bytes = srv.block_on(response.body()).unwrap(); assert_eq!(bytes, Bytes::from_static(STR.as_ref())); } #[test] fn test_body_chunked_explicit() { - let addr = test::TestServer::unused_addr(); - thread::spawn(move || { - Server::new() - .bind("test", addr, move || { - h1::H1Service::new(|_| { - let body = once::<_, Error>(Ok(Bytes::from_static(STR.as_ref()))); - ok::<_, ()>(Response::Ok().streaming(body)) - }).map(|_| ()) - }).unwrap() - .run() + let mut srv = test::TestServer::with_factory(|| { + h1::H1Service::new(|_| { + let body = once::<_, Error>(Ok(Bytes::from_static(STR.as_ref()))); + ok::<_, ()>(Response::Ok().streaming(body)) + }).map(|_| ()) }); - thread::sleep(time::Duration::from_millis(100)); - let mut sys = System::new("test"); - let req = client::ClientRequest::get(format!("http://{}/", addr)) - .finish() - .unwrap(); - let response = sys.block_on(req.send()).unwrap(); + let req = srv.get().finish().unwrap(); + let response = srv.send_request(req).unwrap(); assert!(response.status().is_success()); // read response - let bytes = sys.block_on(response.body()).unwrap(); + let bytes = srv.block_on(response.body()).unwrap(); // decode assert_eq!(bytes, Bytes::from_static(STR.as_ref())); @@ -407,27 +300,18 @@ fn test_body_chunked_explicit() { #[test] fn test_body_chunked_implicit() { - let addr = test::TestServer::unused_addr(); - thread::spawn(move || { - Server::new() - .bind("test", addr, move || { - h1::H1Service::new(|_| { - let body = once::<_, Error>(Ok(Bytes::from_static(STR.as_ref()))); - ok::<_, ()>(Response::Ok().streaming(body)) - }).map(|_| ()) - }).unwrap() - .run() + let mut srv = test::TestServer::with_factory(|| { + h1::H1Service::new(|_| { + let body = once::<_, Error>(Ok(Bytes::from_static(STR.as_ref()))); + ok::<_, ()>(Response::Ok().streaming(body)) + }).map(|_| ()) }); - thread::sleep(time::Duration::from_millis(100)); - let mut sys = System::new("test"); - let req = client::ClientRequest::get(format!("http://{}/", addr)) - .finish() - .unwrap(); - let response = sys.block_on(req.send()).unwrap(); + let req = srv.get().finish().unwrap(); + let response = srv.send_request(req).unwrap(); assert!(response.status().is_success()); // read response - let bytes = sys.block_on(response.body()).unwrap(); + let bytes = srv.block_on(response.body()).unwrap(); assert_eq!(bytes, Bytes::from_static(STR.as_ref())); } diff --git a/tests/test_ws.rs b/tests/test_ws.rs index c246d5e47..21f635129 100644 --- a/tests/test_ws.rs +++ b/tests/test_ws.rs @@ -5,20 +5,18 @@ extern crate actix_web; extern crate bytes; extern crate futures; -use std::{io, thread}; +use std::io; -use actix::System; use actix_net::codec::Framed; use actix_net::framed::IntoFramed; -use actix_net::server::Server; -use actix_net::service::{NewServiceExt, Service}; +use actix_net::service::NewServiceExt; use actix_net::stream::TakeItem; -use actix_web::{test, ws as web_ws}; +use actix_web::ws as web_ws; use bytes::{Bytes, BytesMut}; -use futures::future::{lazy, ok, Either}; -use futures::{Future, IntoFuture, Sink, Stream}; +use futures::future::{ok, Either}; +use futures::{Future, Sink, Stream}; -use actix_http::{h1, ws, ResponseError, SendResponse, ServiceConfig}; +use actix_http::{h1, test, ws, ResponseError, SendResponse, ServiceConfig}; fn ws_service(req: ws::Frame) -> impl Future { match req { @@ -43,72 +41,66 @@ fn ws_service(req: ws::Frame) -> impl Future)| { - // validate request - if let Some(h1::Message::Item(req)) = req { - match ws::verify_handshake(&req) { - Err(e) => { - // validation failed - Either::A( - SendResponse::send(framed, e.error_response()) - .map_err(|_| ()) - .map(|_| ()), - ) - } - Ok(_) => { - Either::B( - // send handshake response - SendResponse::send( - framed, - ws::handshake_response(&req).finish(), - ).map_err(|_| ()) - .and_then(|framed| { - // start websocket service - let framed = - framed.into_framed(ws::Codec::new()); - ws::Transport::with(framed, ws_service) - .map_err(|_| ()) - }), - ) - } - } - } else { - panic!() + let mut srv = test::TestServer::with_factory(|| { + IntoFramed::new(|| h1::Codec::new(ServiceConfig::default())) + .and_then(TakeItem::new().map_err(|_| ())) + .and_then(|(req, framed): (_, Framed<_, _>)| { + // validate request + if let Some(h1::Message::Item(req)) = req { + match ws::verify_handshake(&req) { + Err(e) => { + // validation failed + Either::A( + SendResponse::send(framed, e.error_response()) + .map_err(|_| ()) + .map(|_| ()), + ) } - }) - }).unwrap() - .run(); + Ok(_) => { + Either::B( + // send handshake response + SendResponse::send( + framed, + ws::handshake_response(&req).finish(), + ).map_err(|_| ()) + .and_then(|framed| { + // start websocket service + let framed = framed.into_framed(ws::Codec::new()); + ws::Transport::with(framed, ws_service) + .map_err(|_| ()) + }), + ) + } + } + } else { + panic!() + } + }) }); - let mut sys = System::new("test"); { - let (reader, mut writer) = sys - .block_on(web_ws::Client::new(format!("http://{}/", addr)).connect()) - .unwrap(); + let url = srv.url("/"); + + let (reader, mut writer) = + srv.block_on(web_ws::Client::new(url).connect()).unwrap(); writer.text("text"); - let (item, reader) = sys.block_on(reader.into_future()).unwrap(); + let (item, reader) = srv.block_on(reader.into_future()).unwrap(); assert_eq!(item, Some(web_ws::Message::Text("text".to_owned()))); writer.binary(b"text".as_ref()); - let (item, reader) = sys.block_on(reader.into_future()).unwrap(); + let (item, reader) = srv.block_on(reader.into_future()).unwrap(); assert_eq!( item, Some(web_ws::Message::Binary(Bytes::from_static(b"text").into())) ); writer.ping("ping"); - let (item, reader) = sys.block_on(reader.into_future()).unwrap(); + let (item, reader) = srv.block_on(reader.into_future()).unwrap(); assert_eq!(item, Some(web_ws::Message::Pong("ping".to_owned()))); writer.close(Some(web_ws::CloseCode::Normal.into())); - let (item, _) = sys.block_on(reader.into_future()).unwrap(); + let (item, _) = srv.block_on(reader.into_future()).unwrap(); assert_eq!( item, Some(web_ws::Message::Close(Some( @@ -118,39 +110,33 @@ fn test_simple() { } // client service - let mut client = sys - .block_on(lazy(|| Ok::<_, ()>(ws::Client::default()).into_future())) - .unwrap(); - let framed = sys - .block_on(client.call(ws::Connect::new(format!("http://{}/", addr)))) - .unwrap(); - - let framed = sys + let framed = srv.ws().unwrap(); + let framed = srv .block_on(framed.send(ws::Message::Text("text".to_string()))) .unwrap(); - let (item, framed) = sys.block_on(framed.into_future()).map_err(|_| ()).unwrap(); + let (item, framed) = srv.block_on(framed.into_future()).map_err(|_| ()).unwrap(); assert_eq!(item, Some(ws::Frame::Text(Some(BytesMut::from("text"))))); - let framed = sys + let framed = srv .block_on(framed.send(ws::Message::Binary("text".into()))) .unwrap(); - let (item, framed) = sys.block_on(framed.into_future()).map_err(|_| ()).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()))) ); - let framed = sys + let framed = srv .block_on(framed.send(ws::Message::Ping("text".into()))) .unwrap(); - let (item, framed) = sys.block_on(framed.into_future()).map_err(|_| ()).unwrap(); + let (item, framed) = srv.block_on(framed.into_future()).map_err(|_| ()).unwrap(); assert_eq!(item, Some(ws::Frame::Pong("text".to_string().into()))); - let framed = sys + let framed = srv .block_on(framed.send(ws::Message::Close(Some(ws::CloseCode::Normal.into())))) .unwrap(); - let (item, _framed) = sys.block_on(framed.into_future()).map_err(|_| ()).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())))