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.channel.Channel;
20 import io.netty.channel.ChannelFuture;
21 import io.netty.channel.ChannelHandler;
22 import io.netty.channel.ChannelHandlerContext;
23 import io.netty.channel.ChannelInboundHandlerAdapter;
24 import io.netty.channel.ChannelOption;
25 import io.netty.channel.socket.oio.OioSocketChannel;
26 import io.netty.util.internal.SocketUtils;
27 import io.netty.util.NetUtil;
28 import io.netty.util.concurrent.GlobalEventExecutor;
29 import io.netty.util.concurrent.Promise;
30 import io.netty.util.internal.logging.InternalLoggerFactory;
31 import org.junit.jupiter.api.Test;
32 import org.junit.jupiter.api.TestInfo;
33 import org.junit.jupiter.api.Timeout;
34
35 import java.io.IOException;
36 import java.net.ConnectException;
37 import java.net.Socket;
38 import java.util.concurrent.TimeUnit;
39
40 import static io.netty.testsuite.transport.socket.SocketTestPermutation.BAD_HOST;
41 import static io.netty.testsuite.transport.socket.SocketTestPermutation.BAD_PORT;
42 import static org.hamcrest.CoreMatchers.*;
43 import static org.hamcrest.MatcherAssert.assertThat;
44 import static org.junit.jupiter.api.Assertions.assertFalse;
45 import static org.junit.jupiter.api.Assertions.fail;
46 import static org.junit.jupiter.api.Assumptions.assumeTrue;
47
48 public class SocketConnectionAttemptTest extends AbstractClientSocketTest {
49
50
51 private static final int UNASSIGNED_PORT = 4;
52
53 @Test
54 @Timeout(value = 30000, unit = TimeUnit.MILLISECONDS)
55 public void testConnectTimeout(TestInfo testInfo) throws Throwable {
56 run(testInfo, new Runner<Bootstrap>() {
57 @Override
58 public void run(Bootstrap bootstrap) throws Throwable {
59 testConnectTimeout(bootstrap);
60 }
61 });
62 }
63
64 public void testConnectTimeout(Bootstrap cb) throws Throwable {
65 cb.handler(new TestHandler()).option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 2000);
66 ChannelFuture future = cb.connect(BAD_HOST, BAD_PORT);
67 try {
68 assertThat(future.await(3000), is(true));
69 } finally {
70 future.channel().close();
71 }
72 }
73
74 @Test
75 @Timeout(value = 30000, unit = TimeUnit.MILLISECONDS)
76 public void testConnectRefused(TestInfo testInfo) throws Throwable {
77 run(testInfo, new Runner<Bootstrap>() {
78 @Override
79 public void run(Bootstrap bootstrap) throws Throwable {
80 testConnectRefused(bootstrap);
81 }
82 });
83 }
84
85 public void testConnectRefused(Bootstrap cb) throws Throwable {
86 testConnectRefused0(cb, false);
87 }
88
89 @Test
90 @Timeout(value = 30000, unit = TimeUnit.MILLISECONDS)
91 public void testConnectRefusedHalfClosure(TestInfo testInfo) throws Throwable {
92 run(testInfo, new Runner<Bootstrap>() {
93 @Override
94 public void run(Bootstrap bootstrap) throws Throwable {
95 testConnectRefusedHalfClosure(bootstrap);
96 }
97 });
98 }
99
100 public void testConnectRefusedHalfClosure(Bootstrap cb) throws Throwable {
101 testConnectRefused0(cb, true);
102 }
103
104 private static void testConnectRefused0(Bootstrap cb, boolean halfClosure) throws Throwable {
105 final Promise<Error> errorPromise = GlobalEventExecutor.INSTANCE.newPromise();
106 ChannelHandler handler = new ChannelInboundHandlerAdapter() {
107 @Override
108 public void channelActive(ChannelHandlerContext ctx) throws Exception {
109 errorPromise.setFailure(new AssertionError("should have never been called"));
110 }
111 };
112
113 cb.handler(handler);
114 cb.option(ChannelOption.ALLOW_HALF_CLOSURE, halfClosure);
115 ChannelFuture future = cb.connect(NetUtil.LOCALHOST, UNASSIGNED_PORT).awaitUninterruptibly();
116 assertThat(future.cause(), is(instanceOf(ConnectException.class)));
117 assertThat(errorPromise.cause(), is(nullValue()));
118 }
119
120 @Test
121 public void testConnectCancellation(TestInfo testInfo) throws Throwable {
122
123
124 boolean badHostTimedOut = true;
125 Socket socket = new Socket();
126 try {
127 SocketUtils.connect(socket, SocketUtils.socketAddress(BAD_HOST, BAD_PORT), 10);
128 } catch (ConnectException e) {
129 badHostTimedOut = false;
130
131 } catch (Exception e) {
132
133 } finally {
134 try {
135 socket.close();
136 } catch (IOException e) {
137
138 }
139 }
140
141 assumeTrue(badHostTimedOut, "The connection attempt to " + BAD_HOST + " does not time out.");
142
143 run(testInfo, new Runner<Bootstrap>() {
144 @Override
145 public void run(Bootstrap bootstrap) throws Throwable {
146 testConnectCancellation(bootstrap);
147 }
148 });
149 }
150
151 public void testConnectCancellation(Bootstrap cb) throws Throwable {
152 cb.handler(new TestHandler()).option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 4000);
153 ChannelFuture future = cb.connect(BAD_HOST, BAD_PORT);
154 try {
155 if (future.await(1000)) {
156 if (future.isSuccess()) {
157 fail("A connection attempt to " + BAD_HOST + " must not succeed.");
158 } else {
159 throw future.cause();
160 }
161 }
162
163 if (future.cancel(true)) {
164 assertThat(future.channel().closeFuture().await(500), is(true));
165 assertThat(future.isCancelled(), is(true));
166 } else {
167
168 assertFalse(isConnectCancellationSupported(future.channel()), future.channel().getClass() +
169 " should support connect cancellation");
170 }
171 } finally {
172 future.channel().close();
173 }
174 }
175
176 @SuppressWarnings("deprecation")
177 protected boolean isConnectCancellationSupported(Channel channel) {
178 return !(channel instanceof OioSocketChannel);
179 }
180
181 private static class TestHandler extends ChannelInboundHandlerAdapter {
182 @Override
183 public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
184 InternalLoggerFactory.getInstance(
185 SocketConnectionAttemptTest.class).warn("Unexpected exception:", cause);
186 }
187 }
188 }