1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
//! Test server
use std::{io, net, thread};
use ntex_net::{tcp_connect, Io};
use ntex_rt::System;
use ntex_service::ServiceFactory;
use socket2::{Domain, SockAddr, Socket, Type};
use super::{Server, ServerBuilder};
/// Start test server
///
/// `TestServer` is very simple test server that simplify process of writing
/// integration tests cases for ntex web applications.
///
/// # Examples
///
/// ```rust
/// use ntex::http;
/// use ntex::http::client::Client;
/// use ntex::server;
/// use ntex::web::{self, App, HttpResponse};
///
/// async fn my_handler() -> Result<HttpResponse, std::io::Error> {
/// Ok(HttpResponse::Ok().into())
/// }
///
/// #[ntex::test]
/// async fn test_example() {
/// let mut srv = server::test_server(
/// || http::HttpService::new(
/// App::new().service(
/// web::resource("/").to(my_handler))
/// )
/// );
///
/// let req = Client::new().get("http://127.0.0.1:{}", srv.addr().port());
/// let response = req.send().await.unwrap();
/// assert!(response.status().is_success());
/// }
/// ```
pub fn test_server<F, R>(factory: F) -> TestServer
where
F: Fn() -> R + Send + Clone + 'static,
R: ServiceFactory<Io> + 'static,
{
let (tx, rx) = oneshot::channel();
// run server in separate thread
thread::spawn(move || {
let sys = System::new("ntex-test-server");
let tcp = net::TcpListener::bind("127.0.0.1:0").unwrap();
let local_addr = tcp.local_addr().unwrap();
let system = sys.system();
sys.run(move || {
let server = ServerBuilder::new()
.listen("test", tcp, move |_| factory())?
.set_tag("test", "TEST-SERVER")
.workers(1)
.disable_signals()
.run();
tx.send((system, local_addr, server))
.expect("Failed to send Server to TestServer");
Ok(())
})
});
let (system, addr, server) = rx.recv().unwrap();
TestServer {
addr,
server,
system,
}
}
/// Start new server with server builder
pub fn build_test_server<F>(factory: F) -> TestServer
where
F: FnOnce(ServerBuilder) -> ServerBuilder + Send + 'static,
{
let (tx, rx) = oneshot::channel();
// run server in separate thread
thread::spawn(move || {
let sys = System::new("ntex-test-server");
let system = sys.system();
sys.run(|| {
let server = factory(super::build()).workers(1).disable_signals().run();
tx.send((system, server))
.expect("Failed to send Server to TestServer");
Ok(())
})
});
let (system, server) = rx.recv().unwrap();
TestServer {
system,
server,
addr: "127.0.0.1:0".parse().unwrap(),
}
}
#[derive(Debug)]
/// Test server controller
pub struct TestServer {
addr: net::SocketAddr,
system: System,
server: Server,
}
impl TestServer {
/// Test server socket addr
pub fn addr(&self) -> net::SocketAddr {
self.addr
}
pub fn set_addr(mut self, addr: net::SocketAddr) -> Self {
self.addr = addr;
self
}
/// Connect to server, return Io
pub async fn connect(&self) -> io::Result<Io> {
tcp_connect(self.addr).await
}
/// Stop http server by stopping the runtime.
pub fn stop(&self) {
self.system.stop();
}
/// Get first available unused address
pub fn unused_addr() -> net::SocketAddr {
let addr: net::SocketAddr = "127.0.0.1:0".parse().unwrap();
let socket = Socket::new(Domain::IPV4, Type::STREAM, None).unwrap();
socket.set_reuse_address(true).unwrap();
socket.bind(&SockAddr::from(addr)).unwrap();
let tcp = net::TcpListener::from(socket);
tcp.local_addr().unwrap()
}
/// Get access to the running Server
pub fn server(&self) -> Server {
self.server.clone()
}
}
impl Drop for TestServer {
fn drop(&mut self) {
self.stop()
}
}