View Javadoc
1   /*
2    * Copyright 2016 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.channel.Channel;
21  import io.netty.channel.ChannelHandlerContext;
22  import io.netty.channel.ChannelInboundHandlerAdapter;
23  import io.netty.channel.ChannelInitializer;
24  import io.netty.channel.ChannelOption;
25  import org.junit.jupiter.api.Test;
26  import org.junit.jupiter.api.TestInfo;
27  import org.junit.jupiter.api.Timeout;
28  
29  import java.io.IOException;
30  import java.util.Locale;
31  import java.util.concurrent.CountDownLatch;
32  import java.util.concurrent.TimeUnit;
33  import java.util.concurrent.atomic.AtomicReference;
34  
35  import static org.junit.jupiter.api.Assertions.assertInstanceOf;
36  import static org.junit.jupiter.api.Assertions.assertNull;
37  import static org.junit.jupiter.api.Assertions.assertTrue;
38  
39  public class SocketRstTest extends AbstractSocketTest {
40      protected void assertRstOnCloseException(IOException cause, Channel clientChannel) {
41          if (Locale.getDefault() == Locale.US || Locale.getDefault() == Locale.UK) {
42              assertTrue(cause.getMessage().contains("reset") || cause.getMessage().contains("closed"),
43                  "actual message: " + cause.getMessage());
44          }
45      }
46  
47      @Test
48      @Timeout(value = 30)
49      public void testSoLingerZeroCausesOnlyRstOnClose(TestInfo testInfo) throws Throwable {
50          run(testInfo, new Runner<ServerBootstrap, Bootstrap>() {
51              @Override
52              public void run(ServerBootstrap serverBootstrap, Bootstrap bootstrap) throws Throwable {
53                  testSoLingerZeroCausesOnlyRstOnClose(serverBootstrap, bootstrap);
54              }
55          });
56      }
57  
58      public void testSoLingerZeroCausesOnlyRstOnClose(ServerBootstrap sb, Bootstrap cb) throws Throwable {
59          final AtomicReference<Channel> serverChannelRef = new AtomicReference<Channel>();
60          final AtomicReference<Throwable> throwableRef = new AtomicReference<Throwable>();
61          final CountDownLatch latch = new CountDownLatch(1);
62          final CountDownLatch latch2 = new CountDownLatch(1);
63          // SO_LINGER=0 means that we must send ONLY a RST when closing (not a FIN + RST).
64          sb.childOption(ChannelOption.SO_LINGER, 0);
65          sb.childHandler(new ChannelInitializer<Channel>() {
66              @Override
67              protected void initChannel(Channel ch) throws Exception {
68                  serverChannelRef.compareAndSet(null, ch);
69                  latch.countDown();
70              }
71          });
72          cb.handler(new ChannelInitializer<Channel>() {
73              @Override
74              protected void initChannel(Channel ch) throws Exception {
75                  ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
76                      @Override
77                      public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
78                          throwableRef.compareAndSet(null, cause);
79                      }
80  
81                      @Override
82                      public void channelInactive(ChannelHandlerContext ctx) {
83                          latch2.countDown();
84                      }
85                  });
86              }
87          });
88          Channel sc = sb.bind().sync().channel();
89          Channel cc = cb.connect(sc.localAddress()).sync().channel();
90  
91          // Wait for the server to get setup.
92          latch.await();
93  
94          // The server has SO_LINGER=0 and so it must send a RST when close is called.
95          serverChannelRef.get().close();
96  
97          // Wait for the client to get channelInactive.
98          latch2.await();
99  
100         // Verify the client received a RST.
101         Throwable cause = throwableRef.get();
102         assertInstanceOf(IOException.class, cause,
103                 "actual [type, message]: [" + cause.getClass() + ", " + cause.getMessage() + "]");
104 
105         assertRstOnCloseException((IOException) cause, cc);
106     }
107 
108     @Test
109     @Timeout(value = 30)
110     public void testNoRstIfSoLingerOnClose(TestInfo testInfo) throws Throwable {
111         run(testInfo, new Runner<ServerBootstrap, Bootstrap>() {
112             @Override
113             public void run(ServerBootstrap serverBootstrap, Bootstrap bootstrap) throws Throwable {
114                 testNoRstIfSoLingerOnClose(serverBootstrap, bootstrap);
115             }
116         });
117     }
118 
119     public void testNoRstIfSoLingerOnClose(ServerBootstrap sb, Bootstrap cb) throws Throwable {
120         final AtomicReference<Channel> serverChannelRef = new AtomicReference<Channel>();
121         final AtomicReference<Throwable> throwableRef = new AtomicReference<Throwable>();
122         final CountDownLatch latch = new CountDownLatch(1);
123         final CountDownLatch latch2 = new CountDownLatch(1);
124         sb.childHandler(new ChannelInitializer<Channel>() {
125             @Override
126             protected void initChannel(Channel ch) throws Exception {
127                 serverChannelRef.compareAndSet(null, ch);
128                 latch.countDown();
129             }
130         });
131         cb.handler(new ChannelInitializer<Channel>() {
132             @Override
133             protected void initChannel(Channel ch) throws Exception {
134                 ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
135                     @Override
136                     public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
137                         throwableRef.compareAndSet(null, cause);
138                     }
139 
140                     @Override
141                     public void channelInactive(ChannelHandlerContext ctx) {
142                         latch2.countDown();
143                     }
144                 });
145             }
146         });
147         Channel sc = sb.bind().sync().channel();
148         cb.connect(sc.localAddress()).syncUninterruptibly();
149 
150         // Wait for the server to get setup.
151         latch.await();
152 
153         // The server has SO_LINGER=0 and so it must send a RST when close is called.
154         serverChannelRef.get().close();
155 
156         // Wait for the client to get channelInactive.
157         latch2.await();
158 
159         // Verify the client did not received a RST.
160         assertNull(throwableRef.get());
161     }
162 }