blob: a6ce5af79243b4f4a11784aad95051ff69ab6bac [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.ratis.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.net.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
public interface NetUtils {
Logger LOG = LoggerFactory.getLogger(NetUtils.class);
abstract class StaticResolution {
/** Host -> resolved name */
private static final Map<String, String> HOST_TO_RESOLVED = new ConcurrentHashMap<>();
/** Adds a static resolution for host. */
public static void put(String host, String resolvedName) {
HOST_TO_RESOLVED.put(host, resolvedName);
}
/** @return the resolved name, or null if the host is not found. */
public static String get(String host) {
return HOST_TO_RESOLVED.get(host);
}
}
/** The same as createSocketAddr(target, -1). */
static InetSocketAddress createSocketAddr(String target) {
return createSocketAddr(target, -1);
}
/**
* Create an InetSocketAddress from the given target and default port.
* @param target a string of either "host", "host:port" or "scheme://host:port/path"
* @param defaultPort the default port if <code>target</code> does not
* include a port number
*/
static InetSocketAddress createSocketAddr(String target, int defaultPort) {
target = Objects.requireNonNull(target, "target == null").trim();
boolean hasScheme = target.contains("://");
if (!hasScheme && target.charAt(0) == '/') {
target = target.substring(1);
}
final URI uri;
try {
uri = new URI(hasScheme? target: "dummy://"+target);
} catch (URISyntaxException e) {
throw new IllegalArgumentException("Failed to create URI from target " + target, e);
}
final String host = uri.getHost();
int port = uri.getPort();
if (port == -1) {
port = defaultPort;
}
final String path = uri.getPath();
if (host == null) {
throw new IllegalArgumentException("Host is null in " + target);
} else if (port < 0) {
throw new IllegalArgumentException("Port = " + port + " < 0 in " + target);
} else if (!hasScheme && path != null && !path.isEmpty()) {
throw new IllegalArgumentException("Unexpected path in " + target);
}
return createSocketAddrForHost(host, port);
}
/**
* Create a socket address with the given host and port. The hostname
* might be replaced with another host that was set via
* {@link StaticResolution#put(String, String)}.
* @param host the hostname or IP use to instantiate the object
* @param port the port number
* @return InetSocketAddress
*/
static InetSocketAddress createSocketAddrForHost(String host, int port) {
String staticHost = StaticResolution.get(host);
String resolveHost = (staticHost != null) ? staticHost : host;
InetSocketAddress addr;
try {
InetAddress iaddr = InetAddress.getByName(resolveHost);
// if there is a static entry for the host, make the returned
// address look like the original given host
if (staticHost != null) {
iaddr = InetAddress.getByAddress(host, iaddr.getAddress());
}
addr = new InetSocketAddress(iaddr, port);
} catch (UnknownHostException e) {
addr = InetSocketAddress.createUnresolved(host, port);
}
return addr;
}
/**
* Creates {@code count} unique local addresses. They may conflict with
* addresses created later, but not with one another. Addresses are
* guaranteed to be bound to the loopback interface.
* @param count number of unique local addresses to create
* @return {@code count} number of unique local addresses
*/
@Deprecated
static List<InetSocketAddress> createLocalServerAddress(int count) {
List<InetSocketAddress> list = new ArrayList<>(count);
for (int i = 0; i < count; i++) {
list.add(new InetSocketAddress(LOCALHOST, getFreePort()));
}
return list;
}
/**
* Creates a unique local address. Addresses are guaranteed to be bound to
* the loopback interface.
* @return unique local address
*/
@Deprecated
static InetSocketAddress createLocalServerAddress() {
return new InetSocketAddress(LOCALHOST, getFreePort());
}
static String address2String(InetSocketAddress address) {
if (address == null) {
return null;
}
final StringBuilder b = new StringBuilder(address.getHostName());
if (address.getAddress() instanceof Inet6Address) {
b.insert(0, '[').append(']');
}
return b.append(':').append(address.getPort()).toString();
}
String LOCALHOST = "localhost";
static String localhostWithFreePort() {
return LOCALHOST + ":" + getFreePort();
}
static int getFreePort() {
return PortAllocator.getFreePort();
}
/**
* Helper class to get free port avoiding randomness.
*/
final class PortAllocator {
private static final int MIN_PORT = 15000;
private static final int MAX_PORT = 32000;
private static final AtomicInteger NEXT_PORT = new AtomicInteger(MIN_PORT);
private PortAllocator() {
// no instances
}
public static synchronized int getFreePort() {
while (true) {
int port = NEXT_PORT.getAndIncrement();
if (port > MAX_PORT) {
NEXT_PORT.set(MIN_PORT);
port = NEXT_PORT.getAndIncrement();
}
try (ServerSocket ignored = new ServerSocket(port)) {
return port;
} catch (IOException e) {
// will try next port
}
}
}
}
}