--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/HttpURLConnection/SetAuthenticator/HTTPTest.java Tue Sep 12 19:03:39 2017 +0200
@@ -0,0 +1,284 @@
+/*
+ * Copyright (c) 2016, 2017, 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.
+ */
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.net.Authenticator;
+import java.net.HttpURLConnection;
+import java.net.InetSocketAddress;
+import java.net.MalformedURLException;
+import java.net.PasswordAuthentication;
+import java.net.Proxy;
+import java.net.URL;
+import java.util.Locale;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.stream.Stream;
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSession;
+import jdk.testlibrary.SimpleSSLContext;
+
+/*
+ * @test
+ * @bug 8169415
+ * @library /lib/testlibrary/
+ * @modules java.logging
+ * java.base/sun.net.www
+ * jdk.httpserver/sun.net.httpserver
+ * @build jdk.testlibrary.SimpleSSLContext HTTPTest HTTPTestServer HTTPTestClient
+ * @summary A simple HTTP test that starts an echo server supporting Digest
+ * authentication, 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 Digest Server authentication;
+ * PROXY: The server pretends to be a proxy and performs
+ * Digest Proxy authentication;
+ * SERVER307: The server redirects the client (307) to another
+ * server that perform Digest authentication;
+ * PROXY305: The server attempts to redirect
+ * the client to a proxy using 305 code;
+ * @run main/othervm HTTPTest SERVER
+ * @run main/othervm HTTPTest PROXY
+ * @run main/othervm HTTPTest SERVER307
+ * @run main/othervm HTTPTest PROXY305
+ *
+ * @author danielfuchs
+ */
+public class HTTPTest {
+
+ public static final boolean DEBUG =
+ Boolean.parseBoolean(System.getProperty("test.debug", "false"));
+ public static enum HttpAuthType { SERVER, PROXY, SERVER307, PROXY305 };
+ public static enum HttpProtocolType { HTTP, HTTPS };
+ public static enum HttpSchemeType { NONE, BASICSERVER, BASIC, DIGEST };
+ public static final HttpAuthType DEFAULT_HTTP_AUTH_TYPE = HttpAuthType.SERVER;
+ public static final HttpProtocolType DEFAULT_PROTOCOL_TYPE = HttpProtocolType.HTTP;
+ public static final HttpSchemeType DEFAULT_SCHEME_TYPE = HttpSchemeType.DIGEST;
+
+ public static class HttpTestAuthenticator extends Authenticator {
+ private final String realm;
+ private final String username;
+ // Used to prevent incrementation of 'count' when calling the
+ // authenticator from the server side.
+ private final ThreadLocal<Boolean> skipCount = new ThreadLocal<>();
+ // count will be incremented every time getPasswordAuthentication()
+ // is called from the client side.
+ final AtomicInteger count = new AtomicInteger();
+
+ public HttpTestAuthenticator(String realm, String username) {
+ this.realm = realm;
+ this.username = username;
+ }
+
+ @Override
+ protected PasswordAuthentication getPasswordAuthentication() {
+ if (skipCount.get() == null || skipCount.get().booleanValue() == false) {
+ System.out.println("Authenticator called: " + count.incrementAndGet());
+ }
+ return new PasswordAuthentication(getUserName(),
+ new char[] {'b','a','r'});
+ }
+
+ // Called by the server side to get the password of the user
+ // being authentified.
+ public final char[] getPassword(String user) {
+ if (user.equals(username)) {
+ skipCount.set(Boolean.TRUE);
+ try {
+ return getPasswordAuthentication().getPassword();
+ } finally {
+ skipCount.set(Boolean.FALSE);
+ }
+ }
+ throw new SecurityException("User unknown: " + user);
+ }
+
+ public final String getUserName() {
+ return username;
+ }
+ public final String getRealm() {
+ return realm;
+ }
+
+ }
+ public static final HttpTestAuthenticator AUTHENTICATOR;
+ static {
+ AUTHENTICATOR = new HttpTestAuthenticator("dublin", "foox");
+ Authenticator.setDefault(AUTHENTICATOR);
+ }
+
+ static {
+ try {
+ HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
+ public boolean verify(String hostname, SSLSession session) {
+ return true;
+ }
+ });
+ SSLContext.setDefault(new SimpleSSLContext().get());
+ } catch (IOException ex) {
+ throw new ExceptionInInitializerError(ex);
+ }
+ }
+
+ static final Logger logger = Logger.getLogger ("com.sun.net.httpserver");
+ static {
+ if (DEBUG) logger.setLevel(Level.ALL);
+ Stream.of(Logger.getLogger("").getHandlers())
+ .forEach(h -> h.setLevel(Level.ALL));
+ }
+
+ static final int EXPECTED_AUTH_CALLS_PER_TEST = 1;
+
+ public static void main(String[] args) throws Exception {
+ // new HTTPTest().execute(HttpAuthType.SERVER.name());
+ new HTTPTest().execute(args);
+ }
+
+ public void execute(String... args) throws Exception {
+ Stream<HttpAuthType> modes;
+ if (args == null || args.length == 0) {
+ modes = Stream.of(HttpAuthType.values());
+ } else {
+ modes = Stream.of(args).map(HttpAuthType::valueOf);
+ }
+ modes.forEach(this::test);
+ System.out.println("Test PASSED - Authenticator called: "
+ + expected(AUTHENTICATOR.count.get()));
+ }
+
+ public void test(HttpAuthType mode) {
+ for (HttpProtocolType type: HttpProtocolType.values()) {
+ test(type, mode);
+ }
+ }
+
+ public HttpSchemeType getHttpSchemeType() {
+ return DEFAULT_SCHEME_TYPE;
+ }
+
+ public void test(HttpProtocolType protocol, HttpAuthType mode) {
+ if (mode == HttpAuthType.PROXY305 && protocol == HttpProtocolType.HTTPS ) {
+ // silently skip unsupported test combination
+ return;
+ }
+ System.out.println("\n**** Testing " + protocol + " "
+ + mode + " mode ****\n");
+ int authCount = AUTHENTICATOR.count.get();
+ int expectedIncrement = 0;
+ try {
+ // Creates an HTTP server that echoes back whatever is in the
+ // request body.
+ HTTPTestServer server =
+ HTTPTestServer.create(protocol,
+ mode,
+ AUTHENTICATOR,
+ getHttpSchemeType());
+ try {
+ expectedIncrement += run(server, protocol, mode);
+ } finally {
+ server.stop();
+ }
+ } catch (IOException ex) {
+ ex.printStackTrace(System.err);
+ throw new UncheckedIOException(ex);
+ }
+ int count = AUTHENTICATOR.count.get();
+ if (count != authCount + expectedIncrement) {
+ throw new AssertionError("Authenticator called " + count(count)
+ + " expected it to be called "
+ + expected(authCount + expectedIncrement));
+ }
+ }
+
+ /**
+ * Runs the test with the given parameters.
+ * @param server The server
+ * @param protocol The protocol (HTTP/HTTPS)
+ * @param mode The mode (PROXY, SERVER, SERVER307...)
+ * @return The number of times the default authenticator should have been
+ * called.
+ * @throws IOException in case of connection or protocol issues
+ */
+ public int run(HTTPTestServer server,
+ HttpProtocolType protocol,
+ HttpAuthType mode)
+ throws IOException
+ {
+ // Connect to the server with a GET request, then with a
+ // POST that contains "Hello World!"
+ HTTPTestClient.connect(protocol, server, mode, null);
+ // return the number of times the default authenticator is supposed
+ // to have been called.
+ return EXPECTED_AUTH_CALLS_PER_TEST;
+ }
+
+ public static String count(int count) {
+ switch(count) {
+ case 0: return "not even once";
+ case 1: return "once";
+ case 2: return "twice";
+ default: return String.valueOf(count) + " times";
+ }
+ }
+
+ public static String expected(int count) {
+ switch(count) {
+ default: return count(count);
+ }
+ }
+ public static String protocol(HttpProtocolType type) {
+ return type.name().toLowerCase(Locale.US);
+ }
+
+ public static URL url(HttpProtocolType protocol, InetSocketAddress address,
+ String path) throws MalformedURLException {
+ return new URL(protocol(protocol),
+ address.getHostString(),
+ address.getPort(), path);
+ }
+
+ public static Proxy proxy(HTTPTestServer server, HttpAuthType authType) {
+ return (authType == HttpAuthType.PROXY)
+ ? new Proxy(Proxy.Type.HTTP, server.getAddress())
+ : null;
+ }
+
+ public static HttpURLConnection openConnection(URL url,
+ HttpAuthType authType,
+ Proxy proxy)
+ throws IOException {
+
+ HttpURLConnection conn = (HttpURLConnection)
+ (authType == HttpAuthType.PROXY
+ ? url.openConnection(proxy)
+ : url.openConnection());
+ return conn;
+ }
+}