blob: c94ad1bd55348b7656c6a9f5cfeed346eb620427 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ignite.spi.discovery.tcp;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Arrays;
import javax.net.SocketFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocketFactory;
import org.apache.ignite.internal.IgniteFutureTimeoutCheckedException;
import org.apache.ignite.internal.IgniteInternalFuture;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.ssl.SslContextFactory;
import org.apache.ignite.testframework.GridTestUtils;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
/**
* Check a ssl socket configuration which used in discovery.
*/
public class DiscoveryClientSocketTest {
/** Port to listen. */
public static final int PORT_TO_LNSR = 12346;
/** Host for bind. */
public static final String HOST = "localhost";
/** SSL server socket factory. */
private SSLServerSocketFactory sslSrvSockFactory;
/** SSL socket factory. */
private SocketFactory sslSockFactory;
/** Fake TCP discovery SPI. */
private TcpDiscoverySpi fakeTcpDiscoverySpi;
/**
* Configure SSL and Discovery.
*/
@Before
public void before() {
SslContextFactory socketFactory = (SslContextFactory)GridTestUtils.sslTrustedFactory("node01", "trustone");
SSLContext sslCtx = socketFactory.create();
sslSrvSockFactory = sslCtx.getServerSocketFactory();
sslSockFactory = sslCtx.getSocketFactory();
fakeTcpDiscoverySpi = new TcpDiscoverySpi();
fakeTcpDiscoverySpi.setSoLinger(1);
}
/**
* It creates a SSL socket server and client for checks correctness closing when write exceed read.
*
* @throws Exception If failed.
*/
@Test
public void sslSocketTest() throws Exception {
try (ServerSocket listen = sslSrvSockFactory.createServerSocket(PORT_TO_LNSR)) {
System.out.println("Server started.");
IgniteInternalFuture clientFut = GridTestUtils.runAsync(this::startSslClient);
Socket connection = listen.accept();
try {
fakeTcpDiscoverySpi.configureSocketOptions(connection);
InputStream in = connection.getInputStream();
OutputStream out = connection.getOutputStream();
readHadshake(connection);
connection.getOutputStream().write(U.IGNITE_HEADER);
clientFut.get(10_000);
}
catch (IgniteFutureTimeoutCheckedException e) {
U.closeQuiet(connection);
fail("Can't wait connection closed from client side.");
}
catch (Exception e) {
U.closeQuiet(connection);
System.out.println("Ex: " + e.getMessage() + " (Socket closed)");
}
}
}
/**
* Reads handshake bytes and checks correctness.
*
* @param connection Socket connection.
* @throws IOException If have some issue happens in time read from socket.
*/
public void readHadshake(Socket connection) throws IOException {
byte[] buf = new byte[4];
int read = 0;
while (read < buf.length) {
int r = connection.getInputStream().read(buf, read, buf.length - read);
if (r >= 0)
read += r;
else
fail("Failed to read from socket.");
}
assertEquals("Handshake did not pass, readed bytes: " + read, Arrays.asList(U.IGNITE_HEADER), Arrays.asList(U.IGNITE_HEADER));
}
/**
* Test starts ssl client socket and writes data until socket's write blocking. When the socket is blocking on write
* tries to close it.
*/
public void startSslClient() {
try (Socket clientSocket = sslSockFactory.createSocket(HOST, PORT_TO_LNSR)) {
System.out.println("Client started.");
fakeTcpDiscoverySpi.configureSocketOptions(clientSocket);
long handshakeStartTime = System.currentTimeMillis();
//need to send message in order to ssl handshake passed.
clientSocket.getOutputStream().write(U.IGNITE_HEADER);
readHadshake(clientSocket);
long handshakeInterval = System.currentTimeMillis() - handshakeStartTime;
System.out.println("Handshake time: " + handshakeInterval + "ms");
int iter = 0;
try {
while (true) {
iter++;
IgniteInternalFuture writeFut = GridTestUtils.runAsync(() -> {
try {
clientSocket.getOutputStream().write(new byte[4 * 1024]);
}
catch (IOException e) {
assertEquals("Socket closed", e.getMessage());
}
});
writeFut.get(10 * handshakeInterval);
}
}
catch (IgniteFutureTimeoutCheckedException e) {
System.out.println("Socket stuck on write, when passed " + (iter * 4) + "KB through itself.");
}
System.out.println("Try to close a socket."); //see in try-catch-resource
}
catch (Exception e) {
fail(e.getMessage());
}
}
}