1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.testsuite.transport.socket;
17
18 import io.netty.bootstrap.Bootstrap;
19 import io.netty.bootstrap.ServerBootstrap;
20 import io.netty.buffer.ByteBuf;
21 import io.netty.channel.Channel;
22 import io.netty.channel.ChannelHandlerContext;
23 import io.netty.channel.ChannelInboundHandlerAdapter;
24 import io.netty.channel.ChannelInitializer;
25 import io.netty.channel.ChannelOption;
26 import io.netty.channel.SimpleChannelInboundHandler;
27 import io.netty.util.concurrent.DefaultEventExecutorGroup;
28 import io.netty.util.concurrent.EventExecutorGroup;
29 import io.netty.util.internal.PlatformDependent;
30 import org.junit.jupiter.api.AfterAll;
31 import org.junit.jupiter.api.BeforeAll;
32 import org.junit.jupiter.api.Test;
33 import org.junit.jupiter.api.TestInfo;
34 import org.junit.jupiter.api.Timeout;
35
36 import java.io.IOException;
37 import java.util.Random;
38 import java.util.SplittableRandom;
39 import java.util.concurrent.TimeUnit;
40 import java.util.concurrent.atomic.AtomicReference;
41
42 import static io.netty.testsuite.transport.TestsuitePermutation.randomBufferType;
43 import static org.junit.jupiter.api.Assertions.assertEquals;
44 import static org.junit.jupiter.api.Assertions.assertNotEquals;
45
46 public class SocketEchoTest extends AbstractSocketTest {
47
48 private static final Random random = new Random();
49 static final byte[] data = new byte[1048576];
50
51 private static EventExecutorGroup group;
52
53 static {
54 PlatformDependent.splittableRandomNextBytes(new SplittableRandom(random.nextLong()), data);
55 }
56
57 @BeforeAll
58 public static void createGroup() {
59 group = new DefaultEventExecutorGroup(2);
60 }
61
62 @AfterAll
63 public static void destroyGroup() throws Exception {
64 group.shutdownGracefully().sync();
65 }
66
67 @Test
68 @Timeout(value = 30000, unit = TimeUnit.MILLISECONDS)
69 public void testSimpleEcho(TestInfo testInfo) throws Throwable {
70 run(testInfo, new Runner<ServerBootstrap, Bootstrap>() {
71 @Override
72 public void run(ServerBootstrap serverBootstrap, Bootstrap bootstrap) throws Throwable {
73 testSimpleEcho(serverBootstrap, bootstrap);
74 }
75 });
76 }
77
78 public void testSimpleEcho(ServerBootstrap sb, Bootstrap cb) throws Throwable {
79 testSimpleEcho0(sb, cb, false, false, true);
80 }
81
82 @Test
83 @Timeout(value = 30000, unit = TimeUnit.MILLISECONDS)
84 public void testSimpleEchoNotAutoRead(TestInfo testInfo) throws Throwable {
85 run(testInfo, new Runner<ServerBootstrap, Bootstrap>() {
86 @Override
87 public void run(ServerBootstrap sb1, Bootstrap cb1) throws Throwable {
88 testSimpleEchoNotAutoRead(sb1, cb1);
89 }
90 });
91 }
92
93 public void testSimpleEchoNotAutoRead(ServerBootstrap sb, Bootstrap cb) throws Throwable {
94 testSimpleEcho0(sb, cb, false, false, false);
95 }
96
97 @Test
98 public void testSimpleEchoWithAdditionalExecutor(TestInfo testInfo) throws Throwable {
99 run(testInfo, new Runner<ServerBootstrap, Bootstrap>() {
100 @Override
101 public void run(ServerBootstrap sb1, Bootstrap cb1) throws Throwable {
102 testSimpleEchoWithAdditionalExecutor(sb1, cb1);
103 }
104 });
105 }
106
107 public void testSimpleEchoWithAdditionalExecutor(ServerBootstrap sb, Bootstrap cb) throws Throwable {
108 testSimpleEcho0(sb, cb, true, false, true);
109 }
110
111 @Test
112 public void testSimpleEchoWithAdditionalExecutorNotAutoRead(TestInfo testInfo) throws Throwable {
113 run(testInfo, new Runner<ServerBootstrap, Bootstrap>() {
114 @Override
115 public void run(ServerBootstrap sb1, Bootstrap cb1) throws Throwable {
116 testSimpleEchoWithAdditionalExecutorNotAutoRead(sb1, cb1);
117 }
118 });
119 }
120
121 public void testSimpleEchoWithAdditionalExecutorNotAutoRead(ServerBootstrap sb, Bootstrap cb) throws Throwable {
122 testSimpleEcho0(sb, cb, true, false, false);
123 }
124
125 @Test
126 public void testSimpleEchoWithVoidPromise(TestInfo testInfo) throws Throwable {
127 run(testInfo, new Runner<ServerBootstrap, Bootstrap>() {
128 @Override
129 public void run(ServerBootstrap sb1, Bootstrap cb1) throws Throwable {
130 testSimpleEchoWithVoidPromise(sb1, cb1);
131 }
132 });
133 }
134
135 public void testSimpleEchoWithVoidPromise(ServerBootstrap sb, Bootstrap cb) throws Throwable {
136 testSimpleEcho0(sb, cb, false, true, true);
137 }
138
139 @Test
140 public void testSimpleEchoWithVoidPromiseNotAutoRead(TestInfo testInfo) throws Throwable {
141 run(testInfo, new Runner<ServerBootstrap, Bootstrap>() {
142 @Override
143 public void run(ServerBootstrap sb1, Bootstrap cb1) throws Throwable {
144 testSimpleEchoWithVoidPromiseNotAutoRead(sb1, cb1);
145 }
146 });
147 }
148
149 public void testSimpleEchoWithVoidPromiseNotAutoRead(ServerBootstrap sb, Bootstrap cb) throws Throwable {
150 testSimpleEcho0(sb, cb, false, true, false);
151 }
152
153 @Test
154 @Timeout(value = 30000, unit = TimeUnit.MILLISECONDS)
155 public void testSimpleEchoWithAdditionalExecutorAndVoidPromise(TestInfo testInfo) throws Throwable {
156 run(testInfo, new Runner<ServerBootstrap, Bootstrap>() {
157 @Override
158 public void run(ServerBootstrap sb1, Bootstrap cb1) throws Throwable {
159 testSimpleEchoWithAdditionalExecutorAndVoidPromise(sb1, cb1);
160 }
161 });
162 }
163
164 public void testSimpleEchoWithAdditionalExecutorAndVoidPromise(ServerBootstrap sb, Bootstrap cb) throws Throwable {
165 testSimpleEcho0(sb, cb, true, true, true);
166 }
167
168 private static void testSimpleEcho0(
169 ServerBootstrap sb, Bootstrap cb, boolean additionalExecutor, boolean voidPromise, boolean autoRead)
170 throws Throwable {
171
172 final EchoHandler sh = new EchoHandler(autoRead);
173 final EchoHandler ch = new EchoHandler(autoRead);
174
175 if (additionalExecutor) {
176 sb.childHandler(new ChannelInitializer<Channel>() {
177 @Override
178 protected void initChannel(Channel c) throws Exception {
179 c.pipeline().addLast(group, sh);
180 }
181 });
182 cb.handler(new ChannelInitializer<Channel>() {
183 @Override
184 protected void initChannel(Channel c) throws Exception {
185 c.pipeline().addLast(group, ch);
186 }
187 });
188 } else {
189 sb.childHandler(sh);
190 sb.handler(new ChannelInboundHandlerAdapter() {
191 @Override
192 public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
193 cause.printStackTrace();
194 }
195 });
196 cb.handler(ch);
197 }
198 sb.childOption(ChannelOption.AUTO_READ, autoRead);
199 cb.option(ChannelOption.AUTO_READ, autoRead);
200
201 Channel sc = sb.bind().sync().channel();
202 Channel cc = cb.connect(sc.localAddress()).sync().channel();
203
204 SplittableRandom rng = new SplittableRandom(random.nextLong());
205 for (int i = 0; i < data.length;) {
206 int length = Math.min(rng.nextInt(1024 * 64), data.length - i);
207 ByteBuf buf = randomBufferType(cc.alloc(), data, i, length);
208 if (voidPromise) {
209 assertEquals(cc.voidPromise(), cc.writeAndFlush(buf, cc.voidPromise()));
210 } else {
211 assertNotEquals(cc.voidPromise(), cc.writeAndFlush(buf));
212 }
213 i += length;
214 }
215
216 while (ch.counter < data.length) {
217 if (sh.exception.get() != null) {
218 break;
219 }
220 if (ch.exception.get() != null) {
221 break;
222 }
223
224 Thread.sleep(50);
225 }
226
227 while (sh.counter < data.length) {
228 if (sh.exception.get() != null) {
229 break;
230 }
231 if (ch.exception.get() != null) {
232 break;
233 }
234
235 Thread.sleep(50);
236 }
237
238 sh.channel.close().sync();
239 ch.channel.close().sync();
240 sc.close().sync();
241
242 if (sh.exception.get() != null && !(sh.exception.get() instanceof IOException)) {
243 throw sh.exception.get();
244 }
245 if (ch.exception.get() != null && !(ch.exception.get() instanceof IOException)) {
246 throw ch.exception.get();
247 }
248 if (sh.exception.get() != null) {
249 throw sh.exception.get();
250 }
251 if (ch.exception.get() != null) {
252 throw ch.exception.get();
253 }
254 }
255
256 private static class EchoHandler extends SimpleChannelInboundHandler<ByteBuf> {
257 private final boolean autoRead;
258 volatile Channel channel;
259 final AtomicReference<Throwable> exception = new AtomicReference<Throwable>();
260 volatile int counter;
261
262 EchoHandler(boolean autoRead) {
263 this.autoRead = autoRead;
264 }
265
266 @Override
267 public void channelActive(ChannelHandlerContext ctx)
268 throws Exception {
269 channel = ctx.channel();
270 if (!autoRead) {
271 ctx.read();
272 }
273 }
274
275 @Override
276 public void channelRead0(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
277 byte[] actual = new byte[in.readableBytes()];
278 in.readBytes(actual);
279
280 int lastIdx = counter;
281 for (int i = 0; i < actual.length; i ++) {
282 assertEquals(data[i + lastIdx], actual[i]);
283 }
284
285
286
287 counter += actual.length;
288
289 if (channel.parent() != null) {
290 channel.write(randomBufferType(channel.alloc(), actual, 0, actual.length));
291 }
292 }
293
294 @Override
295 public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
296 try {
297 ctx.flush();
298 } finally {
299 if (!autoRead) {
300 ctx.read();
301 }
302 }
303 }
304
305 @Override
306 public void exceptionCaught(ChannelHandlerContext ctx,
307 Throwable cause) throws Exception {
308 if (exception.compareAndSet(null, cause)) {
309 cause.printStackTrace();
310 ctx.close();
311 }
312 }
313 }
314 }