More robust localhost resolution

Ensure the resolved localhost can be bound and connected actually
This commit is contained in:
Trustin Lee 2012-11-10 08:45:07 +09:00
parent 922a8dbb47
commit 6be84510dc
2 changed files with 104 additions and 32 deletions

View File

@ -15,23 +15,12 @@
*/
package org.jboss.netty.handler.codec.frame;
import static org.junit.Assert.*;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.Random;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicReference;
import org.jboss.netty.bootstrap.ClientBootstrap;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelFactory;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.ExceptionEvent;
@ -43,6 +32,16 @@ import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.Random;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicReference;
import static org.junit.Assert.*;
public abstract class AbstractSocketFixedLengthEchoTest {
private static final Random random = new Random();
@ -83,10 +82,8 @@ public abstract class AbstractSocketFixedLengthEchoTest {
Channel sc = sb.bind(new InetSocketAddress(0));
int port = ((InetSocketAddress) sc.getLocalAddress()).getPort();
ChannelFuture ccf = cb.connect(new InetSocketAddress(TestUtil.getLocalHost(), port));
assertTrue(ccf.awaitUninterruptibly().isSuccess());
Channel cc = ccf.getChannel();
Channel cc = cb.connect(
new InetSocketAddress(TestUtil.getLocalHost(), port)).syncUninterruptibly().getChannel();
for (int i = 0; i < data.length;) {
int length = Math.min(random.nextInt(1024 * 3), data.length - i);
cc.write(ChannelBuffers.wrappedBuffer(data, i, length));

View File

@ -15,34 +15,43 @@
*/
package org.jboss.netty.util;
import org.junit.Ignore;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.UnknownHostException;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
/**
*/
@org.junit.Ignore
@Ignore
public final class TestUtil {
private static final InetAddress LOCALHOST;
private final static int START_PORT = 20000;
private final static int END_PORT = 30000;
static {
InetAddress localhost = null;
InetAddress localhost;
try {
localhost = InetAddress.getLocalHost();
} catch (UnknownHostException e) {
validateHost(localhost);
} catch (IOException e) {
// The default local host names did not work. Try hard-coded IPv4 address.
try {
localhost = InetAddress.getByAddress(new byte[] { 127, 0, 0, 1 });
} catch (UnknownHostException e1) {
validateHost(localhost);
} catch (IOException e1) {
// The hard-coded IPv4 address did not work. Try hard coded IPv6 address.
try {
localhost = InetAddress.getByAddress(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 });
} catch (UnknownHostException e2) {
System.err.println("Failed to get the localhost.");
e2.printStackTrace();
validateHost(localhost);
} catch (IOException e2) {
throw new Error("Failed to resolve localhost - incorrect network configuration?", e2);
}
}
}
@ -50,6 +59,41 @@ public final class TestUtil {
LOCALHOST = localhost;
}
private static void validateHost(InetAddress host) throws IOException {
ServerSocket ss = null;
Socket s1 = null;
Socket s2 = null;
try {
ss = new ServerSocket();
ss.setReuseAddress(false);
ss.bind(new InetSocketAddress(host, 0));
s1 = new Socket(host, ss.getLocalPort());
s2 = ss.accept();
} finally {
if (s2 != null) {
try {
s2.close();
} catch (IOException e) {
// Ignore
}
}
if (s1 != null) {
try {
s1.close();
} catch (IOException e) {
// Ignore
}
}
if (ss != null) {
try {
ss.close();
} catch (IOException e) {
// Ignore
}
}
}
}
public static InetAddress getLocalHost() {
// We cache this because some machine takes almost forever to return
// from InetAddress.getLocalHost(). I think it's due to the incorrect
@ -57,6 +101,26 @@ public final class TestUtil {
return LOCALHOST;
}
private static final int START_PORT = 32768;
private static final int END_PORT = 65536;
private static final int NUM_CANDIDATES = END_PORT - START_PORT;
private static final List<Integer> PORTS = new ArrayList<Integer>();
private static Iterator<Integer> portIterator;
static {
for (int i = START_PORT; i < END_PORT; i ++) {
PORTS.add(i);
}
Collections.shuffle(PORTS);
}
private static int nextCandidatePort() {
if (portIterator == null || !portIterator.hasNext()) {
portIterator = PORTS.iterator();
}
return portIterator.next();
}
/**
* Return a free port which can be used to bind to
@ -64,19 +128,30 @@ public final class TestUtil {
* @return port
*/
public static int getFreePort() {
for(int start = START_PORT; start <= END_PORT; start++) {
for (int i = 0; i < NUM_CANDIDATES; i ++) {
int port = nextCandidatePort();
try {
ServerSocket socket = new ServerSocket(start);
socket.setReuseAddress(true);
socket.close();
return start;
// Ensure it is possible to bind on both wildcard and loopback.
ServerSocket ss;
ss = new ServerSocket();
ss.setReuseAddress(false);
ss.bind(new InetSocketAddress(port));
ss.close();
ss = new ServerSocket();
ss.setReuseAddress(false);
ss.bind(new InetSocketAddress(LOCALHOST, port));
ss.close();
return port;
} catch (IOException e) {
// ignore
}
}
throw new RuntimeException("Unable to find a free port....");
throw new RuntimeException("unable to find a free port");
}
private TestUtil() {
// Unused
}