8304818: Prune HttpURLConnection cache when corresponding Authenticator is garbage collected

Reviewed-by: dfuchs, djelinski
This commit is contained in:
Michael McMahon 2023-04-24 17:25:32 +00:00
parent 6b81342c22
commit 314db55f6d
18 changed files with 340 additions and 680 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 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
@ -25,8 +25,6 @@
package java.net;
import sun.net.www.protocol.http.AuthenticatorKeys;
/**
* The class Authenticator represents an object that knows how to obtain
* authentication for a network connection. Usually, it will do this
@ -72,7 +70,6 @@ class Authenticator {
private String requestingScheme;
private URL requestingURL;
private RequestorType requestingAuthType;
private final String key = AuthenticatorKeys.computeKey(this);
/**
* Constructor for subclasses to call.
@ -576,11 +573,4 @@ class Authenticator {
protected RequestorType getRequestorType () {
return requestingAuthType;
}
static String getKey(Authenticator a) {
return a.key;
}
static {
AuthenticatorKeys.setAuthenticatorKeyAccess(Authenticator::getKey);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1994, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1994, 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
@ -38,7 +38,7 @@ import sun.net.www.MessageHeader;
import sun.net.www.HeaderParser;
import sun.net.www.MeteredStream;
import sun.net.www.ParseUtil;
import sun.net.www.protocol.http.AuthenticatorKeys;
import sun.net.www.protocol.http.AuthCacheImpl;
import sun.net.www.protocol.http.HttpURLConnection;
import sun.util.logging.PlatformLogger;
import static sun.net.www.protocol.http.HttpURLConnection.TunnelState.*;
@ -116,6 +116,8 @@ public class HttpClient extends NetworkClient {
The default value is 'true'. */
private static final boolean cacheSPNEGOProp;
protected volatile AuthCacheImpl authcache;
volatile boolean keepingAlive; /* this is a keep-alive connection */
volatile boolean disableKeepAlive;/* keep-alive has been disabled for this
connection - this will be used when
@ -167,8 +169,6 @@ public class HttpClient extends NetworkClient {
}
}
protected volatile String authenticatorKey;
/**
* A NOP method kept for backwards binary compatibility
* @deprecated -- system properties are no longer cached.
@ -349,10 +349,11 @@ public class HttpClient extends NetworkClient {
}
}
if (ret != null) {
String ak = httpuc == null ? AuthenticatorKeys.DEFAULT
: httpuc.getAuthenticatorKey();
AuthCacheImpl ak = httpuc == null
? AuthCacheImpl.getDefault()
: httpuc.getAuthCache();
boolean compatible = Objects.equals(ret.proxy, p)
&& Objects.equals(ret.getAuthenticatorKey(), ak);
&& Objects.equals(ret.getAuthCache(), ak);
if (compatible) {
ret.lock();
try {
@ -384,7 +385,7 @@ public class HttpClient extends NetworkClient {
if (ret == null) {
ret = new HttpClient(url, p, to);
if (httpuc != null) {
ret.authenticatorKey = httpuc.getAuthenticatorKey();
ret.authcache = httpuc.getAuthCache();
}
} else {
@SuppressWarnings("removal")
@ -422,10 +423,8 @@ public class HttpClient extends NetworkClient {
to, useCache, httpuc);
}
public final String getAuthenticatorKey() {
String k = authenticatorKey;
if (k == null) return AuthenticatorKeys.DEFAULT;
return k;
public final AuthCacheImpl getAuthCache() {
return authcache == null ? AuthCacheImpl.getDefault() : authcache;
}
/* return it to the cache as still usable, if:

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 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
@ -38,8 +38,7 @@ public interface AuthCache {
/**
* Put an entry in the cache. pkey is a string specified as follows:
*
* A:[B:]C:D:E[:F][;key=value] Between 4 and 6 fields separated by ":",
* and an optional semicolon-separated key=value list postfix,
* A:[B:]C:D:E[:F] Between 4 and 6 fields separated by ":",
* where the fields have the following meaning:
* A is "s" or "p" for server or proxy authentication respectively
* B is optional and is the {@link AuthScheme}, e.g. BASIC, DIGEST, NTLM, etc
@ -48,11 +47,6 @@ public interface AuthCache {
* E is the port number
* F is optional and if present is the realm
*
* The semi-colon separated key=value list postfix can be used to
* provide additional contextual information, thus allowing
* to separate AuthCacheValue instances obtained from different
* contexts.
*
* Generally, two entries are created for each AuthCacheValue,
* one including the realm and one without the realm.
* Also, for some schemes (digest) multiple entries may be created

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 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
@ -25,9 +25,13 @@
package sun.net.www.protocol.http;
import java.net.Authenticator;
import java.util.Collections;
import java.util.LinkedList;
import java.util.ListIterator;
import java.util.HashMap;
import java.util.Map;
import java.util.WeakHashMap;
/**
* @author Michael McMahon
@ -105,4 +109,23 @@ public class AuthCacheImpl implements AuthCache {
}
}
}
private static final Map<Authenticator,AuthCacheImpl> caches =
Collections.synchronizedMap(new WeakHashMap<>());
/**
* The default cache is stored under null key which is never garbage
* collected.
*/
public static AuthCacheImpl getDefault() {
return getAuthCacheFor(null);
}
/**
* Atomically check if a cache exists for given Authenticator and return it
* or create one and return it
*/
public static AuthCacheImpl getAuthCacheFor(Authenticator auth) {
return caches.computeIfAbsent(auth, (k) -> new AuthCacheImpl());
}
}

View File

