8304885: Reuse stale data to improve DNS resolver resiliency
Reviewed-by: michaelm, dfuchs
This commit is contained in:
parent
beec734fdd
commit
bdd81b3182
src/java.base/share
classes
conf/security
test/jdk/java/net/spi/InetAddressResolverProvider
AddressesCachingTest.javaAddressesStaleCachingTest.java
props
CacheStale.propsForeverCache.propsForeverCacheIgnoreMinusStale.propsForeverCacheIgnorePositiveStale.propsForeverCacheIgnoreZeroStale.propsNeverCache.propsNeverCacheIgnoreMinusStale.propsNeverCacheIgnorePositiveStale.propsNeverCacheIgnoreZeroStale.props
providers/simple/simple.provider/impl
@ -52,6 +52,7 @@ import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.ConcurrentSkipListSet;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@ -191,9 +192,9 @@ import static java.net.spi.InetAddressResolver.LookupPolicy.IPV6_FIRST;
|
||||
* <p> If the default behavior is not desired, then a Java security property
|
||||
* can be set to a different Time-to-live (TTL) value for positive
|
||||
* caching. Likewise, a system admin can configure a different
|
||||
* negative caching TTL value when needed.
|
||||
* negative caching TTL value when needed or extend the usage of the stale data.
|
||||
*
|
||||
* <p> Two Java security properties control the TTL values used for
|
||||
* <p> Three Java security properties control the TTL values used for
|
||||
* positive and negative host name resolution caching:
|
||||
*
|
||||
* <dl style="margin-left:2em">
|
||||
@ -205,6 +206,25 @@ import static java.net.spi.InetAddressResolver.LookupPolicy.IPV6_FIRST;
|
||||
* <p>
|
||||
* A value of -1 indicates "cache forever".
|
||||
* </dd>
|
||||
* <dt><b>networkaddress.cache.stale.ttl</b></dt>
|
||||
* <dd>Indicates the caching policy for stale names. The value is specified as
|
||||
* an integer to indicate the number of seconds that stale names will be kept in
|
||||
* the cache. A name is considered stale if the TTL has expired and an attempt
|
||||
* to lookup the host name again was not successful. This property is useful if
|
||||
* it is preferable to use a stale name rather than fail due to an unsuccessful
|
||||
* lookup. The default setting is to cache for an implementation specific period
|
||||
* of time.
|
||||
* <p>
|
||||
* If the value of this property is larger than "networkaddress.cache.ttl" then
|
||||
* "networkaddress.cache.ttl" will be used as a refresh interval of the name in
|
||||
* the cache. For example, if this property is set to 1 day and
|
||||
* "networkaddress.cache.ttl" is set to 30 seconds, then the positive response
|
||||
* will be cached for 1 day but an attempt to refresh it will be done every
|
||||
* 30 seconds.
|
||||
* <p>
|
||||
* A value of 0 (zero) or if the property is not set means do not use stale
|
||||
* names. Negative values are ignored.
|
||||
* </dd>
|
||||
* <dt><b>networkaddress.cache.negative.ttl</b> (default: 10)</dt>
|
||||
* <dd>Indicates the caching policy for un-successful name lookups
|
||||
* from the name service. The value is specified as an integer to
|
||||
@ -933,7 +953,7 @@ public sealed class InetAddress implements Serializable permits Inet4Address, In
|
||||
|
||||
// CachedAddresses that have to expire are kept ordered in this NavigableSet
|
||||
// which is scanned on each access
|
||||
private static final NavigableSet<CachedAddresses> expirySet =
|
||||
private static final NavigableSet<CachedLookup> expirySet =
|
||||
new ConcurrentSkipListSet<>();
|
||||
|
||||
// common interface
|
||||
@ -941,15 +961,22 @@ public sealed class InetAddress implements Serializable permits Inet4Address, In
|
||||
InetAddress[] get() throws UnknownHostException;
|
||||
}
|
||||
|
||||
// a holder for cached addresses with required metadata
|
||||
private static final class CachedAddresses implements Addresses, Comparable<CachedAddresses> {
|
||||
/**
|
||||
* A cached result of a name service lookup. The result can be either valid
|
||||
* addresses or invalid (ie a failed lookup) containing no addresses.
|
||||
*/
|
||||
private static class CachedLookup implements Addresses, Comparable<CachedLookup> {
|
||||
private static final AtomicLong seq = new AtomicLong();
|
||||
final String host;
|
||||
final InetAddress[] inetAddresses;
|
||||
final long expiryTime; // time of expiry (in terms of System.nanoTime())
|
||||
volatile InetAddress[] inetAddresses;
|
||||
/**
|
||||
* Time of expiry (in terms of System.nanoTime()). Can be modified only
|
||||
* when the record is not added to the "expirySet".
|
||||
*/
|
||||
volatile long expiryTime;
|
||||
final long id = seq.incrementAndGet(); // each instance is unique
|
||||
|
||||
CachedAddresses(String host, InetAddress[] inetAddresses, long expiryTime) {
|
||||
CachedLookup(String host, InetAddress[] inetAddresses, long expiryTime) {
|
||||
this.host = host;
|
||||
this.inetAddresses = inetAddresses;
|
||||
this.expiryTime = expiryTime;
|
||||
@ -964,7 +991,7 @@ public sealed class InetAddress implements Serializable permits Inet4Address, In
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(CachedAddresses other) {
|
||||
public int compareTo(CachedLookup other) {
|
||||
// natural order is expiry time -
|
||||
// compare difference of expiry times rather than
|
||||
// expiry times directly, to avoid possible overflow.
|
||||
@ -975,6 +1002,106 @@ public sealed class InetAddress implements Serializable permits Inet4Address, In
|
||||
// ties are broken using unique id
|
||||
return Long.compare(this.id, other.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current cache record is expired or not. Expired records
|
||||
* are removed from the expirySet and cache.
|
||||
*
|
||||
* @return {@code true} if the record was removed
|
||||
*/
|
||||
public boolean tryRemoveExpiredAddress(long now) {
|
||||
// compare difference of time instants rather than
|
||||
// time instants directly, to avoid possible overflow.
|
||||
// (see System.nanoTime() recommendations...)
|
||||
if ((expiryTime - now) < 0L) {
|
||||
// ConcurrentSkipListSet uses weakly consistent iterator,
|
||||
// so removing while iterating is OK...
|
||||
if (expirySet.remove(this)) {
|
||||
// ... remove from cache
|
||||
cache.remove(host, this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A cached valid lookup containing addresses whose validity may be
|
||||
* temporarily extended by an additional stale period pending the mapping
|
||||
* being refreshed or updated.
|
||||
*/
|
||||
private static final class ValidCachedLookup extends CachedLookup {
|
||||
/**
|
||||
* Time to refresh (in terms of System.nanoTime()).
|
||||
*/
|
||||
private volatile long refreshTime;
|
||||
/**
|
||||
* For how long the stale data should be used after TTL expiration.
|
||||
* Initially equal to the expiryTime, but increased over time after each
|
||||
* successful lookup.
|
||||
*/
|
||||
private volatile long staleTime;
|
||||
|
||||
/**
|
||||
* only one thread is doing lookup to name service
|
||||
* for particular host at any time.
|
||||
*/
|
||||
private final Lock lookupLock = new ReentrantLock();
|
||||
|
||||
ValidCachedLookup(String host, InetAddress[] inetAddresses,
|
||||
long staleTime, long refreshTime)
|
||||
{
|
||||
super(host, inetAddresses, staleTime);
|
||||
this.refreshTime = refreshTime;
|
||||
this.staleTime = staleTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InetAddress[] get() {
|
||||
long now = System.nanoTime();
|
||||
if ((refreshTime - now) < 0L && lookupLock.tryLock()) {
|
||||
try {
|
||||
// cachePolicy is in [s] - we need [ns]
|
||||
refreshTime = now + InetAddressCachePolicy.get() * 1000_000_000L;
|
||||
// getAddressesFromNameService returns non-empty/non-null value
|
||||
inetAddresses = getAddressesFromNameService(host);
|
||||
// don't update the "expirySet", will do that later
|
||||
staleTime = refreshTime + InetAddressCachePolicy.getStale() * 1000_000_000L;
|
||||
} catch (UnknownHostException ignore) {
|
||||
} finally {
|
||||
lookupLock.unlock();
|
||||
}
|
||||
}
|
||||
return inetAddresses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides the parent method to skip deleting the record from the
|
||||
* cache if the stale data can still be used. Note to update the
|
||||
* "expiryTime" field we have to remove the record from the expirySet
|
||||
* and add it back. It is not necessary to remove/add it here, we can do
|
||||
* that in the "get()" method above, but extracting it minimizes
|
||||
* contention on "expirySet".
|
||||
*/
|
||||
@Override
|
||||
public boolean tryRemoveExpiredAddress(long now) {
|
||||
// compare difference of time instants rather than
|
||||
// time instants directly, to avoid possible overflow.
|
||||
// (see System.nanoTime() recommendations...)
|
||||
if ((expiryTime - now) < 0L) {
|
||||
if ((staleTime - now) < 0L) {
|
||||
return super.tryRemoveExpiredAddress(now);
|
||||
}
|
||||
// ConcurrentSkipListSet uses weakly consistent iterator,
|
||||
// so removing while iterating is OK...
|
||||
if (expirySet.remove(this)) {
|
||||
expiryTime = staleTime;
|
||||
expirySet.add(this);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// a name service lookup based Addresses implementation which replaces itself
|
||||
@ -1021,18 +1148,33 @@ public sealed class InetAddress implements Serializable permits Inet4Address, In
|
||||
if (cachePolicy == InetAddressCachePolicy.NEVER) {
|
||||
cache.remove(host, this);
|
||||
} else {
|
||||
CachedAddresses cachedAddresses = new CachedAddresses(
|
||||
host,
|
||||
inetAddresses,
|
||||
cachePolicy == InetAddressCachePolicy.FOREVER
|
||||
? 0L
|
||||
// cachePolicy is in [s] - we need [ns]
|
||||
: System.nanoTime() + 1000_000_000L * cachePolicy
|
||||
);
|
||||
if (cache.replace(host, this, cachedAddresses) &&
|
||||
long now = System.nanoTime();
|
||||
long expiryTime =
|
||||
cachePolicy == InetAddressCachePolicy.FOREVER ?
|
||||
0L
|
||||
// cachePolicy is in [s] - we need [ns]
|
||||
: now + 1000_000_000L * cachePolicy;
|
||||
CachedLookup cachedLookup;
|
||||
if (InetAddressCachePolicy.getStale() > 0 &&
|
||||
ex == null && expiryTime > 0)
|
||||
{
|
||||
long refreshTime = expiryTime;
|
||||
// staleCachePolicy is in [s] - we need [ns]
|
||||
expiryTime = refreshTime + 1000_000_000L *
|
||||
InetAddressCachePolicy.getStale();
|
||||
cachedLookup = new ValidCachedLookup(host,
|
||||
inetAddresses,
|
||||
expiryTime,
|
||||
refreshTime);
|
||||
} else {
|
||||
cachedLookup = new CachedLookup(host,
|
||||
inetAddresses,
|
||||
expiryTime);
|
||||
}
|
||||
if (cache.replace(host, this, cachedLookup) &&
|
||||
cachePolicy != InetAddressCachePolicy.FOREVER) {
|
||||
// schedule expiry
|
||||
expirySet.add(cachedAddresses);
|
||||
expirySet.add(cachedLookup);
|
||||
}
|
||||
}
|
||||
if (inetAddresses == null || inetAddresses.length == 0) {
|
||||
@ -1638,18 +1780,8 @@ public sealed class InetAddress implements Serializable permits Inet4Address, In
|
||||
// remove expired addresses from cache - expirySet keeps them ordered
|
||||
// by expiry time so we only need to iterate the prefix of the NavigableSet...
|
||||
long now = System.nanoTime();
|
||||
for (CachedAddresses caddrs : expirySet) {
|
||||
// compare difference of time instants rather than
|
||||
// time instants directly, to avoid possible overflow.
|
||||
// (see System.nanoTime() recommendations...)
|
||||
if ((caddrs.expiryTime - now) < 0L) {
|
||||
// ConcurrentSkipListSet uses weakly consistent iterator,
|
||||
// so removing while iterating is OK...
|
||||
if (expirySet.remove(caddrs)) {
|
||||
// ... remove from cache
|
||||
cache.remove(caddrs.host, caddrs);
|
||||
}
|
||||
} else {
|
||||
for (CachedLookup caddrs : expirySet) {
|
||||
if (!caddrs.tryRemoveExpiredAddress(now)) {
|
||||
// we encountered 1st element that expires in future
|
||||
break;
|
||||
}
|
||||
@ -1662,7 +1794,7 @@ public sealed class InetAddress implements Serializable permits Inet4Address, In
|
||||
} else {
|
||||
addrs = cache.remove(host);
|
||||
if (addrs != null) {
|
||||
if (addrs instanceof CachedAddresses) {
|
||||
if (addrs instanceof CachedLookup) {
|
||||
// try removing from expirySet too if CachedAddresses
|
||||
expirySet.remove(addrs);
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
<!DOCTYPE HTML>
|
||||
<!--
|
||||
Copyright (c) 1998, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
Copyright (c) 1998, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
|
||||
This code is free software; you can redistribute it and/or modify it
|
||||
@ -267,13 +267,22 @@ tuning on how the cache is operating.</P>
|
||||
policy, while a value of 0 (zero) means no caching. The default value
|
||||
is -1 (forever) if a security manager is installed, and implementation-specific
|
||||
when no security manager is installed.</P>
|
||||
<LI><P><B>{@systemProperty networkaddress.cache.stale.ttl}</B> (default: see below)<BR>
|
||||
Value is an integer corresponding to the number of seconds that stale names
|
||||
will be kept in the cache. A name is considered stale if the TTL has expired
|
||||
and an attempt to lookup the host name again was not successful. This
|
||||
property is useful if it is preferable to use a stale name rather than
|
||||
fail due to an unsuccessful lookup.
|
||||
A value of 0 (zero) or if the property is not set means do not use stale
|
||||
names. Negative values are ignored.
|
||||
The default value is implementation-specific.</P>
|
||||
<LI><P><B>{@systemProperty networkaddress.cache.negative.ttl}</B> (default: {@code 10})<BR>
|
||||
Value is an integer corresponding to the number of seconds an
|
||||
unsuccessful name lookup will be kept in the cache. A value of -1,
|
||||
or any negative value, means “cache forever”, while a
|
||||
value of 0 (zero) means no caching.</P>
|
||||
</UL>
|
||||
<P>Since these 2 properties are part of the security policy, they are
|
||||
<P>Since these 3 properties are part of the security policy, they are
|
||||
not set by either the -D option or the {@code System.setProperty()} API,
|
||||
instead they are set as security properties.</P>
|
||||
<a id="Unixdomain"></a>
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1998, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1998, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -27,6 +27,7 @@ package sun.net;
|
||||
|
||||
import java.security.PrivilegedAction;
|
||||
import java.security.Security;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@SuppressWarnings("removal")
|
||||
public final class InetAddressCachePolicy {
|
||||
@ -36,6 +37,12 @@ public final class InetAddressCachePolicy {
|
||||
private static final String cachePolicyPropFallback =
|
||||
"sun.net.inetaddr.ttl";
|
||||
|
||||
// Controls the cache stale policy for successful lookups only
|
||||
private static final String cacheStalePolicyProp =
|
||||
"networkaddress.cache.stale.ttl";
|
||||
private static final String cacheStalePolicyPropFallback =
|
||||
"sun.net.inetaddr.stale.ttl";
|
||||
|
||||
// Controls the cache policy for negative lookups only
|
||||
private static final String negativeCachePolicyProp =
|
||||
"networkaddress.cache.negative.ttl";
|
||||
@ -59,6 +66,15 @@ public final class InetAddressCachePolicy {
|
||||
*/
|
||||
private static volatile int cachePolicy = FOREVER;
|
||||
|
||||
/* The Java-level namelookup cache stale policy:
|
||||
*
|
||||
* any positive value: the number of seconds to use the stale names
|
||||
* zero: do not use stale names
|
||||
*
|
||||
* default value is never (NEVER).
|
||||
*/
|
||||
private static volatile int staleCachePolicy = NEVER;
|
||||
|
||||
/* The Java-level namelookup cache policy for negative lookups:
|
||||
*
|
||||
* -1: caching forever
|
||||
@ -85,31 +101,7 @@ public final class InetAddressCachePolicy {
|
||||
* Initialize
|
||||
*/
|
||||
static {
|
||||
|
||||
Integer tmp = java.security.AccessController.doPrivileged(
|
||||
new PrivilegedAction<Integer>() {
|
||||
public Integer run() {
|
||||
try {
|
||||
String tmpString = Security.getProperty(cachePolicyProp);
|
||||
if (tmpString != null) {
|
||||
return Integer.valueOf(tmpString);
|
||||
}
|
||||
} catch (NumberFormatException ignored) {
|
||||
// Ignore
|
||||
}
|
||||
|
||||
try {
|
||||
String tmpString = System.getProperty(cachePolicyPropFallback);
|
||||
if (tmpString != null) {
|
||||
return Integer.decode(tmpString);
|
||||
}
|
||||
} catch (NumberFormatException ignored) {
|
||||
// Ignore
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
Integer tmp = getProperty(cachePolicyProp, cachePolicyPropFallback);
|
||||
if (tmp != null) {
|
||||
cachePolicy = tmp < 0 ? FOREVER : tmp;
|
||||
propertySet = true;
|
||||
@ -121,40 +113,60 @@ public final class InetAddressCachePolicy {
|
||||
cachePolicy = DEFAULT_POSITIVE;
|
||||
}
|
||||
}
|
||||
tmp = java.security.AccessController.doPrivileged (
|
||||
new PrivilegedAction<Integer>() {
|
||||
public Integer run() {
|
||||
try {
|
||||
String tmpString = Security.getProperty(negativeCachePolicyProp);
|
||||
if (tmpString != null) {
|
||||
return Integer.valueOf(tmpString);
|
||||
}
|
||||
} catch (NumberFormatException ignored) {
|
||||
// Ignore
|
||||
}
|
||||
|
||||
try {
|
||||
String tmpString = System.getProperty(negativeCachePolicyPropFallback);
|
||||
if (tmpString != null) {
|
||||
return Integer.decode(tmpString);
|
||||
}
|
||||
} catch (NumberFormatException ignored) {
|
||||
// Ignore
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
tmp = getProperty(negativeCachePolicyProp,
|
||||
negativeCachePolicyPropFallback);
|
||||
|
||||
if (tmp != null) {
|
||||
negativeCachePolicy = tmp < 0 ? FOREVER : tmp;
|
||||
propertyNegativeSet = true;
|
||||
}
|
||||
if (cachePolicy > 0) {
|
||||
tmp = getProperty(cacheStalePolicyProp,
|
||||
cacheStalePolicyPropFallback);
|
||||
if (tmp != null) {
|
||||
staleCachePolicy = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Integer getProperty(String cachePolicyProp,
|
||||
String cachePolicyPropFallback)
|
||||
{
|
||||
return java.security.AccessController.doPrivileged(
|
||||
new PrivilegedAction<Integer>() {
|
||||
public Integer run() {
|
||||
try {
|
||||
String tmpString = Security.getProperty(
|
||||
cachePolicyProp);
|
||||
if (tmpString != null) {
|
||||
return Integer.valueOf(tmpString);
|
||||
}
|
||||
} catch (NumberFormatException ignored) {
|
||||
// Ignore
|
||||
}
|
||||
|
||||
try {
|
||||
String tmpString = System.getProperty(
|
||||
cachePolicyPropFallback);
|
||||
if (tmpString != null) {
|
||||
return Integer.decode(tmpString);
|
||||
}
|
||||
} catch (NumberFormatException ignored) {
|
||||
// Ignore
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static int get() {
|
||||
return cachePolicy;
|
||||
}
|
||||
|
||||
public static int getStale() {
|
||||
return staleCachePolicy;
|
||||
}
|
||||
|
||||
public static int getNegative() {
|
||||
return negativeCachePolicy;
|
||||
}
|
||||
|
@ -357,6 +357,17 @@ ssl.TrustManagerFactory.algorithm=PKIX
|
||||
#
|
||||
#networkaddress.cache.ttl=-1
|
||||
|
||||
#
|
||||
# The Java-level namelookup cache stale policy:
|
||||
#
|
||||
# any positive value: the number of seconds to use the stale names
|
||||
# zero: do not use stale names
|
||||
# negative values are ignored
|
||||
#
|
||||
# default value is 0 (NEVER).
|
||||
#
|
||||
#networkaddress.cache.stale.ttl=0
|
||||
|
||||
# The Java-level namelookup cache policy for failed lookups:
|
||||
#
|
||||
# any negative value: cache forever
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -37,9 +37,27 @@ import impl.SimpleResolverProviderImpl;
|
||||
* @library lib providers/simple
|
||||
* @build test.library/testlib.ResolutionRegistry
|
||||
* simple.provider/impl.SimpleResolverProviderImpl AddressesCachingTest
|
||||
* @run testng/othervm -Djava.security.properties=${test.src}/NeverCache.props
|
||||
* @run testng/othervm -Djava.security.properties=${test.src}/props/NeverCache.props
|
||||
* -Dtest.cachingDisabled=true AddressesCachingTest
|
||||
* @run testng/othervm -Djava.security.properties=${test.src}/ForeverCache.props
|
||||
* @run testng/othervm -Djava.security.properties=${test.src}/props/ForeverCache.props
|
||||
* -Dtest.cachingDisabled=false AddressesCachingTest
|
||||
* @run testng/othervm
|
||||
* -Djava.security.properties=${test.src}/props/NeverCacheIgnoreMinusStale.props
|
||||
* -Dtest.cachingDisabled=true AddressesCachingTest
|
||||
* @run testng/othervm
|
||||
* -Djava.security.properties=${test.src}/props/NeverCacheIgnorePositiveStale.props
|
||||
* -Dtest.cachingDisabled=true AddressesCachingTest
|
||||
* @run testng/othervm
|
||||
* -Djava.security.properties=${test.src}/props/NeverCacheIgnoreZeroStale.props
|
||||
* -Dtest.cachingDisabled=true AddressesCachingTest
|
||||
* @run testng/othervm
|
||||
* -Djava.security.properties=${test.src}/props/ForeverCacheIgnoreMinusStale.props
|
||||
* -Dtest.cachingDisabled=false AddressesCachingTest
|
||||
* @run testng/othervm
|
||||
* -Djava.security.properties=${test.src}/props/ForeverCacheIgnorePositiveStale.props
|
||||
* -Dtest.cachingDisabled=false AddressesCachingTest
|
||||
* @run testng/othervm
|
||||
* -Djava.security.properties=${test.src}/props/ForeverCacheIgnoreZeroStale.props
|
||||
* -Dtest.cachingDisabled=false AddressesCachingTest
|
||||
*/
|
||||
public class AddressesCachingTest {
|
||||
|
@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import impl.SimpleResolverProviderImpl;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary Test that stale InetAddress caching security properties work as
|
||||
* expected when a custom resolver is installed.
|
||||
* @library lib providers/simple
|
||||
* @build test.library/testlib.ResolutionRegistry
|
||||
* simple.provider/impl.SimpleResolverProviderImpl AddressesStaleCachingTest
|
||||
* @run testng/othervm -Djava.security.properties=${test.src}/props/CacheStale.props AddressesStaleCachingTest
|
||||
*/
|
||||
public class AddressesStaleCachingTest {
|
||||
|
||||
private static class Lookup {
|
||||
private final byte[] address;
|
||||
private final long timestamp;
|
||||
|
||||
private Lookup(byte[] address, long timestamp) {
|
||||
this.address = address;
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates successful and unsuccessful lookups when the stale cache is
|
||||
* enabled.
|
||||
*/
|
||||
@Test
|
||||
public void testRefresh() throws Exception{
|
||||
// The first request is to save the data into the cache
|
||||
Lookup first = doLookup(false, 0);
|
||||
|
||||
Thread.sleep(10000); // intentionally big delay > x2 stale property
|
||||
// The refreshTime is expired, we will do the successful lookup.
|
||||
Lookup second = doLookup(false, 0);
|
||||
Assert.assertNotEquals(first.timestamp, second.timestamp,
|
||||
"Two lookups are expected");
|
||||
|
||||
Thread.sleep(10000); // intentionally big delay > x2 stale property
|
||||
// The refreshTime is expired again, we will do the failed lookup.
|
||||
Lookup third = doLookup(true, 0);
|
||||
Assert.assertNotEquals(second.timestamp, third.timestamp,
|
||||
"Two lookups are expected");
|
||||
|
||||
// The stale cache is enabled, so we should get valid/same data for
|
||||
// all requests(even for the failed request).
|
||||
Assert.assertEquals(first.address, second.address,
|
||||
"Same address is expected");
|
||||
Assert.assertEquals(second.address, third.address,
|
||||
"Same address is expected");
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that only one thread is blocked during "refresh", all others
|
||||
* will continue to use the "stale" data.
|
||||
*/
|
||||
@Test
|
||||
public void testOnlyOneThreadIsBlockedDuringRefresh() throws Exception {
|
||||
long timeout = System.nanoTime() + TimeUnit.SECONDS.toNanos(12);
|
||||
doLookup(false, timeout);
|
||||
Thread.sleep(9000);
|
||||
|
||||
CountDownLatch blockServer = new CountDownLatch(1);
|
||||
SimpleResolverProviderImpl.setBlocker(blockServer);
|
||||
|
||||
Thread ts[] = new Thread[10];
|
||||
CountDownLatch wait9 = new CountDownLatch(ts.length - 1);
|
||||
CountDownLatch wait10 = new CountDownLatch(ts.length);
|
||||
CountDownLatch start = new CountDownLatch(ts.length);
|
||||
for (int i = 0; i < ts.length; i++) {
|
||||
ts[i] = new Thread(() -> {
|
||||
start.countDown();
|
||||
try {
|
||||
start.await();
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
doLookup(true, timeout);
|
||||
wait9.countDown();
|
||||
wait10.countDown();
|
||||
});
|
||||
}
|
||||
for (Thread t : ts) {
|
||||
t.start();
|
||||
}
|
||||
if (!wait9.await(10, TimeUnit.SECONDS)) {
|
||||
blockServer.countDown();
|
||||
throw new RuntimeException("Some threads hang");
|
||||
}
|
||||
blockServer.countDown();
|
||||
if (!wait10.await(10, TimeUnit.SECONDS)) {
|
||||
throw new RuntimeException("The last thread hangs");
|
||||
}
|
||||
}
|
||||
|
||||
private static Lookup doLookup(boolean error, long timeout) {
|
||||
SimpleResolverProviderImpl.setUnreachableServer(error);
|
||||
try {
|
||||
byte[] firstAddress = InetAddress.getByName("javaTest.org").getAddress();
|
||||
long firstTimestamp = SimpleResolverProviderImpl.getLastLookupTimestamp();
|
||||
|
||||
byte[] secondAddress = InetAddress.getByName("javaTest.org").getAddress();
|
||||
long secondTimestamp = SimpleResolverProviderImpl.getLastLookupTimestamp();
|
||||
|
||||
Assert.assertEquals(firstAddress, secondAddress,
|
||||
"Same address is expected");
|
||||
if (timeout == 0 || timeout - System.nanoTime() > 0) {
|
||||
Assert.assertEquals(firstTimestamp, secondTimestamp,
|
||||
"Only one positive lookup is expected with caching enabled");
|
||||
}
|
||||
return new Lookup(firstAddress, firstTimestamp);
|
||||
} catch (UnknownHostException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
networkaddress.cache.ttl=7
|
||||
networkaddress.cache.negative.ttl=3
|
||||
networkaddress.cache.stale.ttl=30
|
3
test/jdk/java/net/spi/InetAddressResolverProvider/props/ForeverCacheIgnoreMinusStale.props
Normal file
3
test/jdk/java/net/spi/InetAddressResolverProvider/props/ForeverCacheIgnoreMinusStale.props
Normal file
@ -0,0 +1,3 @@
|
||||
networkaddress.cache.ttl=-1
|
||||
networkaddress.cache.negative.ttl=-1
|
||||
networkaddress.cache.stale.ttl=-1
|
3
test/jdk/java/net/spi/InetAddressResolverProvider/props/ForeverCacheIgnorePositiveStale.props
Normal file
3
test/jdk/java/net/spi/InetAddressResolverProvider/props/ForeverCacheIgnorePositiveStale.props
Normal file
@ -0,0 +1,3 @@
|
||||
networkaddress.cache.ttl=-1
|
||||
networkaddress.cache.negative.ttl=-1
|
||||
networkaddress.cache.stale.ttl=10000
|
3
test/jdk/java/net/spi/InetAddressResolverProvider/props/ForeverCacheIgnoreZeroStale.props
Normal file
3
test/jdk/java/net/spi/InetAddressResolverProvider/props/ForeverCacheIgnoreZeroStale.props
Normal file
@ -0,0 +1,3 @@
|
||||
networkaddress.cache.ttl=-1
|
||||
networkaddress.cache.negative.ttl=-1
|
||||
networkaddress.cache.stale.ttl=0
|
3
test/jdk/java/net/spi/InetAddressResolverProvider/props/NeverCacheIgnoreMinusStale.props
Normal file
3
test/jdk/java/net/spi/InetAddressResolverProvider/props/NeverCacheIgnoreMinusStale.props
Normal file
@ -0,0 +1,3 @@
|
||||
networkaddress.cache.ttl=0
|
||||
networkaddress.cache.negative.ttl=0
|
||||
networkaddress.cache.stale.ttl=-1
|
3
test/jdk/java/net/spi/InetAddressResolverProvider/props/NeverCacheIgnorePositiveStale.props
Normal file
3
test/jdk/java/net/spi/InetAddressResolverProvider/props/NeverCacheIgnorePositiveStale.props
Normal file
@ -0,0 +1,3 @@
|
||||
networkaddress.cache.ttl=0
|
||||
networkaddress.cache.negative.ttl=0
|
||||
networkaddress.cache.stale.ttl=10000
|
3
test/jdk/java/net/spi/InetAddressResolverProvider/props/NeverCacheIgnoreZeroStale.props
Normal file
3
test/jdk/java/net/spi/InetAddressResolverProvider/props/NeverCacheIgnoreZeroStale.props
Normal file
@ -0,0 +1,3 @@
|
||||
networkaddress.cache.ttl=0
|
||||
networkaddress.cache.negative.ttl=0
|
||||
networkaddress.cache.stale.ttl=0
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -31,6 +31,7 @@ import java.net.spi.InetAddressResolverProvider;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@ -41,6 +42,8 @@ public class SimpleResolverProviderImpl extends InetAddressResolverProvider {
|
||||
public static ResolutionRegistry registry = new ResolutionRegistry();
|
||||
private static List<LookupPolicy> LOOKUP_HISTORY = Collections.synchronizedList(new ArrayList<>());
|
||||
private static volatile long LAST_LOOKUP_TIMESTAMP;
|
||||
private static volatile boolean unreachableServer;
|
||||
private static volatile CountDownLatch blocker;
|
||||
private static Logger LOGGER = Logger.getLogger(SimpleResolverProviderImpl.class.getName());
|
||||
|
||||
@Override
|
||||
@ -51,14 +54,27 @@ public class SimpleResolverProviderImpl extends InetAddressResolverProvider {
|
||||
public Stream<InetAddress> lookupByName(String host, LookupPolicy lookupPolicy) throws UnknownHostException {
|
||||
LOGGER.info("Looking-up addresses for '" + host + "'. Lookup characteristics:" +
|
||||
Integer.toString(lookupPolicy.characteristics(), 2));
|
||||
if (blocker != null) {
|
||||
try {
|
||||
blocker.await();
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
LOOKUP_HISTORY.add(lookupPolicy);
|
||||
LAST_LOOKUP_TIMESTAMP = System.nanoTime();
|
||||
if (unreachableServer) {
|
||||
throw new UnknownHostException("unreachableServer");
|
||||
}
|
||||
return registry.lookupHost(host, lookupPolicy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String lookupByAddress(byte[] addr) throws UnknownHostException {
|
||||
LOGGER.info("Looking host name for the following address:" + ResolutionRegistry.addressBytesToString(addr));
|
||||
if (unreachableServer) {
|
||||
throw new UnknownHostException("unreachableServer");
|
||||
}
|
||||
return registry.lookupAddress(addr);
|
||||
}
|
||||
};
|
||||
@ -73,6 +89,14 @@ public class SimpleResolverProviderImpl extends InetAddressResolverProvider {
|
||||
return LAST_LOOKUP_TIMESTAMP;
|
||||
}
|
||||
|
||||
public static void setUnreachableServer(boolean unreachableServer) {
|
||||
SimpleResolverProviderImpl.unreachableServer = unreachableServer;
|
||||
}
|
||||
|
||||
public static void setBlocker(CountDownLatch blocker) {
|
||||
SimpleResolverProviderImpl.blocker = blocker;
|
||||
}
|
||||
|
||||
public static LookupPolicy lookupPolicyHistory(int position) {
|
||||
if (LOOKUP_HISTORY.isEmpty()) {
|
||||
throw new RuntimeException("No registered lookup policies");
|
||||
|
Loading…
x
Reference in New Issue
Block a user