1 //! Test a simple Unix-domain socket server and client.
2 //!
3 //! The client sends lists of integers and the server sends back sums.
4
5 // This test uses `AF_UNIX` with `SOCK_SEQPACKET` which is unsupported on macOS.
6 #![cfg(not(any(
7 target_os = "ios",
8 target_os = "macos",
9 target_os = "redox",
10 target_os = "wasi",
11 )))]
12 // This test uses `DecInt`.
13 #![cfg(feature = "itoa")]
14 #![cfg(feature = "fs")]
15
16 use rustix::fs::{cwd, unlinkat, AtFlags};
17 use rustix::io::{read, write};
18 use rustix::net::{
19 accept, bind_unix, connect_unix, listen, socket, AddressFamily, Protocol, SocketAddrUnix,
20 SocketType,
21 };
22 use rustix::path::DecInt;
23 use std::path::Path;
24 use std::str::FromStr;
25 use std::sync::{Arc, Condvar, Mutex};
26 use std::thread;
27
28 const BUFFER_SIZE: usize = 20;
29
server(ready: Arc<(Mutex<bool>, Condvar)>, path: &Path)30 fn server(ready: Arc<(Mutex<bool>, Condvar)>, path: &Path) {
31 let connection_socket = socket(
32 AddressFamily::UNIX,
33 SocketType::SEQPACKET,
34 Protocol::default(),
35 )
36 .unwrap();
37
38 let name = SocketAddrUnix::new(path).unwrap();
39 bind_unix(&connection_socket, &name).unwrap();
40 listen(&connection_socket, 1).unwrap();
41
42 {
43 let (lock, cvar) = &*ready;
44 let mut started = lock.lock().unwrap();
45 *started = true;
46 cvar.notify_all();
47 }
48
49 let mut buffer = vec![0; BUFFER_SIZE];
50 'exit: loop {
51 let data_socket = accept(&connection_socket).unwrap();
52 let mut sum = 0;
53 loop {
54 let nread = read(&data_socket, &mut buffer).unwrap();
55
56 if &buffer[..nread] == b"exit" {
57 break 'exit;
58 }
59 if &buffer[..nread] == b"sum" {
60 break;
61 }
62
63 sum += i32::from_str(&String::from_utf8_lossy(&buffer[..nread])).unwrap();
64 }
65
66 write(&data_socket, DecInt::new(sum).as_bytes()).unwrap();
67 }
68
69 unlinkat(cwd(), path, AtFlags::empty()).unwrap();
70 }
71
client(ready: Arc<(Mutex<bool>, Condvar)>, path: &Path, runs: &[(&[&str], i32)])72 fn client(ready: Arc<(Mutex<bool>, Condvar)>, path: &Path, runs: &[(&[&str], i32)]) {
73 {
74 let (lock, cvar) = &*ready;
75 let mut started = lock.lock().unwrap();
76 while !*started {
77 started = cvar.wait(started).unwrap();
78 }
79 }
80
81 let addr = SocketAddrUnix::new(path).unwrap();
82 let mut buffer = vec![0; BUFFER_SIZE];
83
84 for (args, sum) in runs {
85 let data_socket = socket(
86 AddressFamily::UNIX,
87 SocketType::SEQPACKET,
88 Protocol::default(),
89 )
90 .unwrap();
91 connect_unix(&data_socket, &addr).unwrap();
92
93 for arg in *args {
94 write(&data_socket, arg.as_bytes()).unwrap();
95 }
96 write(&data_socket, b"sum").unwrap();
97
98 let nread = read(&data_socket, &mut buffer).unwrap();
99 assert_eq!(
100 i32::from_str(&String::from_utf8_lossy(&buffer[..nread])).unwrap(),
101 *sum
102 );
103 }
104
105 let data_socket = socket(
106 AddressFamily::UNIX,
107 SocketType::SEQPACKET,
108 Protocol::default(),
109 )
110 .unwrap();
111 connect_unix(&data_socket, &addr).unwrap();
112 write(&data_socket, b"exit").unwrap();
113 }
114
115 #[test]
test_unix()116 fn test_unix() {
117 let ready = Arc::new((Mutex::new(false), Condvar::new()));
118 let ready_clone = Arc::clone(&ready);
119
120 let tmp = tempfile::tempdir().unwrap();
121 let path = tmp.path().join("soccer");
122 let send_path = path.to_owned();
123 let server = thread::Builder::new()
124 .name("server".to_string())
125 .spawn(move || {
126 server(ready, &send_path);
127 })
128 .unwrap();
129 let send_path = path.to_owned();
130 let client = thread::Builder::new()
131 .name("client".to_string())
132 .spawn(move || {
133 client(
134 ready_clone,
135 &send_path,
136 &[
137 (&["1", "2"], 3),
138 (&["4", "77", "103"], 184),
139 (&["5", "78", "104"], 187),
140 (&[], 0),
141 ],
142 );
143 })
144 .unwrap();
145 client.join().unwrap();
146 server.join().unwrap();
147 }
148