View Javadoc
1   /*
2    * Copyright 2012 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
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             // Update the counter before calling write(...) as write could in theory trigger another channelRead(...)
286             // which then would use the wrong lastIdx.
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 }