Skip to content

Commit

Permalink
Avoid UDP port clashes in tests
Browse files Browse the repository at this point in the history
Fixes #6489
  • Loading branch information
jamesnetherton committed Sep 19, 2024
1 parent 6bb6a7b commit e93268c
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@
package org.apache.camel.quarkus.test;

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.DatagramSocket;
import java.net.ServerSocket;
import java.util.HashMap;
import java.util.Map;
Expand Down Expand Up @@ -51,22 +50,30 @@ private AvailablePortFinder() {
}

/**
* Gets the next available port.
* Gets the next available TCP port.
*
* @throws IllegalStateException if there are no ports available
* @return the available port
*/
public static int getNextAvailable() {
return getNextAvailable(Protocol.TCP);
}

/**
* Gets the next available port for the given protocol.
*
* @param protocol the network protocol to reserve the port for. Either TCP or UDP
* @throws IllegalStateException if there are no ports available
* @return the available port
*/
public static int getNextAvailable(Protocol protocol) {
// Using AvailablePortFinder in native applications can be problematic
// E.g The reserved port may be allocated at build time and preserved indefinitely at runtime. I.e it never changes on each execution of the native application
logWarningIfNativeApplication();

while (true) {
try (ServerSocket ss = new ServerSocket()) {
ss.setReuseAddress(true);
ss.bind(new InetSocketAddress((InetAddress) null, 0), 1);

int port = ss.getLocalPort();
try {
int port = protocol.getPort();
if (!isQuarkusReservedPort(port)) {
String callerClassName = getCallerClassName();
String value = RESERVED_PORTS.putIfAbsent(port, callerClassName);
Expand All @@ -82,20 +89,34 @@ public static int getNextAvailable() {
}

/**
* Reserve a list of random and not in use network ports and place them in Map.
* Reserve a list of random and not in use TCP network ports and places them in Map.
*/
public static Map<String, Integer> reserveNetworkPorts(String... names) {
return reserveNetworkPorts(Function.identity(), names);
}

/**
* Reserve a list of random and not in use network ports and place them in Map.
* Reserve a list of random and not in use network ports for the given protocol and places them in Map.
*/
public static Map<String, Integer> reserveNetworkPorts(Protocol protocol, String... names) {
return reserveNetworkPorts(protocol, Function.identity(), names);
}

/**
* Reserve a list of random and not in use TCP network ports and places them in Map.
*/
public static <T> Map<String, T> reserveNetworkPorts(Function<Integer, T> converter, String... names) {
return reserveNetworkPorts(Protocol.TCP, converter, names);
}

/**
* Reserve a list of random and not in use network ports for the given protocol and places them in Map.
*/
public static <T> Map<String, T> reserveNetworkPorts(Protocol protocol, Function<Integer, T> converter, String... names) {
Map<String, T> reservedPorts = new HashMap<>();

for (String name : names) {
reservedPorts.put(name, converter.apply(getNextAvailable()));
reservedPorts.put(name, converter.apply(getNextAvailable(protocol)));
}

return reservedPorts;
Expand Down Expand Up @@ -139,4 +160,25 @@ private static void logWarningIfNativeApplication() {
+ "Pass the reserved port to the native application under test with QuarkusTestResourceLifecycleManager or via an HTTP request");
}
}

public enum Protocol {
TCP,
UDP;

int getPort() throws IOException {
if (this.equals(TCP)) {
try (ServerSocket socket = new ServerSocket()) {
socket.setReuseAddress(true);
socket.bind(null);
return socket.getLocalPort();
}
}

try (DatagramSocket socket = new DatagramSocket()) {
// NOTE: There's no need for socket.bind as it happens during DatagramSocket instantiation
socket.setReuseAddress(true);
return socket.getLocalPort();
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,13 @@

import io.quarkus.test.common.QuarkusTestResourceLifecycleManager;
import org.apache.camel.quarkus.test.AvailablePortFinder;
import org.apache.camel.quarkus.test.AvailablePortFinder.Protocol;

public class NettyUdpTestResource implements QuarkusTestResourceLifecycleManager {
@Override
public Map<String, String> start() {
return AvailablePortFinder.reserveNetworkPorts(
Protocol.UDP,
Objects::toString,
"camel.netty.test-udp-port",
"camel.netty.test-codec-udp-port",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,13 @@

import io.quarkus.test.common.QuarkusTestResourceLifecycleManager;
import org.apache.camel.quarkus.test.AvailablePortFinder;
import org.apache.camel.quarkus.test.AvailablePortFinder.Protocol;

public class SyslogTestResource implements QuarkusTestResourceLifecycleManager {

@Override
public Map<String, String> start() {
return AvailablePortFinder.reserveNetworkPorts(Objects::toString, "camel.netty.rfc5425.port",
return AvailablePortFinder.reserveNetworkPorts(Protocol.UDP, Objects::toString, "camel.netty.rfc5425.port",
"camel.netty.rfc3164.port");
}

Expand Down

0 comments on commit e93268c

Please sign in to comment.