test/jdk/java/net/HttpURLConnection/SetAuthenticator/HTTPSetAuthenticatorTest.java
8191169: java/net/Authenticator/B4769350.java failed intermittently
Summary: fixed a race condition in AuthenticationInfo when serializeAuth=true
Reviewed-by: chegar, michaelm
/*
* Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.io.IOException;
import java.net.Authenticator;
import java.net.HttpURLConnection;
import java.net.Proxy;
import java.net.URL;
import java.util.Arrays;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/*
* @test
* @bug 8169415
* @library /test/lib
* @modules java.logging
* java.base/sun.net.www
* java.base/sun.net.www.protocol.http
* jdk.httpserver/sun.net.httpserver
* @build jdk.test.lib.net.SimpleSSLContext HTTPTest HTTPTestServer HTTPTestClient HTTPSetAuthenticatorTest
* @summary A simple HTTP test that starts an echo server supporting the given
* authentication scheme, then starts a regular HTTP client to invoke it.
* The client first does a GET request on "/", then follows on
* with a POST request that sends "Hello World!" to the server.
* The client expects to receive "Hello World!" in return.
* The test supports several execution modes:
* SERVER: The server performs Server authentication;
* PROXY: The server pretends to be a proxy and performs
* Proxy authentication;
* SERVER307: The server redirects the client (307) to another
* server that perform Server authentication;
* PROXY305: The server attempts to redirect
* the client to a proxy using 305 code;
* This test runs the client several times, providing different
* authenticators to the HttpURLConnection and verifies that
* the authenticator is invoked as expected - validating that
* connections with different authenticators do not share each
* other's socket channel and authentication info.
* Note: BASICSERVER means that the server will let the underlying
* com.sun.net.httpserver.HttpServer perform BASIC
* authentication when in Server mode. There should be
* no real difference between BASICSERVER and BASIC - it should
* be transparent on the client side.
* @run main/othervm HTTPSetAuthenticatorTest NONE SERVER PROXY SERVER307 PROXY305
* @run main/othervm HTTPSetAuthenticatorTest DIGEST SERVER
* @run main/othervm HTTPSetAuthenticatorTest DIGEST PROXY
* @run main/othervm HTTPSetAuthenticatorTest DIGEST PROXY305
* @run main/othervm HTTPSetAuthenticatorTest DIGEST SERVER307
* @run main/othervm HTTPSetAuthenticatorTest BASIC SERVER
* @run main/othervm HTTPSetAuthenticatorTest BASIC PROXY
* @run main/othervm HTTPSetAuthenticatorTest BASIC PROXY305
* @run main/othervm HTTPSetAuthenticatorTest BASIC SERVER307
* @run main/othervm HTTPSetAuthenticatorTest BASICSERVER SERVER
* @run main/othervm HTTPSetAuthenticatorTest BASICSERVER SERVER307
*
* @author danielfuchs
*/
public class HTTPSetAuthenticatorTest extends HTTPTest {
public static void main(String[] args) throws Exception {
String[] schemes;
String[] params;
if (args == null || args.length == 0) {
schemes = Stream.of(HttpSchemeType.values())
.map(HttpSchemeType::name)
.collect(Collectors.toList())
.toArray(new String[0]);
params = new String[0];
} else {
schemes = new String[] { args[0] };
params = Arrays.copyOfRange(args, 1, args.length);
}
for (String scheme : schemes) {
System.out.println("==== Testing with scheme=" + scheme + " ====\n");
new HTTPSetAuthenticatorTest(HttpSchemeType.valueOf(scheme))
.execute(params);
System.out.println();
}
}
final HttpSchemeType scheme;
public HTTPSetAuthenticatorTest(HttpSchemeType scheme) {
this.scheme = scheme;
}
@Override
public HttpSchemeType getHttpSchemeType() {
return scheme;
}
@Override
public int run(HTTPTestServer server,
HttpProtocolType protocol,
HttpAuthType mode)
throws IOException
{
HttpTestAuthenticator authOne = new HttpTestAuthenticator("authOne", "dublin", "foox");
HttpTestAuthenticator authTwo = new HttpTestAuthenticator("authTwo", "dublin", "foox");
int expectedIncrement = scheme == HttpSchemeType.NONE
? 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
System.out.println("\nClient: Using authenticator #1: "
+ toString(authOne));
HTTPTestClient.connect(protocol, server, mode, authOne);
count = authOne.count.get();
if (count != expectedIncrement) {
throw new AssertionError("Authenticator #1 called " + count(count)
+ " expected it to be called " + expected(expectedIncrement));
}
// Connect to the server with a GET request, then with a
// POST that contains "Hello World!"
// Uses authenticator #2
System.out.println("\nClient: Using authenticator #2: "
+ toString(authTwo));
HTTPTestClient.connect(protocol, server, mode, authTwo);
count = authTwo.count.get();
if (count != expectedIncrement) {
throw new AssertionError("Authenticator #2 called " + count(count)
+ " expected it to be called " + expected(expectedIncrement));
}
// Connect to the server with a GET request, then with a
// POST that contains "Hello World!"
// Uses authenticator #1
System.out.println("\nClient: Using authenticator #1 again: "
+ toString(authOne));
HTTPTestClient.connect(protocol, server, mode, authOne);
count = authOne.count.get();
if (count != expectedIncrement) {
throw new AssertionError("Authenticator #1 called " + count(count)
+ " expected it to be called " + expected(expectedIncrement));
}
count = authTwo.count.get();
if (count != expectedIncrement) {
throw new AssertionError("Authenticator #2 called " + count(count)
+ " expected it to be called " + expected(expectedIncrement));
}
count = AUTHENTICATOR.count.get();
if (count != defaultCount) {
throw new AssertionError("Default Authenticator called " + count(count)
+ " expected it to be called " + expected(defaultCount));
}
// Now tries with the default authenticator: it should be invoked.
System.out.println("\nClient: Using the default authenticator: "
+ toString(null));
HTTPTestClient.connect(protocol, server, mode, null);
count = authOne.count.get();
if (count != expectedIncrement) {
throw new AssertionError("Authenticator #1 called " + count(count)
+ " expected it to be called " + expected(expectedIncrement));
}
count = authTwo.count.get();
if (count != expectedIncrement) {
throw new AssertionError("Authenticator #2 called " + count(count)
+ " expected it to be called " + expected(expectedIncrement));
}
count = AUTHENTICATOR.count.get();
if (count != defaultCount + expectedIncrement) {
throw new AssertionError("Default Authenticator called " + count(count)
+ " expected it to be called " + expected(defaultCount + expectedIncrement));
}
// Now tries with explicitly setting the default authenticator: it should
// be invoked again.
// Uncomment the code below when 8169068 is available.
// System.out.println("\nClient: Explicitly setting the default authenticator: "
// + toString(Authenticator.getDefault()));
// HTTPTestClient.connect(protocol, server, mode, Authenticator.getDefault());
// count = authOne.count.get();
// if (count != expectedIncrement) {
// throw new AssertionError("Authenticator #1 called " + count(count)
// + " expected it to be called " + expected(expectedIncrement));
// }
// count = authTwo.count.get();
// if (count != expectedIncrement) {
// throw new AssertionError("Authenticator #2 called " + count(count)
// + " expected it to be called " + expected(expectedIncrement));
// }
// count = AUTHENTICATOR.count.get();
// if (count != defaultCount + 2 * expectedIncrement) {
// throw new AssertionError("Default Authenticator called " + count(count)
// + " expected it to be called "
// + expected(defaultCount + 2 * expectedIncrement));
// }
// Now tries to set an authenticator on a connected connection.
URL url = url(protocol, server.getAddress(), "/");
Proxy proxy = proxy(server, mode);
HttpURLConnection conn = openConnection(url, mode, proxy);
try {
conn.setAuthenticator(null);
throw new RuntimeException("Expected NullPointerException"
+ " trying to set a null authenticator"
+ " not raised.");
} catch (NullPointerException npe) {
System.out.println("Client: caught expected NPE"
+ " trying to set a null authenticator: "
+ npe);
}
conn.connect();
try {
try {
conn.setAuthenticator(authOne);
throw new RuntimeException("Expected IllegalStateException"
+ " trying to set an authenticator after connect"
+ " not raised.");
} catch (IllegalStateException ise) {
System.out.println("Client: caught expected ISE"
+ " trying to set an authenticator after connect: "
+ ise);
}
// Uncomment the code below when 8169068 is available.
// try {
// conn.setAuthenticator(Authenticator.getDefault());
// throw new RuntimeException("Expected IllegalStateException"
// + " trying to set an authenticator after connect"
// + " not raised.");
// } catch (IllegalStateException ise) {
// System.out.println("Client: caught expected ISE"
// + " trying to set an authenticator after connect: "
// + ise);
// }
try {
conn.setAuthenticator(null);
throw new RuntimeException("Expected"
+ " IllegalStateException or NullPointerException"
+ " trying to set a null authenticator after connect"
+ " not raised.");
} catch (IllegalStateException | NullPointerException xxe) {
System.out.println("Client: caught expected "
+ xxe.getClass().getSimpleName()
+ " trying to set a null authenticator after connect: "
+ xxe);
}
} finally {
conn.disconnect();
}
// double check that authOne and authTwo haven't been invoked.
count = authOne.count.get();
if (count != expectedIncrement) {
throw new AssertionError("Authenticator #1 called " + count(count)
+ " expected it to be called " + expected(expectedIncrement));
}
count = authTwo.count.get();
if (count != expectedIncrement) {
throw new AssertionError("Authenticator #2 called " + count(count)
+ " expected it to be called " + expected(expectedIncrement));
}
// All good!
// return the number of times the default authenticator is supposed
// to have been called.
return scheme == HttpSchemeType.NONE ? 0 : 1 * EXPECTED_AUTH_CALLS_PER_TEST;
}
static String toString(Authenticator a) {
return sun.net.www.protocol.http.AuthenticatorKeys.getKey(a);
}
}