@ -25,7 +25,6 @@
package sun.net.www.protocol.http;
import java.io.Serializable;
import java.net.PasswordAuthentication;
/**
@ -35,25 +34,13 @@ import java.net.PasswordAuthentication;
* @author Michael McMahon
*/
public abstract class AuthCacheValue implements Serializable {
@java.io.Serial
static final long serialVersionUID = 735249334068211611L;
public abstract class AuthCacheValue {
public enum Type {
Proxy,
Server
};
/**
* Caches authentication info entered by user. See cacheKey()
*/
protected static AuthCache cache = new AuthCacheImpl();
public static void setAuthCache (AuthCache map) {
cache = map;
}
/* Package private ctor to prevent extension outside package */
AuthCacheValue() {}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1995, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1995, 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
@ -33,7 +33,7 @@ import java.util.HashMap;
import java.util.Objects;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Function;
import java.util.function.BiFunction;
import sun.net.www.HeaderParser;
@ -55,9 +55,6 @@ import sun.net.www.HeaderParser;
public abstract class AuthenticationInfo extends AuthCacheValue implements Cloneable {
@java.io.Serial
static final long serialVersionUID = -2588378268010453259L;
// Constants saying what kind of authorization this is. This determines
// the namespace in the hash table lookup.
public static final char SERVER_AUTHENTICATION = 's';
@ -76,7 +73,7 @@ public abstract class AuthenticationInfo extends AuthCacheValue implements Clone
/* AuthCacheValue: */
protected transient PasswordAuthentication pw;
protected PasswordAuthentication pw;
public PasswordAuthentication credentials() {
return pw;
@ -135,8 +132,10 @@ public abstract class AuthenticationInfo extends AuthCacheValue implements Clone
* and returns the cached authentication value.
* Otherwise, returns the cached authentication value, which may be null.
*/
private static AuthenticationInfo requestAuthentication(String key, Function<String, AuthenticationInfo> cache) {
AuthenticationInfo cached = cache.apply(key);
private static AuthenticationInfo requestAuthentication(
String key, AuthCacheImpl acache, BiFunction<String, AuthCacheImpl, AuthenticationInfo> cachefunc)
{
AuthenticationInfo cached = cachefunc.apply(key, acache);
if (cached != null || !serializeAuth) {
// either we already have a value in the cache, and we can
// use that immediately, or the serializeAuth behavior is disabled,
@ -147,7 +146,7 @@ public abstract class AuthenticationInfo extends AuthCacheValue implements Clone
try {
// check again after locking, and if available
// just return the cached value.
cached = cache.apply(key);
cached = cachefunc.apply(key, acache);
if (cached != null) return cached;
// Otherwise, if no request is in progress, record this
@ -166,7 +165,7 @@ public abstract class AuthenticationInfo extends AuthCacheValue implements Clone
requestLock.unlock();
}
/* entry may be in cache now. */
return cache.apply(key);
return cachefunc.apply(key, acache);
}
/* signal completion of an authentication (whether it succeeded or not)
@ -186,10 +185,6 @@ public abstract class AuthenticationInfo extends AuthCacheValue implements Clone
}
}
//public String toString () {
//return ("{"+type+":"+authScheme+":"+protocol+":"+host+":"+port+":"+realm+":"+path+"}");
//}
// REMIND: This cache just grows forever. We should put in a bounded
// cache, or maybe something using WeakRef's.
@ -218,18 +213,9 @@ public abstract class AuthenticationInfo extends AuthCacheValue implements Clone
/** The shortest path from the URL we authenticated against. */
String path;
/**
* A key identifying the authenticator from which the credentials
* were obtained.
* {@link AuthenticatorKeys#DEFAULT} identifies the {@linkplain
* java.net.Authenticator#setDefault(java.net.Authenticator) default}
* authenticator.
*/
String authenticatorKey;
/** Use this constructor only for proxy entries */
public AuthenticationInfo(char type, AuthScheme authScheme, String host,
int port, String realm, String authenticatorKey) {
int port, String realm) {
this.type = type;
this.authScheme = authScheme;
this.protocol = "";
@ -237,7 +223,6 @@ public abstract class AuthenticationInfo extends AuthCacheValue implements Clone
this.port = port;
this.realm = realm;
this.path = null;
this.authenticatorKey = Objects.requireNonNull(authenticatorKey);
}
public Object clone() {
@ -253,8 +238,7 @@ public abstract class AuthenticationInfo extends AuthCacheValue implements Clone
* Constructor used to limit the authorization to the path within
* the URL. Use this constructor for origin server entries.
*/
public AuthenticationInfo(char type, AuthScheme authScheme, URL url, String realm,
String authenticatorKey) {
public AuthenticationInfo(char type, AuthScheme authScheme, URL url, String realm) {
this.type = type;
this.authScheme = authScheme;
this.protocol = url.getProtocol().toLowerCase();
@ -271,16 +255,6 @@ public abstract class AuthenticationInfo extends AuthCacheValue implements Clone
else {
this.path = reducePath (urlPath);
}
this.authenticatorKey = Objects.requireNonNull(authenticatorKey);
}
/**
* The {@linkplain java.net.Authenticator#getKey(java.net.Authenticator) key}
* of the authenticator that was used to obtain the credentials.
* @return The authenticator's key.
*/
public final String getAuthenticatorKey() {
return authenticatorKey;
}
/*
@ -305,15 +279,14 @@ public abstract class AuthenticationInfo extends AuthCacheValue implements Clone
* don't yet know the realm
* (i.e. when we're preemptively setting the auth).
*/
static AuthenticationInfo getServerAuth(URL url, String authenticatorKey) {
static AuthenticationInfo getServerAuth(URL url, AuthCacheImpl cache) {
int port = url.getPort();
if (port == -1) {
port = url.getDefaultPort();
}
String key = SERVER_AUTHENTICATION + ":" + url.getProtocol().toLowerCase()
+ ":" + url.getHost().toLowerCase() + ":" + port
+ ";auth=" + authenticatorKey;
return getAuth(key, url);
+ ":" + url.getHost().toLowerCase() + ":" + port;
return getAuth(key, url, cache);
}
/**
@ -322,8 +295,7 @@ public abstract class AuthenticationInfo extends AuthCacheValue implements Clone
* In this case we do not use the path because the protection space
* is identified by the host:port:realm only
*/
static String getServerAuthKey(URL url, String realm, AuthScheme scheme,
String authenticatorKey) {
static String getServerAuthKey(URL url, String realm, AuthScheme scheme) {
int port = url.getPort();
if (port == -1) {
port = url.getDefaultPort();
@ -331,30 +303,29 @@ public abstract class AuthenticationInfo extends AuthCacheValue implements Clone
String key = SERVER_AUTHENTICATION + ":" + scheme + ":"
+ url.getProtocol().toLowerCase()
+ ":" + url.getHost().toLowerCase()
+ ":" + port + ":" + realm
+ ";auth=" + authenticatorKey;
+ ":" + port + ":" + realm;
return key;
}
private static AuthenticationInfo getCachedServerAuth(String key) {
return getAuth(key, null);
private static AuthenticationInfo getCachedServerAuth(String key, AuthCacheImpl cache) {
return getAuth(key, null, cache);
}
static AuthenticationInfo getServerAuth(String key) {
if (!serializeAuth) return getCachedServerAuth(key);
return requestAuthentication(key, AuthenticationInfo::getCachedServerAuth);
static AuthenticationInfo getServerAuth(String key, AuthCacheImpl cache) {
if (!serializeAuth) return getCachedServerAuth(key, cache);
return requestAuthentication(key, cache, AuthenticationInfo::getCachedServerAuth);
}
/**
* Return the AuthenticationInfo object from the cache if it's path is
* a substring of the supplied URLs path.
*/
static AuthenticationInfo getAuth(String key, URL url) {
static AuthenticationInfo getAuth(String key, URL url, AuthCacheImpl acache) {
Objects.requireNonNull(acache);
if (url == null) {
return (AuthenticationInfo)cache.get (key, null);
return (AuthenticationInfo)acache.get (key, null);
} else {
return (AuthenticationInfo)cache.get (key, url.getPath());
return (AuthenticationInfo)acache.get (key, url.getPath());
}
}
@ -363,11 +334,10 @@ public abstract class AuthenticationInfo extends AuthCacheValue implements Clone
* for preemptive header-setting. Note, the protocol field is always
* blank for proxies.
*/
static AuthenticationInfo getProxyAuth(String host, int port,
String authenticatorKey) {
String key = PROXY_AUTHENTICATION + "::" + host.toLowerCase() + ":" + port
+ ";auth=" + authenticatorKey;
AuthenticationInfo result = (AuthenticationInfo) cache.get(key, null);
static AuthenticationInfo getProxyAuth(String host, int port, AuthCacheImpl acache) {
Objects.requireNonNull(acache);
String key = PROXY_AUTHENTICATION + "::" + host.toLowerCase() + ":" + port;
AuthenticationInfo result = (AuthenticationInfo) acache.get(key, null);
return result;
}
@ -376,34 +346,34 @@ public abstract class AuthenticationInfo extends AuthCacheValue implements Clone
* Used in response to a challenge. Note, the protocol field is always
* blank for proxies.
*/
static String getProxyAuthKey(String host, int port, String realm,
AuthScheme scheme, String authenticatorKey) {
static String getProxyAuthKey(String host, int port, String realm, AuthScheme scheme) {
String key = PROXY_AUTHENTICATION + ":" + scheme
+ "::" + host.toLowerCase()
+ ":" + port + ":" + realm
+ ";auth=" + authenticatorKey;
+ ":" + port + ":" + realm;
return key;
}
private static AuthenticationInfo getCachedProxyAuth(String key) {
return (AuthenticationInfo) cache.get(key, null);
private static AuthenticationInfo getCachedProxyAuth(String key, AuthCacheImpl acache) {
Objects.requireNonNull(acache);
return (AuthenticationInfo) acache.get(key, null);
}
static AuthenticationInfo getProxyAuth(String key) {
if (!serializeAuth) return getCachedProxyAuth(key);
return requestAuthentication(key, AuthenticationInfo::getCachedProxyAuth);
static AuthenticationInfo getProxyAuth(String key, AuthCacheImpl acache) {
if (!serializeAuth) return getCachedProxyAuth(key, acache);
return requestAuthentication(key, acache, AuthenticationInfo::getCachedProxyAuth);
}
/**
* Add this authentication to the cache
*/
void addToCache() {
void addToCache(AuthCacheImpl authcache) {
Objects.requireNonNull(authcache);
String key = cacheKey(true);
if (useAuthCache()) {
cache.put(key, this);
authcache.put(key, this);
if (supportsPreemptiveAuthorization()) {
cache.put(cacheKey(false), this);
authcache.put(cacheKey(false), this);
}
}
endAuthRequest(key);
@ -419,10 +389,11 @@ public abstract class AuthenticationInfo extends AuthCacheValue implements Clone
/**
* Remove this authentication from the cache
*/
void removeFromCache() {
cache.remove(cacheKey(true), this);
void removeFromCache(AuthCacheImpl authcache) {
Objects.requireNonNull(authcache);
authcache.remove(cacheKey(true), this);
if (supportsPreemptiveAuthorization()) {
cache.remove(cacheKey(false), this);
authcache.remove(cacheKey(false), this);
}
}
@ -483,43 +454,14 @@ public abstract class AuthenticationInfo extends AuthCacheValue implements Clone
String cacheKey(boolean includeRealm) {
// This must be kept in sync with the getXXXAuth() methods in this
// class.
String authenticatorKey = getAuthenticatorKey();
if (includeRealm) {
return type + ":" + authScheme + ":" + protocol + ":"
+ host + ":" + port + ":" + realm
+ ";auth=" + authenticatorKey;
+ host + ":" + port + ":" + realm;
} else {
return type + ":" + protocol + ":" + host + ":" + port
+ ";auth=" + authenticatorKey;
return type + ":" + protocol + ":" + host + ":" + port;
}
}
String s1, s2; /* used for serialization of pw */
@java.io.Serial
// should be safe to keep synchronized here
private synchronized void readObject(ObjectInputStream s)
throws IOException, ClassNotFoundException
{
s.defaultReadObject ();
pw = new PasswordAuthentication (s1, s2.toCharArray());
s1 = null; s2= null;
if (authenticatorKey == null) {
authenticatorKey = AuthenticatorKeys.DEFAULT;
}
}
@java.io.Serial
// should be safe to keep synchronized here
private synchronized void writeObject(java.io.ObjectOutputStream s)
throws IOException
{
Objects.requireNonNull(authenticatorKey);
s1 = pw.getUserName();
s2 = new String (pw.getPassword());
s.defaultWriteObject ();
}
/**
* Releases any system or cryptographic resources.
* It is up to implementors to override disposeContext()

View File

@ -1,76 +0,0 @@
/*
* Copyright (c) 2016, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*/
package sun.net.www.protocol.http;
import java.net.Authenticator;
import java.util.concurrent.atomic.AtomicLong;
/**
* A class used to tie a key to an authenticator instance.
*/
public final class AuthenticatorKeys {
private AuthenticatorKeys() {
throw new InternalError("Trying to instantiate static class");
}
public static final String DEFAULT = "default";
private static final AtomicLong IDS = new AtomicLong();
public static String computeKey(Authenticator a) {
return System.identityHashCode(a) + "-" + IDS.incrementAndGet()
+ "@" + a.getClass().getName();
}
/**
* Returns a key for the given authenticator.
*
* @param authenticator The authenticator; {@code null} should be
* passed when the {@linkplain
* Authenticator#setDefault(java.net.Authenticator) default}
* authenticator is meant.
* @return A key for the given authenticator, {@link #DEFAULT} for
* {@code null}.
*/
public static String getKey(Authenticator authenticator) {
if (authenticator == null) {
return DEFAULT;
}
return authenticatorKeyAccess.getKey(authenticator);
}
@FunctionalInterface
public interface AuthenticatorKeyAccess {
public String getKey(Authenticator a);
}
private static AuthenticatorKeyAccess authenticatorKeyAccess;
public static void setAuthenticatorKeyAccess(AuthenticatorKeyAccess access) {
if (authenticatorKeyAccess == null && access != null) {
authenticatorKeyAccess = access;
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 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
@ -63,10 +63,9 @@ class BasicAuthentication extends AuthenticationInfo {
*/
public BasicAuthentication(boolean isProxy, String host, int port,
String realm, PasswordAuthentication pw,
boolean isUTF8, String authenticatorKey) {
boolean isUTF8) {
super(isProxy ? PROXY_AUTHENTICATION : SERVER_AUTHENTICATION,
AuthScheme.BASIC, host, port, realm,
Objects.requireNonNull(authenticatorKey));
AuthScheme.BASIC, host, port, realm);
this.auth = authValueFrom(pw, isUTF8);
this.pw = pw;
}
@ -75,11 +74,9 @@ class BasicAuthentication extends AuthenticationInfo {
* Create a BasicAuthentication
*/
public BasicAuthentication(boolean isProxy, String host, int port,
String realm, String auth,
String authenticatorKey) {
String realm, String auth) {
super(isProxy ? PROXY_AUTHENTICATION : SERVER_AUTHENTICATION,
AuthScheme.BASIC, host, port, realm,
Objects.requireNonNull(authenticatorKey));
AuthScheme.BASIC, host, port, realm);
this.auth = "Basic " + auth;
}
@ -87,11 +84,9 @@ class BasicAuthentication extends AuthenticationInfo {
* Create a BasicAuthentication
*/
public BasicAuthentication(boolean isProxy, URL url, String realm,
PasswordAuthentication pw, boolean isUTF8,
String authenticatorKey) {
PasswordAuthentication pw, boolean isUTF8) {
super(isProxy ? PROXY_AUTHENTICATION : SERVER_AUTHENTICATION,
AuthScheme.BASIC, url, realm,
Objects.requireNonNull(authenticatorKey));
AuthScheme.BASIC, url, realm);
this.auth = authValueFrom(pw, isUTF8);
this.pw = pw;
}
@ -116,10 +111,9 @@ class BasicAuthentication extends AuthenticationInfo {
* Create a BasicAuthentication
*/
public BasicAuthentication(boolean isProxy, URL url, String realm,
String auth, String authenticatorKey) {
String auth) {
super(isProxy ? PROXY_AUTHENTICATION : SERVER_AUTHENTICATION,
AuthScheme.BASIC, url, realm,
Objects.requireNonNull(authenticatorKey));
AuthScheme.BASIC, url, realm);
this.auth = "Basic " + auth;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 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
@ -296,12 +296,11 @@ class DigestAuthentication extends AuthenticationInfo {
*/
public DigestAuthentication(boolean isProxy, URL url, String realm,
String authMethod, PasswordAuthentication pw,
Parameters params, String authenticatorKey) {
Parameters params){
super(isProxy ? PROXY_AUTHENTICATION : SERVER_AUTHENTICATION,
AuthScheme.DIGEST,
url,
realm,
Objects.requireNonNull(authenticatorKey));
realm);
this.authMethod = authMethod;
this.pw = pw;
this.params = params;
@ -309,13 +308,12 @@ class DigestAuthentication extends AuthenticationInfo {
public DigestAuthentication(boolean isProxy, String host, int port, String realm,
String authMethod, PasswordAuthentication pw,
Parameters params, String authenticatorKey) {
Parameters params) {
super(isProxy ? PROXY_AUTHENTICATION : SERVER_AUTHENTICATION,
AuthScheme.DIGEST,
host,
port,
realm,
Objects.requireNonNull(authenticatorKey));
realm);
this.authMethod = authMethod;
this.pw = pw;
this.params = params;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1995, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1995, 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
@ -310,7 +310,7 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
protected Handler handler;
protected Proxy instProxy;
protected volatile Authenticator authenticator;
protected volatile String authenticatorKey;
protected volatile AuthCacheImpl authCache = AuthCacheImpl.getDefault();
private CookieHandler cookieHandler;
private final ResponseCache cacheHandler;
@ -447,7 +447,6 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
return connectionLock.isHeldByCurrentThread();
}
/*
* privileged request password authentication
*
@ -539,16 +538,14 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
"Authenticator must be set before connecting");
}
authenticator = Objects.requireNonNull(auth);
authenticatorKey = AuthenticatorKeys.getKey(authenticator);
authCache = AuthCacheImpl.getAuthCacheFor(authenticator);
} finally {
unlock();
}
}
public String getAuthenticatorKey() {
String k = authenticatorKey;
if (k == null) return AuthenticatorKeys.getKey(authenticator);
return k;
public AuthCacheImpl getAuthCache() {
return authCache;
}
/*
@ -684,8 +681,7 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
requests.setIfNotSet("If-Modified-Since", fo.format(date));
}
// check for preemptive authorization
AuthenticationInfo sauth = AuthenticationInfo.getServerAuth(url,
getAuthenticatorKey());
AuthenticationInfo sauth = AuthenticationInfo.getServerAuth(url, authCache);
if (sauth != null && sauth.supportsPreemptiveAuthorization() ) {
// Sets "Authorization"
requests.setIfNotSet(sauth.getHeaderName(), sauth.getHeaderValue(url,method));
@ -1769,7 +1765,7 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
// cache proxy authentication info
if (proxyAuthentication != null) {
// cache auth info on success, domain header not relevant.
proxyAuthentication.addToCache();
proxyAuthentication.addToCache(authCache);
}
if (respCode == HTTP_UNAUTHORIZED) {
@ -1817,7 +1813,7 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
setCookieHeader();
continue;
} else {
serverAuthentication.removeFromCache();
serverAuthentication.removeFromCache(authCache);
}
}
serverAuthentication = getServerAuthentication(srvHdr);
@ -1858,11 +1854,11 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
// remove the entry and create a new one
BasicAuthentication a =
(BasicAuthentication) serverAuthentication.clone();
serverAuthentication.removeFromCache();
serverAuthentication.removeFromCache(authCache);
a.path = npath;
serverAuthentication = a;
}
serverAuthentication.addToCache();
serverAuthentication.addToCache(authCache);
} else {
// what we cache is based on the domain list in the request
DigestAuthentication srv = (DigestAuthentication)
@ -1878,8 +1874,8 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
URL u = newURL (url, path);
DigestAuthentication d = new DigestAuthentication (
false, u, realm, "Digest", pw,
digestparams, srv.authenticatorKey);
d.addToCache ();
digestparams);
d.addToCache (authCache);
} catch (Exception e) {}
}
}
@ -2090,7 +2086,7 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
currentProxyCredentials = proxyAuthentication;
return proxyAuthentication;
} else {
proxyAuthentication.removeFromCache();
proxyAuthentication.removeFromCache(authCache);
}
}
proxyAuthentication = getHttpProxyAuthentication(auth);
@ -2231,7 +2227,7 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
// cache proxy authentication info
if (proxyAuthentication != null) {
// cache auth info on success, domain header not relevant.
proxyAuthentication.addToCache();
proxyAuthentication.addToCache(authCache);
}
if (respCode == HTTP_OK) {
@ -2333,7 +2329,7 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
AuthenticationInfo pauth
= AuthenticationInfo.getProxyAuth(http.getProxyHostUsed(),
http.getProxyPortUsed(),
getAuthenticatorKey());
authCache);
if (pauth != null && pauth.supportsPreemptiveAuthorization()) {
String value;
if (pauth instanceof DigestAuthentication) {
@ -2393,9 +2389,8 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
if (realm == null)
realm = "";
proxyAuthKey = AuthenticationInfo.getProxyAuthKey(host, port, realm,
authScheme, getAuthenticatorKey());
ret = AuthenticationInfo.getProxyAuth(proxyAuthKey);
proxyAuthKey = AuthenticationInfo.getProxyAuthKey(host, port, realm, authScheme);
ret = AuthenticationInfo.getProxyAuth(proxyAuthKey, authCache);
if (ret == null) {
switch (authScheme) {
case BASIC:
@ -2418,8 +2413,7 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
host, addr, port, "http",
realm, scheme, url, RequestorType.PROXY);
if (a != null) {
ret = new BasicAuthentication(true, host, port, realm, a,
isUTF8, getAuthenticatorKey());
ret = new BasicAuthentication(true, host, port, realm, a, isUTF8);
}
break;
case DIGEST:
@ -2431,8 +2425,7 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
DigestAuthentication.Parameters params =
new DigestAuthentication.Parameters();
ret = new DigestAuthentication(true, host, port, realm,
scheme, a, params,
getAuthenticatorKey());
scheme, a, params);
}
break;
case NTLM:
@ -2471,8 +2464,7 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
*/
if (tryTransparentNTLMProxy ||
(!tryTransparentNTLMProxy && a != null)) {
ret = NTLMAuthenticationProxy.proxy.create(true, host,
port, a, getAuthenticatorKey());
ret = NTLMAuthenticationProxy.proxy.create(true, host, port, a);
}
/* set to false so that we do not try again */
@ -2504,8 +2496,7 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
URL u = new URL("http", host, port, "/");
String a = defaultAuth.authString(u, scheme, realm);
if (a != null) {
ret = new BasicAuthentication (true, host, port, realm, a,
getAuthenticatorKey());
ret = new BasicAuthentication (true, host, port, realm, a);
// not in cache by default - cache on success
}
} catch (java.net.MalformedURLException ignored) {
@ -2566,9 +2557,8 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
domain = p.findValue ("domain");
if (realm == null)
realm = "";
serverAuthKey = AuthenticationInfo.getServerAuthKey(url, realm, authScheme,
getAuthenticatorKey());
ret = AuthenticationInfo.getServerAuth(serverAuthKey);
serverAuthKey = AuthenticationInfo.getServerAuthKey(url, realm, authScheme);
ret = AuthenticationInfo.getServerAuth(serverAuthKey, authCache);
InetAddress addr = null;
if (ret == null) {
try {
@ -2597,8 +2587,7 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
url.getHost(), addr, port, url.getProtocol(),
realm, scheme, url, RequestorType.SERVER);
if (a != null) {
ret = new BasicAuthentication(false, url, realm, a,
isUTF8, getAuthenticatorKey());
ret = new BasicAuthentication(false, url, realm, a, isUTF8);
}
break;
case DIGEST:
@ -2609,8 +2598,7 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
if (a != null) {
digestparams = new DigestAuthentication.Parameters();
ret = new DigestAuthentication(false, url, realm, scheme,
a, digestparams,
getAuthenticatorKey());
a, digestparams);
}
break;
case NTLM:
@ -2655,8 +2643,7 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
*/
if (tryTransparentNTLMServer ||
(!tryTransparentNTLMServer && a != null)) {
ret = NTLMAuthenticationProxy.proxy.create(false,
url1, a, getAuthenticatorKey());
ret = NTLMAuthenticationProxy.proxy.create(false, url1, a);
}
/* set to false so that we do not try again */
@ -2680,8 +2667,7 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
&& defaultAuth.schemeSupported(scheme)) {
String a = defaultAuth.authString(url, scheme, realm);
if (a != null) {
ret = new BasicAuthentication (false, url, realm, a,
getAuthenticatorKey());
ret = new BasicAuthentication (false, url, realm, a);
// not in cache by default - cache on success
}
}
@ -2932,7 +2918,7 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
// check for preemptive authorization
AuthenticationInfo sauth =
AuthenticationInfo.getServerAuth(url, getAuthenticatorKey());
AuthenticationInfo.getServerAuth(url, authCache);
if (sauth != null && sauth.supportsPreemptiveAuthorization() ) {
// Sets "Authorization"
requests.setIfNotSet(sauth.getHeaderName(), sauth.getHeaderValue(url,method));

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2009, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2009, 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
@ -45,22 +45,21 @@ class NTLMAuthenticationProxy {
static final boolean supported = proxy != null ? true : false;
static final boolean supportsTransparentAuth = supported ? supportsTransparentAuth() : false;
private final Constructor<? extends AuthenticationInfo> threeArgCtr;
private final Constructor<? extends AuthenticationInfo> fourArgCtr;
private final Constructor<? extends AuthenticationInfo> sixArgCtr;
private NTLMAuthenticationProxy(Constructor<? extends AuthenticationInfo> fourArgCtr,
Constructor<? extends AuthenticationInfo> sixArgCtr) {
private NTLMAuthenticationProxy(Constructor<? extends AuthenticationInfo> threeArgCtr,
Constructor<? extends AuthenticationInfo> fourArgCtr) {
this.threeArgCtr = threeArgCtr;
this.fourArgCtr = fourArgCtr;
this.sixArgCtr = sixArgCtr;
}
AuthenticationInfo create(boolean isProxy,
URL url,
PasswordAuthentication pw,
String authenticatorKey) {
PasswordAuthentication pw) {
try {
return fourArgCtr.newInstance(isProxy, url, pw, authenticatorKey);
return threeArgCtr.newInstance(isProxy, url, pw);
} catch (ReflectiveOperationException roe) {
finest(roe);
}
@ -71,10 +70,9 @@ class NTLMAuthenticationProxy {
AuthenticationInfo create(boolean isProxy,
String host,
int port,
PasswordAuthentication pw,
String authenticatorKey) {
PasswordAuthentication pw) {
try {
return sixArgCtr.newInstance(isProxy, host, port, pw, authenticatorKey);
return fourArgCtr.newInstance(isProxy, host, port, pw);
} catch (ReflectiveOperationException roe) {
finest(roe);
}
@ -117,23 +115,21 @@ class NTLMAuthenticationProxy {
@SuppressWarnings("unchecked")
private static NTLMAuthenticationProxy tryLoadNTLMAuthentication() {
Class<? extends AuthenticationInfo> cl;
Constructor<? extends AuthenticationInfo> fourArg, sixArg;
Constructor<? extends AuthenticationInfo> threeArg, fourArg;
try {
cl = (Class<? extends AuthenticationInfo>)Class.forName(clazzStr, true, null);
if (cl != null) {
fourArg = cl.getConstructor(boolean.class,
threeArg = cl.getConstructor(boolean.class,
URL.class,
PasswordAuthentication.class,
String.class);
sixArg = cl.getConstructor(boolean.class,
PasswordAuthentication.class);
fourArg = cl.getConstructor(boolean.class,
String.class,
int.class,
PasswordAuthentication.class,
String.class);
PasswordAuthentication.class);
supportsTA = cl.getDeclaredMethod(supportsTAStr);
isTrustedSite = cl.getDeclaredMethod(isTrustedSiteStr, java.net.URL.class);
return new NTLMAuthenticationProxy(fourArg,
sixArg);
return new NTLMAuthenticationProxy(threeArg,
fourArg);
}
} catch (ClassNotFoundException cnfe) {
finest(cnfe);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2005, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 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
@ -80,9 +80,7 @@ class NegotiateAuthentication extends AuthenticationInfo {
public NegotiateAuthentication(HttpCallerInfo hci) {
super(RequestorType.PROXY==hci.authType ? PROXY_AUTHENTICATION : SERVER_AUTHENTICATION,
hci.scheme.equalsIgnoreCase("Negotiate") ? NEGOTIATE : KERBEROS,
hci.url,
"",
AuthenticatorKeys.getKey(hci.authenticator));
hci.url, "");
this.hci = hci;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2001, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2001, 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
@ -46,7 +46,7 @@ import java.util.StringTokenizer;
import javax.net.ssl.*;
import sun.net.www.http.HttpClient;
import sun.net.www.protocol.http.AuthenticatorKeys;
import sun.net.www.protocol.http.AuthCacheImpl;
import sun.net.www.protocol.http.HttpURLConnection;
import sun.security.action.*;
@ -335,11 +335,10 @@ final class HttpsClient extends HttpClient
}
if (ret != null) {
String ak = httpuc == null ? AuthenticatorKeys.DEFAULT
: httpuc.getAuthenticatorKey();
AuthCacheImpl ak = httpuc == null ? null : httpuc.getAuthCache();
boolean compatible = ((ret.proxy != null && ret.proxy.equals(p)) ||
(ret.proxy == null && p == Proxy.NO_PROXY))
&& Objects.equals(ret.getAuthenticatorKey(), ak);
&& Objects.equals(ret.getAuthCache(), ak);
if (compatible) {
ret.lock();
@ -377,7 +376,7 @@ final class HttpsClient extends HttpClient
if (ret == null) {
ret = new HttpsClient(sf, url, p, connectTimeout);
if (httpuc != null) {
ret.authenticatorKey = httpuc.getAuthenticatorKey();
ret.authcache = httpuc.getAuthCache();
}
} else {
@SuppressWarnings("removal")

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2005, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 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
@ -131,13 +131,10 @@ public class NTLMAuthentication extends AuthenticationInfo {
* If this notation is not used, then the domain will be taken
* from a system property: "http.auth.ntlm.domain".
*/
public NTLMAuthentication(boolean isProxy, URL url, PasswordAuthentication pw,
String authenticatorKey) {
public NTLMAuthentication(boolean isProxy, URL url, PasswordAuthentication pw) {
super(isProxy ? PROXY_AUTHENTICATION : SERVER_AUTHENTICATION,
AuthScheme.NTLM,
url,
"",
Objects.requireNonNull(authenticatorKey));
url, "");
init (pw);
}
@ -174,14 +171,12 @@ public class NTLMAuthentication extends AuthenticationInfo {
* Constructor used for proxy entries
*/
public NTLMAuthentication(boolean isProxy, String host, int port,
PasswordAuthentication pw,
String authenticatorKey) {
PasswordAuthentication pw) {
super(isProxy ? PROXY_AUTHENTICATION : SERVER_AUTHENTICATION,
AuthScheme.NTLM,
host,
port,
"",
Objects.requireNonNull(authenticatorKey));
"");
init (pw);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2002, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2002, 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
@ -119,13 +119,11 @@ public class NTLMAuthentication extends AuthenticationInfo {
* If this notation is not used, then the domain will be taken
* from a system property: "http.auth.ntlm.domain".
*/
public NTLMAuthentication(boolean isProxy, URL url, PasswordAuthentication pw,
String authenticatorKey) {
public NTLMAuthentication(boolean isProxy, URL url, PasswordAuthentication pw) {
super(isProxy ? PROXY_AUTHENTICATION : SERVER_AUTHENTICATION,
AuthScheme.NTLM,
url,
"",
Objects.requireNonNull(authenticatorKey));
"");
init (pw);
}
@ -155,14 +153,12 @@ public class NTLMAuthentication extends AuthenticationInfo {
* Constructor used for proxy entries
*/
public NTLMAuthentication(boolean isProxy, String host, int port,
PasswordAuthentication pw,
String authenticatorKey) {
PasswordAuthentication pw) {
super(isProxy?PROXY_AUTHENTICATION:SERVER_AUTHENTICATION,
AuthScheme.NTLM,
host,
port,
"",
Objects.requireNonNull(authenticatorKey));
"");
init (pw);
}

View File

@ -1,337 +0,0 @@
/*
* Copyright (c) 2003, 2021, 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.
*/
// Note: this test saves a cache.ser file in the scratch directory,
// which the cache implementation will load its configuration
// from. Therefore adding several @run lines does not work.
/*
* @test
* @bug 4933582
* @key intermittent
* @library /test/lib
* @modules java.base/sun.net.www
* java.base/sun.net.www.protocol.http
*
* @run main/othervm B4933582
*/
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.PrintWriter;
import java.net.Authenticator;
import java.net.BindException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.PasswordAuthentication;
import java.net.ProxySelector;
import java.net.URL;
import java.net.URLConnection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.concurrent.Executors;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import jdk.test.lib.net.URIBuilder;
import sun.net.www.protocol.http.AuthCacheImpl;
import sun.net.www.protocol.http.AuthCacheValue;
public class B4933582 implements HttpHandler {
static int count = 0;
static String authstring;
void errorReply (HttpExchange req, String reply) throws IOException {
req.getResponseHeaders().set("Connection", "close");
req.getResponseHeaders().set("WWW-Authenticate", reply);
req.sendResponseHeaders(401, -1);
}
void okReply (HttpExchange req) throws IOException {
req.sendResponseHeaders(200, 0);
try(PrintWriter pw = new PrintWriter(req.getResponseBody())) {
pw.print("Hello .");
}
}
static volatile boolean firstTime = true;
public void handle (HttpExchange req) {
try {
if(req.getRequestHeaders().get("Authorization") != null) {
authstring = req.getRequestHeaders().get("Authorization").get(0);
System.out.println(authstring);
}
if (firstTime) {
switch (count) {
case 0:
errorReply (req, "Basic realm=\"wallyworld\"");
break;
case 1:
/* client stores a username/pw for wallyworld
*/
save (authstring);
okReply (req);
break;
}
} else {
/* check the auth string is premptively set from last time */
String savedauth = retrieve();
if (savedauth.equals (authstring)) {
okReply (req);
} else {
System.out.println ("savedauth = " + savedauth);
System.out.println ("authstring = " + authstring);
errorReply (req, "Basic realm=\"wallyworld\"");
}
}
count ++;
} catch (IOException e) {
e.printStackTrace();
}
}
void save (String s) {
try {
FileOutputStream f = new FileOutputStream ("auth.save");
ObjectOutputStream os = new ObjectOutputStream (f);
os.writeObject (s);
} catch (IOException e) {
assert false;
}
}
String retrieve () {
String s = null;
try {
FileInputStream f = new FileInputStream ("auth.save");
ObjectInputStream is = new ObjectInputStream (f);
s = (String) is.readObject();
} catch (Exception e) {
assert false;
}
return s;
}
static void read (InputStream is) throws IOException {
int c;
System.out.println ("reading");
while ((c=is.read()) != -1) {
System.out.write (c);
}
System.out.println ("");
System.out.println ("finished reading");
}
static void client (String u) throws Exception {
URL url = new URL (u);
System.out.println ("client opening connection to: " + u);
URLConnection urlc = url.openConnection ();
try(InputStream is = urlc.getInputStream ()) {
read (is);
}
}
static HttpServer server;
public static void main (String[] args) throws Exception {
B4933582 b4933582 = new B4933582();
MyAuthenticator auth = new MyAuthenticator ();
Authenticator.setDefault (auth);
ProxySelector.setDefault(ProxySelector.of(null)); // no proxy
InetAddress loopback = InetAddress.getLoopbackAddress();
CacheImpl cache;
try {
server = HttpServer.create(new InetSocketAddress(loopback, 0), 10);
server.createContext("/", b4933582);
server.setExecutor(Executors.newSingleThreadExecutor());
server.start();
cache = new CacheImpl (server.getAddress().getPort());
AuthCacheValue.setAuthCache (cache);
String serverURL = URIBuilder.newBuilder()
.scheme("http")
.loopback()
.port(server.getAddress().getPort())
.path("/")
.build()
.toString();
client(serverURL + "d1/foo.html");
} finally {
if (server != null) {
server.stop(1);
}
}
int f = auth.getCount();
if (f != 1) {
except("Authenticator was called " + f + " times. Should be 1");
}
firstTime = false;
int retries = 0;
cache = new CacheImpl();
while (true) {
try {
server = HttpServer.create(new InetSocketAddress(loopback, cache.getPort()), 10);
server.createContext("/", b4933582);
server.setExecutor(Executors.newSingleThreadExecutor());
server.start();
break;
} catch (BindException e) {
if (retries++ < 5) {
Thread.sleep(200L);
System.out.println("BindException \"" + e.getMessage()
+ "\", retrying...");
continue;
} else {
throw e;
}
}
}
try {
AuthCacheValue.setAuthCache(cache);
String serverURL = URIBuilder.newBuilder()
.scheme("http")
.loopback()
.port(server.getAddress().getPort())
.path("/")
.build()
.toString();
client(serverURL + "d1/foo.html");
} finally {
if (server != null) {
server.stop(1);
}
}
f = auth.getCount();
if (f != 1) {
except("Authenticator was called " + f + " times. Should be 1");
}
}
public static void except (String s) {
server.stop(1);
throw new RuntimeException (s);
}
static class MyAuthenticator extends Authenticator {
MyAuthenticator () {
super ();
}
volatile int count = 0;
public PasswordAuthentication getPasswordAuthentication () {
PasswordAuthentication pw;
pw = new PasswordAuthentication ("user", "pass1".toCharArray());
count ++;
return pw;
}
public int getCount () {
return (count);
}
}
static class CacheImpl extends AuthCacheImpl {
HashMap<String,LinkedList<AuthCacheValue>> map;
int port; // need to store the port number the server is using
CacheImpl () throws IOException {
this (-1);
}
CacheImpl (int port) throws IOException {
super();
this.port = port;
File src = new File ("cache.ser");
if (src.exists()) {
try (ObjectInputStream is = new ObjectInputStream(
new FileInputStream(src))) {
map = (HashMap<String,LinkedList<AuthCacheValue>>)is
.readObject();
this.port = (Integer)is.readObject ();
System.out.println ("read port from file " + port);
} catch (ClassNotFoundException e) {
assert false;
}
System.out.println ("setMap from cache.ser");
} else {
map = new HashMap<>();
}
setMap (map);
}
int getPort () {
return port;
}
private void writeMap () {
File dst = new File("cache.ser");
try {
dst.delete();
if (!dst.createNewFile()) {
return;
}
} catch (IOException e) {
}
try (ObjectOutputStream os = new ObjectOutputStream(
new FileOutputStream(dst))) {
os.writeObject(map);
os.writeObject(port);
System.out.println("wrote port " + port);
} catch (IOException e) {
}
}
public void put (String pkey, AuthCacheValue value) {
System.out.println ("put: " + pkey + " " + value);
super.put (pkey, value);
writeMap();
}
public AuthCacheValue get (String pkey, String skey) {
System.out.println ("get: " + pkey + " " + skey);
AuthCacheValue i = super.get (pkey, skey);
System.out.println ("---> " + i);
return i;
}
public void remove (String pkey, AuthCacheValue value) {
System.out.println ("remove: " + pkey + " " + value);
super.remove (pkey, value);
writeMap();
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 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
@ -121,7 +121,6 @@ public class HTTPSetAuthenticatorTest extends HTTPTest {
? 0 : EXPECTED_AUTH_CALLS_PER_TEST;
int count;
int defaultCount = AUTHENTICATOR.count.get();
// Connect to the server with a GET request, then with a
// POST that contains "Hello World!"
// Uses authenticator #1
@ -283,7 +282,7 @@ public class HTTPSetAuthenticatorTest extends HTTPTest {
}
static String toString(Authenticator a) {
return sun.net.www.protocol.http.AuthenticatorKeys.getKey(a);
return a == null ? "null" : a.toString();
}
}

View File

@ -0,0 +1,177 @@
/*
* 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.
*/
/*
* @test
* @bug 8304818
* @modules java.base/sun.net.www.protocol.http
* @library /test/lib
* @build jdk.test.lib.util.ForceGC
* @run main/othervm AuthCache
*/
import com.sun.net.httpserver.BasicAuthenticator;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import java.io.IOException;
import java.lang.ref.PhantomReference;
import java.net.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
import jdk.test.lib.util.ForceGC;
public class AuthCache {
static class ClientAuth extends Authenticator {
private final String realm;
private final String username;
private final String password;
private AtomicBoolean wasCalled = new AtomicBoolean();
private String errorMsg;
ClientAuth(String realm, String username, String password) {
this.realm = realm;
this.username = username;
this.password = password;
}
/**
* returns true if getPasswordAuthentication() was called
* since the last time this method was called. The wasCalled
* flag is cleared after each call.
* If an error occurred, a RuntimeException is thrown
* @return
*/
public synchronized boolean wasCalled() {
if (errorMsg != null)
throw new RuntimeException(errorMsg);
return wasCalled.getAndSet(false);
}
protected synchronized PasswordAuthentication getPasswordAuthentication() {
if (!getRequestingPrompt().equals(realm)) {
errorMsg = String.format("Error: %s expected as realm, received %s", realm, getRequestingPrompt());
}
wasCalled.set(true);
return new PasswordAuthentication(username, password.toCharArray());
}
}
static final HttpHandler handler = (HttpExchange exch) -> {
exch.sendResponseHeaders(200, -1);
exch.close();
};
static class ServerAuth extends BasicAuthenticator {
private final String user, pass;
ServerAuth(String realm, String user, String pass) {
super(realm);
this.user = user;
this.pass = pass;
}
@Override
public boolean checkCredentials(String username, String password) {
return username.equals(user) && password.equals(pass);
}
}
/**
* Creates two Authenticators and two realms ("r1" and "r2")
* "r1" uses context "/path1" credentials = user1/pass1
* "r2" uses context "/path2" credentials = user2/pass2
*
* 1) Send request to "r1" and "r2" expect both authenticators to be called
* cache size should be 4
*
* 2) Send request to "r1" and "r2". Authenticators should not be called (cache)
*
* 3) Clear reference to "r1" and call gc.
* cache size should be 2
*
* 4) Send request to "r1" and "r2". "r1" auth should be called, but not "r2"
* cache size should be 4
*/
public static void main(String[] args) throws IOException {
var clauth1 = new ClientAuth("r1", "user1", "pass1");
PhantomReference<Authenticator> ref = new PhantomReference<>(clauth1, null);
var clauth2 = new ClientAuth("r2", "user2", "pass2");
var server = HttpServer.create(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0), 0);
var ctx1 = server.createContext("/path1", handler);
ctx1.setAuthenticator(new ServerAuth("r1", "user1", "pass1"));
var ctx2 = server.createContext("/path2", handler);
ctx2.setAuthenticator(new ServerAuth("r2", "user2", "pass2"));
var addr = server.getAddress();
var url1 = URI.create("http://" + addr.getHostName() + ":" + addr.getPort() + "/path1/").toURL();
var url2 = URI.create("http://" + addr.getHostName() + ":" + addr.getPort() + "/path2/").toURL();
server.start();
sendRequest(url1, url2, clauth1, clauth2, true, true);
sendRequest(url1, url2, clauth1, clauth2, false, false);
clauth1 = null;
ForceGC.wait(() -> ref.refersTo(null));
delay(1);
clauth1 = new ClientAuth("r1", "user1", "pass1");
sendRequest(url1, url2, clauth1, clauth2, true, false);
System.out.println("Passed");
server.stop(0);
}
static void delay(int seconds) {
try {
Thread.sleep(seconds * 1000);
} catch (InterruptedException e) {
}
}
static void sendRequest(URL u1, URL u2, ClientAuth a1, ClientAuth a2, boolean auth1Called, boolean auth2Called) throws IOException {
var urlc1 = (HttpURLConnection)u1.openConnection();
urlc1.setAuthenticator(a1);
var urlc2 = (HttpURLConnection)u2.openConnection();
urlc2.setAuthenticator(a2);
var is1 = urlc1.getInputStream();
is1.readAllBytes();
is1.close();
var is2 = urlc2.getInputStream();
is2.readAllBytes();
is2.close();
urlc1 = urlc2 = null;
boolean a1Called = a1.wasCalled();
boolean a2Called = a2.wasCalled();
if (a1Called && !auth1Called)
throw new RuntimeException("a1Called && !auth1Called");
if (!a1Called && auth1Called)
throw new RuntimeException("!a1Called && auth1Called");
if (a2Called && !auth2Called)
throw new RuntimeException("a2Called && !auth2Called");
if (!a2Called && auth2Called)
throw new RuntimeException("!a2Called && auth2Called");
}
}