From f15140415ad827b6c865e5d61f744a7de8f59f82 Mon Sep 17 00:00:00 2001 From: Gary Russell Date: Wed, 5 Aug 2015 12:35:55 -0400 Subject: [PATCH] Properly detect available port on localhost in SocketUtils SocketUtils is used to find available ports on localhost; however, prior to this commit, SocketUtils incorrectly reported a port as available on localhost if another process was already bound to localhost on the given port but not to other network interfaces. In other words, SocketUtils determined that a given port was available for some interface though not necessarily for the loopback interface. This commit addresses this issue by refactoring SocketUtils so that it tests the loopback interface to ensure that the port is actually available for localhost. Issue: SPR-13321 --- .../org/springframework/util/SocketUtils.java | 9 +++-- .../util/SocketUtilsTests.java | 33 ++++++++++++++++++- 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/util/SocketUtils.java b/spring-core/src/main/java/org/springframework/util/SocketUtils.java index 907445487d..6754a30a8b 100644 --- a/spring-core/src/main/java/org/springframework/util/SocketUtils.java +++ b/spring-core/src/main/java/org/springframework/util/SocketUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2015 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. @@ -17,6 +17,7 @@ package org.springframework.util; import java.net.DatagramSocket; +import java.net.InetAddress; import java.net.ServerSocket; import java.util.Random; import java.util.SortedSet; @@ -34,6 +35,7 @@ import javax.net.ServerSocketFactory; * @author Ben Hale * @author Arjen Poutsma * @author Gunnar Hillert + * @author Gary Russell * @since 4.0 */ public class SocketUtils { @@ -196,7 +198,8 @@ public class SocketUtils { @Override protected boolean isPortAvailable(int port) { try { - ServerSocket serverSocket = ServerSocketFactory.getDefault().createServerSocket(port); + ServerSocket serverSocket = ServerSocketFactory.getDefault().createServerSocket(port, 1, + InetAddress.getByName("localhost")); serverSocket.close(); return true; } @@ -210,7 +213,7 @@ public class SocketUtils { @Override protected boolean isPortAvailable(int port) { try { - DatagramSocket socket = new DatagramSocket(port); + DatagramSocket socket = new DatagramSocket(port, InetAddress.getByName("localhost")); socket.close(); return true; } diff --git a/spring-core/src/test/java/org/springframework/util/SocketUtilsTests.java b/spring-core/src/test/java/org/springframework/util/SocketUtilsTests.java index f5e2c60ba9..362ef70132 100644 --- a/spring-core/src/test/java/org/springframework/util/SocketUtilsTests.java +++ b/spring-core/src/test/java/org/springframework/util/SocketUtilsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2015 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. @@ -16,7 +16,11 @@ package org.springframework.util; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.net.ServerSocket; import java.util.SortedSet; +import javax.net.ServerSocketFactory; import org.junit.Test; @@ -27,6 +31,7 @@ import static org.springframework.util.SocketUtils.*; * Unit tests for {@link SocketUtils}. * * @author Sam Brannen + * @author Gary Russell */ public class SocketUtilsTests { @@ -60,6 +65,19 @@ public class SocketUtilsTests { assertPortInRange(port, PORT_RANGE_MIN, PORT_RANGE_MAX); } + @Test(expected = IllegalStateException.class) + public void findAvailableTcpPortWhenPortOnLoopbackInterfaceIsNotAvailable() throws Exception { + int port = SocketUtils.findAvailableTcpPort(); + ServerSocket socket = ServerSocketFactory.getDefault().createServerSocket(port, 1, InetAddress.getByName("localhost")); + try { + // will only look for the exact port, since random.nextInt(1) always returns 0 + SocketUtils.findAvailableTcpPort(port, port + 1); + } + finally { + socket.close(); + } + } + @Test public void findAvailableTcpPortWithMin() { int port = SocketUtils.findAvailableTcpPort(50000); @@ -127,6 +145,19 @@ public class SocketUtilsTests { assertPortInRange(port, PORT_RANGE_MIN, PORT_RANGE_MAX); } + @Test(expected = IllegalStateException.class) + public void findAvailableUdpPortWhenPortOnLoopbackInterfaceIsNotAvailable() throws Exception { + int port = SocketUtils.findAvailableUdpPort(); + DatagramSocket socket = new DatagramSocket(port, InetAddress.getByName("localhost")); + try { + // will only look for the exact port, since random.nextInt(1) always returns 0 + SocketUtils.findAvailableUdpPort(port, port + 1); + } + finally { + socket.close(); + } + } + @Test public void findAvailableUdpPortWithMin() { int port = SocketUtils.findAvailableUdpPort(50000);