From 314db55f6dde033f62481b62f10dd11030473569 Mon Sep 17 00:00:00 2001 From: Michael McMahon Date: Mon, 24 Apr 2023 17:25:32 +0000 Subject: [PATCH] 8304818: Prune HttpURLConnection cache when corresponding Authenticator is garbage collected Reviewed-by: dfuchs, djelinski --- .../share/classes/java/net/Authenticator.java | 12 +- .../classes/sun/net/www/http/HttpClient.java | 23 +- .../sun/net/www/protocol/http/AuthCache.java | 10 +- .../net/www/protocol/http/AuthCacheImpl.java | 25 +- .../net/www/protocol/http/AuthCacheValue.java | 15 +- .../www/protocol/http/AuthenticationInfo.java | 152 +++----- .../www/protocol/http/AuthenticatorKeys.java | 76 ---- .../protocol/http/BasicAuthentication.java | 24 +- .../protocol/http/DigestAuthentication.java | 12 +- .../www/protocol/http/HttpURLConnection.java | 70 ++-- .../http/NTLMAuthenticationProxy.java | 36 +- .../http/NegotiateAuthentication.java | 6 +- .../net/www/protocol/https/HttpsClient.java | 11 +- .../http/ntlm/NTLMAuthentication.java | 15 +- .../http/ntlm/NTLMAuthentication.java | 14 +- test/jdk/java/net/Authenticator/B4933582.java | 337 ------------------ .../HTTPSetAuthenticatorTest.java | 5 +- .../sun/net/www/protocol/http/AuthCache.java | 177 +++++++++ 18 files changed, 340 insertions(+), 680 deletions(-) delete mode 100644 src/java.base/share/classes/sun/net/www/protocol/http/AuthenticatorKeys.java delete mode 100644 test/jdk/java/net/Authenticator/B4933582.java create mode 100644 test/jdk/sun/net/www/protocol/http/AuthCache.java diff --git a/src/java.base/share/classes/java/net/Authenticator.java b/src/java.base/share/classes/java/net/Authenticator.java index 8e78f84336f..c164c598db9 100644 --- a/src/java.base/share/classes/java/net/Authenticator.java +++ b/src/java.base/share/classes/java/net/Authenticator.java @@ -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); - } } diff --git a/src/java.base/share/classes/sun/net/www/http/HttpClient.java b/src/java.base/share/classes/sun/net/www/http/HttpClient.java index 31f32b44da6..a1c9e972990 100644 --- a/src/java.base/share/classes/sun/net/www/http/HttpClient.java +++ b/src/java.base/share/classes/sun/net/www/http/HttpClient.java @@ -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: diff --git a/src/java.base/share/classes/sun/net/www/protocol/http/AuthCache.java b/src/java.base/share/classes/sun/net/www/protocol/http/AuthCache.java index 5ca44fa64e6..92cf996a4ce 100644 --- a/src/java.base/share/classes/sun/net/www/protocol/http/AuthCache.java +++ b/src/java.base/share/classes/sun/net/www/protocol/http/AuthCache.java @@ -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 diff --git a/src/java.base/share/classes/sun/net/www/protocol/http/AuthCacheImpl.java b/src/java.base/share/classes/sun/net/www/protocol/http/AuthCacheImpl.java index 1fac8291fe7..2f7206c93ad 100644 --- a/src/java.base/share/classes/sun/net/www/protocol/http/AuthCacheImpl.java +++ b/src/java.base/share/classes/sun/net/www/protocol/http/AuthCacheImpl.java @@ -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 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()); + } } diff --git a/src/java.base/share/classes/sun/net/www/protocol/http/AuthCacheValue.java b/src/java.base/share/classes/sun/net/www/protocol/http/AuthCacheValue.java index 9931ab9a607..c339f012b12 100644 --- a/src/java.base/share/classes/sun/net/www/protocol/http/AuthCacheValue.java +++ b/src/java.base/share/classes/sun/net/www/protocol/http/AuthCacheValue.java @@ -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() {} diff --git a/src/java.base/share/classes/sun/net/www/protocol/http/AuthenticationInfo.java b/src/java.base/share/classes/sun/net/www/protocol/http/AuthenticationInfo.java index 8fb6a1fac13..8134b994812 100644 --- a/src/java.base/share/classes/sun/net/www/protocol/http/AuthenticationInfo.java +++ b/src/java.base/share/classes/sun/net/www/protocol/http/AuthenticationInfo.java @@ -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 cache) { - AuthenticationInfo cached = cache.apply(key); + private static AuthenticationInfo requestAuthentication( + String key, AuthCacheImpl acache, BiFunction 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() diff --git a/src/java.base/share/classes/sun/net/www/protocol/http/AuthenticatorKeys.java b/src/java.base/share/classes/sun/net/www/protocol/http/AuthenticatorKeys.java deleted file mode 100644 index 3ae256ca50f..00000000000 --- a/src/java.base/share/classes/sun/net/www/protocol/http/AuthenticatorKeys.java +++ /dev/null @@ -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; - } - } - -} diff --git a/src/java.base/share/classes/sun/net/www/protocol/http/BasicAuthentication.java b/src/java.base/share/classes/sun/net/www/protocol/http/BasicAuthentication.java index 4dce36840c4..73d5ff98b3a 100644 --- a/src/java.base/share/classes/sun/net/www/protocol/http/BasicAuthentication.java +++ b/src/java.base/share/classes/sun/net/www/protocol/http/BasicAuthentication.java @@ -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; } diff --git a/src/java.base/share/classes/sun/net/www/protocol/http/DigestAuthentication.java b/src/java.base/share/classes/sun/net/www/protocol/http/DigestAuthentication.java index 0c25025cd09..705c3d4976f 100644 --- a/src/java.base/share/classes/sun/net/www/protocol/http/DigestAuthentication.java +++ b/src/java.base/share/classes/sun/net/www/protocol/http/DigestAuthentication.java @@ -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; diff --git a/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java b/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java index b677278459a..8388b11124b 100644 --- a/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java +++ b/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java @@ -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)); diff --git a/src/java.base/share/classes/sun/net/www/protocol/http/NTLMAuthenticationProxy.java b/src/java.base/share/classes/sun/net/www/protocol/http/NTLMAuthenticationProxy.java index 07057c61db1..cd83d951c83 100644 --- a/src/java.base/share/classes/sun/net/www/protocol/http/NTLMAuthenticationProxy.java +++ b/src/java.base/share/classes/sun/net/www/protocol/http/NTLMAuthenticationProxy.java @@ -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 threeArgCtr; private final Constructor fourArgCtr; - private final Constructor sixArgCtr; - private NTLMAuthenticationProxy(Constructor fourArgCtr, - Constructor sixArgCtr) { + private NTLMAuthenticationProxy(Constructor threeArgCtr, + Constructor 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 cl; - Constructor fourArg, sixArg; + Constructor threeArg, fourArg; try { cl = (Class)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); diff --git a/src/java.base/share/classes/sun/net/www/protocol/http/NegotiateAuthentication.java b/src/java.base/share/classes/sun/net/www/protocol/http/NegotiateAuthentication.java index 97aa5233833..676020a11ba 100644 --- a/src/java.base/share/classes/sun/net/www/protocol/http/NegotiateAuthentication.java +++ b/src/java.base/share/classes/sun/net/www/protocol/http/NegotiateAuthentication.java @@ -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; } diff --git a/src/java.base/share/classes/sun/net/www/protocol/https/HttpsClient.java b/src/java.base/share/classes/sun/net/www/protocol/https/HttpsClient.java index d6af0df8383..9bc28a353ab 100644 --- a/src/java.base/share/classes/sun/net/www/protocol/https/HttpsClient.java +++ b/src/java.base/share/classes/sun/net/www/protocol/https/HttpsClient.java @@ -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") diff --git a/src/java.base/unix/classes/sun/net/www/protocol/http/ntlm/NTLMAuthentication.java b/src/java.base/unix/classes/sun/net/www/protocol/http/ntlm/NTLMAuthentication.java index 19d169bb1f8..72ef34c5f0f 100644 --- a/src/java.base/unix/classes/sun/net/www/protocol/http/ntlm/NTLMAuthentication.java +++ b/src/java.base/unix/classes/sun/net/www/protocol/http/ntlm/NTLMAuthentication.java @@ -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); } diff --git a/src/java.base/windows/classes/sun/net/www/protocol/http/ntlm/NTLMAuthentication.java b/src/java.base/windows/classes/sun/net/www/protocol/http/ntlm/NTLMAuthentication.java index d528d7265a7..d9eaabe2b4f 100644 --- a/src/java.base/windows/classes/sun/net/www/protocol/http/ntlm/NTLMAuthentication.java +++ b/src/java.base/windows/classes/sun/net/www/protocol/http/ntlm/NTLMAuthentication.java @@ -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); } diff --git a/test/jdk/java/net/Authenticator/B4933582.java b/test/jdk/java/net/Authenticator/B4933582.java deleted file mode 100644 index be6584a297d..00000000000 --- a/test/jdk/java/net/Authenticator/B4933582.java +++ /dev/null @@ -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> 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>)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(); - } - } -} diff --git a/test/jdk/java/net/HttpURLConnection/SetAuthenticator/HTTPSetAuthenticatorTest.java b/test/jdk/java/net/HttpURLConnection/SetAuthenticator/HTTPSetAuthenticatorTest.java index 6145f48523d..4d6a74e760b 100644 --- a/test/jdk/java/net/HttpURLConnection/SetAuthenticator/HTTPSetAuthenticatorTest.java +++ b/test/jdk/java/net/HttpURLConnection/SetAuthenticator/HTTPSetAuthenticatorTest.java @@ -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(); } } diff --git a/test/jdk/sun/net/www/protocol/http/AuthCache.java b/test/jdk/sun/net/www/protocol/http/AuthCache.java new file mode 100644 index 00000000000..fb9872bc820 --- /dev/null +++ b/test/jdk/sun/net/www/protocol/http/AuthCache.java @@ -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 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"); + } +}