1use std::{fmt, io, marker::PhantomData, net, thread};
3
4use ntex_io::Io;
5use ntex_net::tcp_connect;
6use ntex_rt::System;
7use ntex_service::{ServiceFactory, cfg::SharedCfg};
8use socket2::{Domain, SockAddr, Socket, Type};
9
10use super::{Server, ServerBuilder};
11
12pub struct TestServerBuilder<F, R> {
14 factory: F,
15 config: SharedCfg,
16 client_config: SharedCfg,
17 _t: PhantomData<R>,
18}
19
20impl<F, R> fmt::Debug for TestServerBuilder<F, R> {
21 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
22 f.debug_struct("TestServerBuilder")
23 .field("config", &self.config)
24 .field("client_config", &self.client_config)
25 .finish()
26 }
27}
28
29impl<F, R> TestServerBuilder<F, R>
30where
31 F: AsyncFn() -> R + Send + Clone + 'static,
32 R: ServiceFactory<Io, SharedCfg> + 'static,
33{
34 pub fn new(factory: F) -> Self {
35 Self {
36 factory,
37 config: SharedCfg::new("TEST-SERVER").into(),
38 client_config: SharedCfg::new("TEST-CLIENT").into(),
39 _t: PhantomData,
40 }
41 }
42
43 pub fn config<T: Into<SharedCfg>>(mut self, cfg: T) -> Self {
45 self.config = cfg.into();
46 self
47 }
48
49 pub fn client_config<T: Into<SharedCfg>>(mut self, cfg: T) -> Self {
51 self.client_config = cfg.into();
52 self
53 }
54
55 pub fn start(self) -> TestServer {
57 let factory = self.factory;
58 let config = self.config;
59
60 let (tx, rx) = oneshot::channel();
61 thread::spawn(move || {
63 let sys = System::new("ntex-test-server");
64 let tcp = net::TcpListener::bind("127.0.0.1:0").unwrap();
65 let local_addr = tcp.local_addr().unwrap();
66 let system = sys.system();
67
68 sys.run(move || {
69 let server = ServerBuilder::new()
70 .listen("test", tcp, async move |_| factory().await)?
71 .config("test", config)
72 .workers(1)
73 .disable_signals()
74 .run();
75
76 ntex_rt::spawn(async move {
77 ntex_util::time::sleep(ntex_util::time::Millis(75)).await;
78 tx.send((system, local_addr, server))
79 .expect("Failed to send Server to TestServer");
80 });
81
82 Ok(())
83 })
84 });
85
86 let (system, addr, server) = rx.recv().unwrap();
87
88 TestServer {
89 addr,
90 server,
91 system,
92 cfg: self.client_config,
93 }
94 }
95}
96
97pub fn test_server<F, R>(factory: F) -> TestServer
127where
128 F: AsyncFn() -> R + Send + Clone + 'static,
129 R: ServiceFactory<Io, SharedCfg> + 'static,
130{
131 TestServerBuilder::new(factory).start()
132}
133
134pub fn build_test_server<F>(factory: F) -> TestServer
136where
137 F: AsyncFnOnce(ServerBuilder) -> ServerBuilder + Send + 'static,
138{
139 let (tx, rx) = oneshot::channel();
140 thread::spawn(move || {
142 let sys = System::new("ntex-test-server");
143 let system = sys.system();
144
145 sys.block_on(async move {
146 let server = factory(super::build())
147 .await
148 .workers(1)
149 .disable_signals()
150 .run();
151 tx.send((system, server.clone()))
152 .expect("Failed to send Server to TestServer");
153 let _ = server.await;
154 });
155 });
156 let (system, server) = rx.recv().unwrap();
157
158 TestServer {
159 system,
160 server,
161 addr: "127.0.0.1:0".parse().unwrap(),
162 cfg: SharedCfg::new("TEST-CLIENT").into(),
163 }
164}
165
166#[derive(Debug)]
167pub struct TestServer {
169 addr: net::SocketAddr,
170 system: System,
171 server: Server,
172 cfg: SharedCfg,
173}
174
175impl TestServer {
176 pub fn addr(&self) -> net::SocketAddr {
178 self.addr
179 }
180
181 pub fn set_addr(mut self, addr: net::SocketAddr) -> Self {
182 self.addr = addr;
183 self
184 }
185
186 pub async fn connect(&self) -> io::Result<Io> {
188 tcp_connect(self.addr, self.cfg).await
189 }
190
191 pub fn stop(&self) {
193 self.system.stop();
194 }
195
196 pub fn unused_addr() -> net::SocketAddr {
198 let addr: net::SocketAddr = "127.0.0.1:0".parse().unwrap();
199 let socket = Socket::new(Domain::IPV4, Type::STREAM, None).unwrap();
200 socket.set_reuse_address(true).unwrap();
201 socket.bind(&SockAddr::from(addr)).unwrap();
202 let tcp = net::TcpListener::from(socket);
203 tcp.local_addr().unwrap()
204 }
205
206 pub fn server(&self) -> Server {
208 self.server.clone()
209 }
210}
211
212impl Drop for TestServer {
213 fn drop(&mut self) {
214 self.stop()
215 }
216}