/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed 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.springframework.integration.ip.util;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.Socket;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.util.Enumeration;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.integration.ip.AbstractInternetProtocolReceivingChannelAdapter;
/**
* TCP/IP Test utilities.
*
* @author Gary Russell
* @author Gunnar Hillert
*
*/
public class SocketTestUtils {
public static final String TEST_STRING = "TestMessage";
private static final Log logger = LogFactory.getLog(SocketTestUtils.class);
private SocketTestUtils() {
super();
}
/**
* Sends a message in two chunks with a preceding length. Two such messages are sent.
* @param latch If not null, await until counted down before sending second chunk.
*/
public static CountDownLatch testSendLength(final int port, final CountDownLatch latch) {
final CountDownLatch testCompleteLatch = new CountDownLatch(1);
Thread thread = new Thread(() -> {
Socket socket = null;
try {
socket = new Socket(InetAddress.getByName("localhost"), port);
for (int i = 0; i < 2; i++) {
byte[] len = new byte[4];
ByteBuffer.wrap(len).putInt(TEST_STRING.length() * 2);
socket.getOutputStream().write(len);
socket.getOutputStream().write(TEST_STRING.getBytes());
logger.debug(i + " Wrote first part");
if (latch != null) {
latch.await();
}
Thread.sleep(500);
// send the second chunk
socket.getOutputStream().write(TEST_STRING.getBytes());
logger.debug(i + " Wrote second part");
}
testCompleteLatch.await(10, TimeUnit.SECONDS);
}
catch (Exception e1) {
e1.printStackTrace();
}
finally {
if (socket != null) {
try {
socket.close();
}
catch (IOException e2) { }
}
}
});
thread.setDaemon(true);
thread.start();
return testCompleteLatch;
}
/**
* Sends a message with a bad length part, causing an overflow on the receiver.
*/
public static CountDownLatch testSendLengthOverflow(final int port) {
final CountDownLatch testCompleteLatch = new CountDownLatch(1);
Thread thread = new Thread(() -> {
Socket socket = null;
try {
socket = new Socket(InetAddress.getByName("localhost"), port);
byte[] len = new byte[4];
ByteBuffer.wrap(len).putInt(Integer.MAX_VALUE);
socket.getOutputStream().write(len);
socket.getOutputStream().write(TEST_STRING.getBytes());
testCompleteLatch.await(10, TimeUnit.SECONDS);
}
catch (Exception e1) {
e1.printStackTrace();
}
finally {
if (socket != null) {
try {
socket.close();
}
catch (IOException e2) { }
}
}
});
thread.setDaemon(true);
thread.start();
return testCompleteLatch;
}
/**
* Test for reassembly of completely fragmented message; sends
* 6 bytes 500ms apart.
*/
public static CountDownLatch testSendFragmented(final int port, final int howMany, final boolean noDelay) {
final CountDownLatch testCompleteLatch = new CountDownLatch(1);
Thread thread = new Thread(() -> {
Socket socket = null;
try {
logger.debug("Connecting to " + port);
socket = new Socket(InetAddress.getByName("localhost"), port);
OutputStream os = socket.getOutputStream();
for (int i = 0; i < howMany; i++) {
writeByte(os, 0, noDelay);
writeByte(os, 0, noDelay);
writeByte(os, 0, noDelay);
writeByte(os, 2, noDelay);
writeByte(os, 'x', noDelay);
writeByte(os, 'x', noDelay);
}
testCompleteLatch.await(10, TimeUnit.SECONDS);
}
catch (Exception e1) {
e1.printStackTrace();
}
finally {
if (socket != null) {
try {
socket.close();
}
catch (IOException e2) { }
}
}
});
thread.setDaemon(true);
thread.start();
return testCompleteLatch;
}
private static void writeByte(OutputStream os, int b, boolean noDelay) throws Exception {
os.write(b);
logger.trace("Wrote 0x" + Integer.toHexString(b));
if (noDelay) {
return;
}
Thread.sleep(500);
}
/**
* Sends a STX/ETX message in two chunks. Two such messages are sent.
* @param latch If not null, await until counted down before sending second chunk.
*/
public static CountDownLatch testSendStxEtx(final int port, final CountDownLatch latch) {
final CountDownLatch testCompleteLatch = new CountDownLatch(1);
Thread thread = new Thread(() -> {
Socket socket = null;
try {
socket = new Socket(InetAddress.getByName("localhost"), port);
OutputStream outputStream = socket.getOutputStream();
for (int i = 0; i < 2; i++) {
writeByte(outputStream, 0x02, true);
outputStream.write(TEST_STRING.getBytes());
logger.debug(i + " Wrote first part");
if (latch != null) {
latch.await();
}
Thread.sleep(500);
// send the second chunk
outputStream.write(TEST_STRING.getBytes());
logger.debug(i + " Wrote second part");
writeByte(outputStream, 0x03, true);
}
testCompleteLatch.await(10, TimeUnit.SECONDS);
}
catch (Exception e1) {
e1.printStackTrace();
}
finally {
if (socket != null) {
try {
socket.close();
}
catch (IOException e2) { }
}
}
});
thread.setDaemon(true);
thread.start();
return testCompleteLatch;
}
/**
* Sends a large STX/ETX message with no ETX
*/
public static CountDownLatch testSendStxEtxOverflow(final int port) {
final CountDownLatch testCompleteLatch = new CountDownLatch(1);
Thread thread = new Thread(() -> {
Socket socket = null;
try {
socket = new Socket(InetAddress.getByName("localhost"), port);
OutputStream outputStream = socket.getOutputStream();
writeByte(outputStream, 0x02, true);
for (int i = 0; i < 1500; i++) {
writeByte(outputStream, 'x', true);
}
testCompleteLatch.await(10, TimeUnit.SECONDS);
}
catch (Exception e1) {
logger.debug("write failed", e1);
}
finally {
if (socket != null) {
try {
socket.close();
}
catch (IOException e2) { }
}
}
});
thread.setDaemon(true);
thread.start();
return testCompleteLatch;
}
/**
* Sends a message +CRLF in two chunks. Two such messages are sent.
* @param latch If not null, await until counted down before sending second chunk.
*/
public static CountDownLatch testSendCrLf(final int port, final CountDownLatch latch) {
final CountDownLatch testCompleteLatch = new CountDownLatch(1);
Thread thread = new Thread(() -> {
Socket socket = null;
try {
socket = new Socket(InetAddress.getByName("localhost"), port);
OutputStream outputStream = socket.getOutputStream();
for (int i = 0; i < 2; i++) {
outputStream.write(TEST_STRING.getBytes());
logger.debug(i + " Wrote first part");
if (latch != null) {
latch.await();
}
Thread.sleep(500);
// send the second chunk
outputStream.write(TEST_STRING.getBytes());
logger.debug(i + " Wrote second part");
writeByte(outputStream, '\r', true);
writeByte(outputStream, '\n', true);
}
testCompleteLatch.await(10, TimeUnit.SECONDS);
}
catch (Exception e1) {
e1.printStackTrace();
}
finally {
if (socket != null) {
try {
socket.close();
}
catch (IOException e2) { }
}
}
});
thread.setDaemon(true);
thread.start();
return testCompleteLatch;
}
/**
* Sends a single message +CRLF.
* @param latch Waits for latch to count down before closing the socket.
*/
public static void testSendCrLfSingle(final int port, final CountDownLatch latch) {
Thread thread = new Thread(() -> {
try {
Socket socket = new Socket(InetAddress.getByName("localhost"), port);
OutputStream outputStream = socket.getOutputStream();
outputStream.write(TEST_STRING.getBytes());
outputStream.write(TEST_STRING.getBytes());
writeByte(outputStream, '\r', true);
writeByte(outputStream, '\n', true);
if (latch != null) {
latch.await();
}
socket.close();
}
catch (Exception e) {
e.printStackTrace();
}
});
thread.setDaemon(true);
thread.start();
}
/**
* Sends a single message in two chunks and then closes the socket.
*/
public static void testSendRaw(final int port) {
Thread thread = new Thread(() -> {
try {
Socket socket = new Socket(InetAddress.getByName("localhost"), port);
OutputStream outputStream = socket.getOutputStream();
outputStream.write(TEST_STRING.getBytes());
outputStream.write(TEST_STRING.getBytes());
socket.close();
}
catch (Exception e) {
e.printStackTrace();
}
});
thread.setDaemon(true);
thread.start();
}
/**
* Sends two serialized objects over the same socket.
* @param port
*/
public static CountDownLatch testSendSerialized(final int port) {
final CountDownLatch testCompleteLatch = new CountDownLatch(1);
Thread thread = new Thread(() -> {
Socket socket = null;
try {
socket = new Socket(InetAddress.getByName("localhost"), port);
OutputStream outputStream = socket.getOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(outputStream);
oos.writeObject(TEST_STRING);
oos.flush();
oos = new ObjectOutputStream(outputStream);
oos.writeObject(TEST_STRING);
oos.flush();
testCompleteLatch.await(10, TimeUnit.SECONDS);
}
catch (Exception e1) {
e1.printStackTrace();
}
finally {
if (socket != null) {
try {
socket.close();
}
catch (IOException e2) { }
}
}
});
thread.setDaemon(true);
thread.start();
return testCompleteLatch;
}
/**
* Sends a large CRLF message with no CRLF.
*/
public static CountDownLatch testSendCrLfOverflow(final int port) {
final CountDownLatch testCompleteLatch = new CountDownLatch(1);
Thread thread = new Thread(() -> {
try {
Socket socket = new Socket(InetAddress.getByName("localhost"), port);
OutputStream outputStream = socket.getOutputStream();
for (int i = 0; i < 1500; i++) {
writeByte(outputStream, 'x', true);
}
testCompleteLatch.await(10, TimeUnit.SECONDS);
socket.close();
}
catch (Exception e) { }
});
thread.setDaemon(true);
thread.start();
return testCompleteLatch;
}
public static void setLocalNicIfPossible(
AbstractInternetProtocolReceivingChannelAdapter adapter)
throws UnknownHostException {
InetAddress[] nics = InetAddress.getAllByName(null);
if (nics.length > 0) {
// just listen on the loopback interface
String loopBack = nics[0].getHostAddress();
adapter.setLocalAddress(loopBack);
}
}
public static String chooseANic(boolean multicast) throws Exception {
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
while (interfaces.hasMoreElements()) {
NetworkInterface intface = interfaces.nextElement();
if (intface.isLoopback() || (multicast && !intface.supportsMulticast())
|| intface.getName().contains("vboxnet")) {
continue;
}
for (Enumeration<InetAddress> inetAddr = intface.getInetAddresses(); inetAddr.hasMoreElements(); ) {
InetAddress nextElement = inetAddr.nextElement();
if (nextElement instanceof Inet4Address) {
return nextElement.getHostAddress();
}
}
}
return null;
}
public static void waitListening(AbstractInternetProtocolReceivingChannelAdapter adapter) throws Exception {
int n = 0;
while (!adapter.isListening()) {
Thread.sleep(100);
if (n++ > 100) {
throw new Exception("Gateway failed to listen");
}
}
}
}