actix_server/
test_server.rs1use std::{io, net, sync::mpsc, thread};
2
3use actix_rt::{net::TcpStream, System};
4
5use crate::{Server, ServerBuilder, ServerHandle, ServerServiceFactory};
6
7pub struct TestServer;
30
31pub struct TestServerHandle {
33 addr: net::SocketAddr,
34 host: String,
35 port: u16,
36 server_handle: ServerHandle,
37 thread_handle: Option<thread::JoinHandle<io::Result<()>>>,
38}
39
40impl TestServer {
41 pub fn start(factory: impl ServerServiceFactory<TcpStream>) -> TestServerHandle {
43 Self::start_with_builder(Server::build(), factory)
44 }
45
46 pub fn start_with_builder(
48 server_builder: ServerBuilder,
49 factory: impl ServerServiceFactory<TcpStream>,
50 ) -> TestServerHandle {
51 let (tx, rx) = mpsc::channel();
52
53 let thread_handle = thread::spawn(move || {
55 let lst = net::TcpListener::bind("127.0.0.1:0").unwrap();
56 let local_addr = lst.local_addr().unwrap();
57
58 System::new().block_on(async {
59 let server = server_builder
60 .listen("test", lst, factory)
61 .unwrap()
62 .workers(1)
63 .disable_signals()
64 .run();
65
66 tx.send((server.handle(), local_addr)).unwrap();
67 server.await
68 })
69 });
70
71 let (server_handle, addr) = rx.recv().unwrap();
72
73 let host = format!("{}", addr.ip());
74 let port = addr.port();
75
76 TestServerHandle {
77 addr,
78 host,
79 port,
80 server_handle,
81 thread_handle: Some(thread_handle),
82 }
83 }
84
85 pub fn unused_addr() -> net::SocketAddr {
90 Self::unused_listener().1
91 }
92
93 pub fn unused_listener() -> (net::TcpListener, net::SocketAddr) {
99 use socket2::{Domain, Protocol, Socket, Type};
100
101 let addr: net::SocketAddr = "127.0.0.1:0".parse().unwrap();
102 let domain = Domain::for_address(addr);
103 let socket = Socket::new(domain, Type::STREAM, Some(Protocol::TCP)).unwrap();
104
105 socket.set_reuse_address(true).unwrap();
106 socket.set_nonblocking(true).unwrap();
107 socket.bind(&addr.into()).unwrap();
108 socket.listen(1024).unwrap();
109
110 let listener = net::TcpListener::from(socket);
111 let addr = listener.local_addr().unwrap();
112 (listener, addr)
113 }
114}
115
116impl TestServerHandle {
117 pub fn host(&self) -> &str {
119 &self.host
120 }
121
122 pub fn port(&self) -> u16 {
124 self.port
125 }
126
127 pub fn addr(&self) -> net::SocketAddr {
129 self.addr
130 }
131
132 fn stop(&mut self) {
134 drop(self.server_handle.stop(false));
135 self.thread_handle.take().unwrap().join().unwrap().unwrap();
136 }
137
138 pub fn connect(&self) -> io::Result<TcpStream> {
140 let stream = net::TcpStream::connect(self.addr)?;
141 stream.set_nonblocking(true)?;
142 TcpStream::from_std(stream)
143 }
144}
145
146impl Drop for TestServerHandle {
147 fn drop(&mut self) {
148 self.stop()
149 }
150}
151
152#[cfg(test)]
153mod tests {
154 use actix_service::fn_service;
155
156 use super::*;
157
158 #[tokio::test]
159 async fn connect_in_tokio_runtime() {
160 let srv = TestServer::start(|| fn_service(|_sock| async move { Ok::<_, ()>(()) }));
161 assert!(srv.connect().is_ok());
162 }
163
164 #[actix_rt::test]
165 async fn connect_in_actix_runtime() {
166 let srv = TestServer::start(|| fn_service(|_sock| async move { Ok::<_, ()>(()) }));
167 assert!(srv.connect().is_ok());
168 }
169}