jdk/test/java/net/HttpURLConnection/SetAuthenticator/HTTPTest.java
changeset 42351 85ed90be0ae1
child 43497 1a2262d4395c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/net/HttpURLConnection/SetAuthenticator/HTTPTest.java	Fri Dec 02 13:18:50 2016 +0000
@@ -0,0 +1,283 @@
+/*
+ * 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.
+ */
+
+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.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;
+    }
+}