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