Skip to main content

actix_server/
test_server.rs

1use std::{io, net, sync::mpsc, thread};
2
3use actix_rt::{net::TcpStream, System};
4
5use crate::{Server, ServerBuilder, ServerHandle, ServerServiceFactory};
6
7/// A testing server.
8///
9/// `TestServer` is very simple test server that simplify process of writing integration tests for
10/// network applications.
11///
12/// # Examples
13/// ```
14/// use actix_service::fn_service;
15/// use actix_server::TestServer;
16///
17/// #[actix_rt::main]
18/// async fn main() {
19///     let srv = TestServer::start(|| fn_service(
20///         |sock| async move {
21///             println!("New connection: {:?}", sock);
22///             Ok::<_, ()>(())
23///         }
24///     ));
25///
26///     println!("SOCKET: {:?}", srv.connect());
27/// }
28/// ```
29pub struct TestServer;
30
31/// Test server handle.
32pub 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    /// Start new `TestServer` using application factory and default server config.
42    pub fn start(factory: impl ServerServiceFactory<TcpStream>) -> TestServerHandle {
43        Self::start_with_builder(Server::build(), factory)
44    }
45
46    /// Start new `TestServer` using application factory and server builder.
47    pub fn start_with_builder(
48        server_builder: ServerBuilder,
49        factory: impl ServerServiceFactory<TcpStream>,
50    ) -> TestServerHandle {
51        let (tx, rx) = mpsc::channel();
52
53        // run server in separate thread
54        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    /// Get first available unused local address.
86    ///
87    /// The listener is dropped before this method returns, so the port is no longer reserved.
88    /// Use [`Self::unused_listener()`] to keep the port reserved.
89    pub fn unused_addr() -> net::SocketAddr {
90        Self::unused_listener().1
91    }
92
93    /// Bind a TCP listener to an OS-assigned port on the IPv4 loopback address.
94    ///
95    /// Returns the nonblocking listener and its local address. The caller owns the listener, which
96    /// keeps the port reserved until it is dropped. Unlike [`Self::unused_addr()`], this method
97    /// leaves the listener open so it can be passed to [`ServerBuilder::listen()`].
98    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    /// Test server host.
118    pub fn host(&self) -> &str {
119        &self.host
120    }
121
122    /// Test server port.
123    pub fn port(&self) -> u16 {
124        self.port
125    }
126
127    /// Get test server address.
128    pub fn addr(&self) -> net::SocketAddr {
129        self.addr
130    }
131
132    /// Stop server.
133    fn stop(&mut self) {
134        drop(self.server_handle.stop(false));
135        self.thread_handle.take().unwrap().join().unwrap().unwrap();
136    }
137
138    /// Connect to server, returning a Tokio `TcpStream`.
139    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}