
io.aeron.driver.media.WildcardPortManager Maven / Gradle / Ivy
Show all versions of aeron-all Show documentation
/*
* Copyright 2014-2024 Real Logic Limited.
*
* 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
*
* https://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 io.aeron.driver.media;
import org.agrona.collections.IntHashSet;
import java.net.BindException;
import java.net.InetSocketAddress;
/**
* Class for managing wildcard ports for UDP channel transports within a specific range.
*
* Does not track per interface, but as a range over all interfaces.
*/
public class WildcardPortManager implements PortManager
{
/**
* Placeholder to represent an empty port range.
*/
public static final int[] EMPTY_PORT_RANGE = new int[2];
final IntHashSet portSet = new IntHashSet();
final int highPort;
final int lowPort;
final boolean isOsWildcard;
final boolean isSender;
int nextPort;
/**
* Instantiate a wildcard port manager with the given port range.
*
* @param portRange for the port manager
* @param isSender for this port manager
*/
public WildcardPortManager(final int[] portRange, final boolean isSender)
{
this.lowPort = portRange[0];
this.highPort = portRange[1];
this.isOsWildcard = lowPort == highPort && 0 == lowPort;
this.nextPort = lowPort;
this.isSender = isSender;
}
/**
* {@inheritDoc}
*/
public InetSocketAddress getManagedPort(
final UdpChannel udpChannel,
final InetSocketAddress bindAddress) throws BindException
{
InetSocketAddress address = bindAddress;
if (bindAddress.getPort() != 0)
{
portSet.add(bindAddress.getPort());
}
else if (!isOsWildcard)
{
// do not map if not a subscription and does not have a control address. We want to use an ephemeral port
// for the control channel on publications.
if (!isSender || udpChannel.hasExplicitControl())
{
address = new InetSocketAddress(bindAddress.getAddress(), allocateOpenPort());
}
}
return address;
}
/**
* {@inheritDoc}
*/
public void freeManagedPort(final InetSocketAddress bindAddress)
{
if (bindAddress.getPort() != 0)
{
portSet.remove(bindAddress.getPort());
}
}
/**
* Parse a port range in the format "low high".
*
* @param value for the port range in string format.
* @return port range as low index 0 and high index 1.
*/
public static int[] parsePortRange(final String value)
{
if (null == value || value.isEmpty())
{
return EMPTY_PORT_RANGE;
}
final String[] ports = value.split(" +");
if (ports.length != 2)
{
throw new IllegalArgumentException("port range \"" + value + "\" incorrect format");
}
final int[] portRange = new int[2];
try
{
portRange[0] = Integer.parseUnsignedInt(ports[0]);
portRange[1] = Integer.parseUnsignedInt(ports[1]);
if (portRange[0] > 65535 || portRange[1] > 65535)
{
throw new NumberFormatException(value + ":port must be an integer value between 0 and 65535");
}
if (portRange[0] > portRange[1])
{
throw new NumberFormatException(value + ":low port value must be lower than high port value");
}
}
catch (final NumberFormatException ex)
{
throw new IllegalArgumentException("invalid port value " + value, ex);
}
return portRange;
}
private int findOpenPort()
{
for (int i = nextPort; i <= highPort; i++)
{
if (!portSet.contains(i))
{
return i;
}
}
for (int i = lowPort; i < nextPort; i++)
{
if (!portSet.contains(i))
{
return i;
}
}
return 0;
}
private int allocateOpenPort() throws BindException
{
final int port = findOpenPort();
if (0 == port)
{
throw new BindException("no available ports in range " + this.lowPort + " " + this.highPort);
}
nextPort = port + 1;
if (nextPort > highPort)
{
nextPort = lowPort;
}
portSet.add(port);
return port;
}
}