1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty5.testsuite.transport.socket;
17
18 import io.netty5.bootstrap.Bootstrap;
19 import io.netty5.channel.Channel;
20 import io.netty5.channel.ChannelHandler;
21 import io.netty5.channel.ChannelHandlerContext;
22 import io.netty5.channel.ChannelOption;
23 import io.netty5.channel.ConnectTimeoutException;
24 import io.netty5.util.NetUtil;
25 import io.netty5.util.concurrent.Future;
26 import io.netty5.util.concurrent.GlobalEventExecutor;
27 import io.netty5.util.concurrent.Promise;
28 import io.netty5.util.internal.SocketUtils;
29 import io.netty5.util.internal.logging.InternalLoggerFactory;
30 import org.junit.jupiter.api.Test;
31 import org.junit.jupiter.api.TestInfo;
32 import org.junit.jupiter.api.Timeout;
33
34 import java.net.ConnectException;
35 import java.net.Socket;
36 import java.util.concurrent.ExecutionException;
37 import java.util.concurrent.TimeUnit;
38
39 import static io.netty5.testsuite.transport.socket.SocketTestPermutation.BAD_HOST;
40 import static io.netty5.testsuite.transport.socket.SocketTestPermutation.BAD_PORT;
41 import static org.assertj.core.api.Assertions.assertThat;
42 import static org.junit.jupiter.api.Assertions.assertFalse;
43 import static org.junit.jupiter.api.Assertions.assertThrows;
44 import static org.junit.jupiter.api.Assertions.fail;
45 import static org.junit.jupiter.api.Assumptions.assumeTrue;
46
47 public class SocketConnectionAttemptTest extends AbstractClientSocketTest {
48
49
50 private static final int UNASSIGNED_PORT = 4;
51
52 @Test
53 @Timeout(value = 30000, unit = TimeUnit.MILLISECONDS)
54 public void testConnectTimeout(TestInfo testInfo) throws Throwable {
55 run(testInfo, this::testConnectTimeout);
56 }
57
58 public void testConnectTimeout(Bootstrap cb) throws Throwable {
59 cb.handler(new TestHandler()).option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 2000);
60 Future<Channel> future = cb.connect(BAD_HOST, BAD_PORT);
61 assertThat(future.asStage().await(3000, TimeUnit.MILLISECONDS)).isTrue();
62 ExecutionException e = assertThrows(ExecutionException.class, future.asStage()::get);
63 assertThat(e).hasCauseInstanceOf(ConnectTimeoutException.class);
64 }
65
66 @Test
67 @Timeout(value = 30000, unit = TimeUnit.MILLISECONDS)
68 public void testConnectRefused(TestInfo testInfo) throws Throwable {
69 run(testInfo, this::testConnectRefused);
70 }
71
72 public void testConnectRefused(Bootstrap cb) throws Throwable {
73 testConnectRefused0(cb, false);
74 }
75
76 @Test
77 @Timeout(value = 30000, unit = TimeUnit.MILLISECONDS)
78 public void testConnectRefusedHalfClosure(TestInfo testInfo) throws Throwable {
79 run(testInfo, this::testConnectRefusedHalfClosure);
80 }
81
82 public void testConnectRefusedHalfClosure(Bootstrap cb) throws Throwable {
83 testConnectRefused0(cb, true);
84 }
85
86 private static void testConnectRefused0(Bootstrap cb, boolean halfClosure) throws Throwable {
87 final Promise<Error> errorPromise = GlobalEventExecutor.INSTANCE.newPromise();
88 ChannelHandler handler = new ChannelHandler() {
89 @Override
90 public void channelActive(ChannelHandlerContext ctx) throws Exception {
91 errorPromise.setFailure(new AssertionError("should have never been called"));
92 }
93 };
94
95 cb.handler(handler);
96 cb.option(ChannelOption.ALLOW_HALF_CLOSURE, halfClosure);
97 Throwable cause = cb.connect(NetUtil.LOCALHOST, UNASSIGNED_PORT).asStage().getCause();
98 assertThat(cause).isInstanceOf(ConnectException.class);
99 assertFalse(errorPromise.isFailed());
100 }
101
102 @Test
103 public void testConnectCancellation(TestInfo testInfo) throws Throwable {
104
105
106 boolean badHostTimedOut = true;
107 try (Socket socket = new Socket()) {
108 SocketUtils.connect(socket, SocketUtils.socketAddress(BAD_HOST, BAD_PORT), 10);
109 } catch (ConnectException e) {
110 badHostTimedOut = false;
111
112 } catch (Exception e) {
113
114 }
115
116
117 assumeTrue(badHostTimedOut, "The connection attempt to " + BAD_HOST + " does not time out.");
118
119 run(testInfo, this::testConnectCancellation);
120 }
121
122 public void testConnectCancellation(Bootstrap cb) throws Throwable {
123 cb.handler(new TestHandler()).option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 4000);
124 Future<Channel> future = cb.connect(BAD_HOST, BAD_PORT);
125 if (future.asStage().await(1000, TimeUnit.MILLISECONDS)) {
126 if (future.isSuccess()) {
127 fail("A connection attempt to " + BAD_HOST + " must not succeed.");
128 } else {
129 throw future.cause();
130 }
131 }
132
133 if (future.cancel()) {
134 assertThat(future.isCancelled()).isTrue();
135 } else {
136
137 future.asStage().get().close();
138 }
139 }
140
141 private static class TestHandler implements ChannelHandler {
142 @Override
143 public void channelExceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
144 InternalLoggerFactory.getInstance(
145 SocketConnectionAttemptTest.class).warn("Unexpected exception:", cause);
146 }
147 }
148 }