8304818: Prune HttpURLConnection cache when corresponding Authenticator is garbage collected
Reviewed-by: dfuchs, djelinski
This commit is contained in:
parent
6b81342c22
commit
314db55f6d
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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() {}
|
||||
|
@ -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()
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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")
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
177
test/jdk/sun/net/www/protocol/http/AuthCache.java
Normal file
177
test/jdk/sun/net/www/protocol/http/AuthCache.java
Normal 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");
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user