1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.channel.unix.tests;
17
18 import io.netty.channel.unix.Buffer;
19 import io.netty.channel.unix.DomainSocketAddress;
20 import io.netty.channel.unix.Errors;
21 import io.netty.channel.unix.Errors.NativeIoException;
22 import io.netty.channel.unix.Limits;
23 import io.netty.channel.unix.Socket;
24 import io.netty.channel.unix.UnixChannelUtil;
25 import io.netty.util.internal.CleanableDirectBuffer;
26 import io.netty.util.internal.PlatformDependent;
27 import org.junit.jupiter.api.AfterEach;
28 import org.junit.jupiter.api.BeforeEach;
29 import org.junit.jupiter.api.Test;
30 import org.opentest4j.TestAbortedException;
31
32 import java.io.File;
33 import java.io.IOException;
34 import java.nio.ByteBuffer;
35 import java.nio.charset.StandardCharsets;
36 import java.nio.file.Files;
37 import java.util.Arrays;
38
39 import static org.junit.jupiter.api.Assertions.assertArrayEquals;
40 import static org.junit.jupiter.api.Assertions.assertEquals;
41 import static org.junit.jupiter.api.Assertions.assertFalse;
42 import static org.junit.jupiter.api.Assertions.assertNotEquals;
43 import static org.junit.jupiter.api.Assertions.assertThrows;
44 import static org.junit.jupiter.api.Assertions.assertTrue;
45
46 public abstract class SocketTest<T extends Socket> {
47 protected T socket;
48
49 protected abstract T newSocket();
50
51 @BeforeEach
52 public void setup() {
53 socket = newSocket();
54 }
55
56 @AfterEach
57 public void tearDown() throws IOException {
58 socket.close();
59 }
60
61 @Test
62 public void testKeepAlive() throws Exception {
63 assertFalse(socket.isKeepAlive());
64 socket.setKeepAlive(true);
65 assertTrue(socket.isKeepAlive());
66 }
67
68 @Test
69 public void testTcpNoDelay() throws Exception {
70 assertFalse(socket.isTcpNoDelay());
71 socket.setTcpNoDelay(true);
72 assertTrue(socket.isTcpNoDelay());
73 }
74
75 @Test
76 public void testReceivedBufferSize() throws Exception {
77 int size = socket.getReceiveBufferSize();
78 int newSize = 65535;
79 assertTrue(size > 0);
80 socket.setReceiveBufferSize(newSize);
81
82 assertTrue(newSize <= socket.getReceiveBufferSize());
83 }
84
85 @Test
86 public void testSendBufferSize() throws Exception {
87 int size = socket.getSendBufferSize();
88 int newSize = 65535;
89 assertTrue(size > 0);
90 socket.setSendBufferSize(newSize);
91
92 assertTrue(newSize <= socket.getSendBufferSize());
93 }
94
95 @Test
96 public void testSoLinger() throws Exception {
97 assertEquals(-1, socket.getSoLinger());
98 socket.setSoLinger(10);
99 assertEquals(10, socket.getSoLinger());
100 }
101
102 @Test
103 public void testDoubleCloseDoesNotThrow() throws IOException {
104 Socket socket = Socket.newSocketStream();
105 socket.close();
106 socket.close();
107 }
108
109 @Test
110 public void testTrafficClass() throws IOException {
111
112 final int value = 0x08;
113 socket.setTrafficClass(value);
114 assertEquals(value, socket.getTrafficClass());
115 }
116
117 @Test
118 public void testIntOpt() throws IOException {
119 socket.setReuseAddress(false);
120 socket.setIntOpt(level(), optname(), 1);
121
122 assertNotEquals(0, socket.getIntOpt(level(), optname()));
123 socket.setIntOpt(level(), optname(), 0);
124
125 assertEquals(0, socket.getIntOpt(level(), optname()));
126 }
127
128 @Test
129 public void testRawOpt() throws IOException {
130 CleanableDirectBuffer cleanableDirectBuffer = Buffer.allocateDirectBufferWithNativeOrder(4);
131 ByteBuffer buffer = cleanableDirectBuffer.buffer();
132 buffer.putInt(1).flip();
133 socket.setRawOpt(level(), optname(), buffer);
134
135 ByteBuffer out = ByteBuffer.allocate(4);
136 socket.getRawOpt(level(), optname(), out);
137 assertFalse(out.hasRemaining());
138
139 out.flip();
140 assertNotEquals(ByteBuffer.allocate(0), out);
141 cleanableDirectBuffer.clean();
142 }
143
144 @Test
145 public void testSendToDomainSocketWithTooLongPath() throws IOException {
146 byte[] path = new byte[4096];
147 Arrays.fill(path, (byte) 'a');
148 path[0] = '/';
149
150 CleanableDirectBuffer cleanableDirectBuffer = Buffer.allocateDirectBufferWithNativeOrder(1);
151 Socket domainSocket = Socket.newSocketDomainDgram();
152 try {
153 ByteBuffer buffer = cleanableDirectBuffer.buffer();
154
155 NativeIoException e = assertThrows(NativeIoException.class,
156 () -> domainSocket.sendToDomainSocket(buffer, 0, 1, path));
157 assertEquals(Errors.ERRNO_ENAMETOOLONG_NEGATIVE, e.expectedErr());
158 } finally {
159 domainSocket.close();
160 cleanableDirectBuffer.clean();
161 }
162 }
163
164 protected int level() {
165 throw new TestAbortedException("Not supported");
166 }
167
168 protected int optname() {
169 throw new TestAbortedException("Not supported");
170 }
171
172 @Test
173 public void testBindDomainSocketDoesNotRemoveRegularFile() throws Exception {
174 File file = PlatformDependent.createTempFile("NETTY", "UDS", null);
175 try {
176 byte[] content = "do not delete".getBytes(StandardCharsets.US_ASCII);
177 Files.write(file.toPath(), content);
178
179 Socket domainSocket = Socket.newSocketDomain();
180 try {
181 assertThrows(IOException.class, () -> domainSocket.bind(new DomainSocketAddress(file)));
182 } finally {
183 domainSocket.close();
184 }
185 assertTrue(file.isFile());
186 assertArrayEquals(content, Files.readAllBytes(file.toPath()));
187 } finally {
188 file.delete();
189 }
190 }
191
192 @Test
193 public void testBindDomainSocketReplacesStaleSocket() throws Exception {
194 DomainSocketAddress address = UnixTestUtils.newDomainSocketAddress();
195 File file = new File(address.path());
196 try {
197 Socket first = Socket.newSocketDomain();
198 try {
199 first.bind(address);
200 } finally {
201 first.close();
202 }
203
204 assertTrue(file.exists());
205
206 Socket second = Socket.newSocketDomain();
207 try {
208 second.bind(address);
209 } finally {
210 second.close();
211 }
212 } finally {
213 file.delete();
214 }
215 }
216
217 @Test
218 public void testDeleteDomainSocketFileOnlyDeletesSockets() throws Exception {
219 File regular = PlatformDependent.createTempFile("NETTY", "UDS", null);
220 DomainSocketAddress address = UnixTestUtils.newDomainSocketAddress();
221 File socketFile = new File(address.path());
222 try {
223 assertFalse(UnixChannelUtil.deleteDomainSocketFile(regular.getPath()));
224 assertTrue(regular.exists());
225
226 Socket domainSocket = Socket.newSocketDomain();
227 try {
228 domainSocket.bind(address);
229 } finally {
230 domainSocket.close();
231 }
232 assertTrue(UnixChannelUtil.deleteDomainSocketFile(address.path()));
233 assertFalse(socketFile.exists());
234
235 assertFalse(UnixChannelUtil.deleteDomainSocketFile(address.path()));
236 } finally {
237 regular.delete();
238 socketFile.delete();
239 }
240 }
241
242 @Test
243 public void testConnectDomainSocketWithMaxLengthPath() throws Exception {
244 int max = Limits.UDS_SUN_PATH_SIZE;
245 assertTrue(max >= 100);
246
247 for (int i = 0; i < 100; i++) {
248 assertFalse(isConnectPathTooLong(max));
249 }
250 assertTrue(isConnectPathTooLong(max + 1));
251 }
252
253 private static boolean isConnectPathTooLong(int length) throws IOException {
254 StringBuilder path = new StringBuilder("/");
255 while (path.length() < length) {
256 path.append('a');
257 }
258 Socket domainSocket = Socket.newSocketDomain();
259 try {
260 domainSocket.connect(new DomainSocketAddress(path.toString()));
261 return false;
262 } catch (IOException e) {
263 return e.getMessage() != null && e.getMessage().contains("too long");
264 } finally {
265 domainSocket.close();
266 }
267 }
268 }