1 /*
   2  * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 import java.io.IOException;
  27 import java.net.Authenticator;
  28 import java.net.HttpURLConnection;
  29 import java.net.Proxy;
  30 import java.net.URL;
  31 import java.util.Arrays;
  32 import java.util.stream.Collectors;
  33 import java.util.stream.Stream;
  34 
  35 /*
  36  * @test
  37  * @bug 8169415
  38  * @library /lib/testlibrary/
  39  * @modules java.logging
  40  *          java.base/sun.net.www
  41  *          java.base/sun.net.www.protocol.http
  42  *          jdk.httpserver/sun.net.httpserver
  43  * @build jdk.testlibrary.SimpleSSLContext HTTPTest HTTPTestServer HTTPTestClient HTTPSetAuthenticatorTest
  44  * @summary A simple HTTP test that starts an echo server supporting the given
  45  *          authentication scheme, then starts a regular HTTP client to invoke it.
  46  *          The client first does a GET request on "/", then follows on
  47  *          with a POST request that sends "Hello World!" to the server.
  48  *          The client expects to receive "Hello World!" in return.
  49  *          The test supports several execution modes:
  50  *            SERVER: The server performs Server authentication;
  51  *            PROXY:  The server pretends to be a proxy and performs
  52  *                    Proxy authentication;
  53  *            SERVER307: The server redirects the client (307) to another
  54  *                    server that perform Server authentication;
  55  *            PROXY305: The server attempts to redirect
  56  *                    the client to a proxy using 305 code;
  57  *           This test runs the client several times, providing different
  58  *           authenticators to the HttpURLConnection and verifies that
  59  *           the authenticator is invoked as expected - validating that
  60  *           connections with different authenticators do not share each
  61  *           other's socket channel and authentication info.
  62  *           Note: BASICSERVER means that the server will let the underlying
  63  *                 com.sun.net.httpserver.HttpServer perform BASIC
  64  *                 authentication when in Server mode. There should be
  65  *                 no real difference between BASICSERVER and BASIC - it should
  66  *                 be transparent on the client side.
  67  * @run main/othervm HTTPSetAuthenticatorTest NONE SERVER PROXY SERVER307 PROXY305
  68  * @run main/othervm HTTPSetAuthenticatorTest DIGEST SERVER
  69  * @run main/othervm HTTPSetAuthenticatorTest DIGEST PROXY
  70  * @run main/othervm HTTPSetAuthenticatorTest DIGEST PROXY305
  71  * @run main/othervm HTTPSetAuthenticatorTest DIGEST SERVER307
  72  * @run main/othervm HTTPSetAuthenticatorTest BASIC  SERVER
  73  * @run main/othervm HTTPSetAuthenticatorTest BASIC  PROXY
  74  * @run main/othervm HTTPSetAuthenticatorTest BASIC  PROXY305
  75  * @run main/othervm HTTPSetAuthenticatorTest BASIC  SERVER307
  76  * @run main/othervm HTTPSetAuthenticatorTest BASICSERVER SERVER
  77  * @run main/othervm HTTPSetAuthenticatorTest BASICSERVER SERVER307
  78  *
  79  * @author danielfuchs
  80  */
  81 public class HTTPSetAuthenticatorTest extends HTTPTest {
  82 
  83     public static void main(String[] args) throws Exception {
  84         String[] schemes;
  85         String[] params;
  86          if (args == null || args.length == 0) {
  87             schemes = Stream.of(HttpSchemeType.values())
  88                         .map(HttpSchemeType::name)
  89                         .collect(Collectors.toList())
  90                         .toArray(new String[0]);
  91             params = new String[0];
  92         } else {
  93             schemes = new String[] { args[0] };
  94             params = Arrays.copyOfRange(args, 1, args.length);
  95         }
  96         for (String scheme : schemes) {
  97             System.out.println("==== Testing with scheme=" + scheme + " ====\n");
  98             new HTTPSetAuthenticatorTest(HttpSchemeType.valueOf(scheme))
  99                 .execute(params);
 100             System.out.println();
 101         }
 102     }
 103 
 104     final HttpSchemeType scheme;
 105     public HTTPSetAuthenticatorTest(HttpSchemeType scheme) {
 106         this.scheme = scheme;
 107     }
 108 
 109     @Override
 110     public HttpSchemeType getHttpSchemeType() {
 111         return scheme;
 112     }
 113 
 114     @Override
 115     public int run(HTTPTestServer server,
 116                    HttpProtocolType protocol,
 117                    HttpAuthType mode)
 118             throws IOException
 119     {
 120         HttpTestAuthenticator authOne = new HttpTestAuthenticator("dublin", "foox");
 121         HttpTestAuthenticator authTwo = new HttpTestAuthenticator("dublin", "foox");
 122         int expectedIncrement = scheme == HttpSchemeType.NONE
 123                                 ? 0 : EXPECTED_AUTH_CALLS_PER_TEST;
 124         int count;
 125         int defaultCount = AUTHENTICATOR.count.get();
 126 
 127         // Connect to the server with a GET request, then with a
 128         // POST that contains "Hello World!"
 129         // Uses authenticator #1
 130         System.out.println("\nClient: Using authenticator #1: "
 131             + toString(authOne));
 132         HTTPTestClient.connect(protocol, server, mode, authOne);
 133         count = authOne.count.get();
 134         if (count != expectedIncrement) {
 135             throw new AssertionError("Authenticator #1 called " + count(count)
 136                 + " expected it to be called " + expected(expectedIncrement));
 137         }
 138 
 139         // Connect to the server with a GET request, then with a
 140         // POST that contains "Hello World!"
 141         // Uses authenticator #2
 142         System.out.println("\nClient: Using authenticator #2: "
 143             + toString(authTwo));
 144         HTTPTestClient.connect(protocol, server, mode, authTwo);
 145         count = authTwo.count.get();
 146         if (count != expectedIncrement) {
 147             throw new AssertionError("Authenticator #2 called " + count(count)
 148                 + " expected it to be called " + expected(expectedIncrement));
 149         }
 150 
 151         // Connect to the server with a GET request, then with a
 152         // POST that contains "Hello World!"
 153         // Uses authenticator #1
 154         System.out.println("\nClient: Using authenticator #1 again: "
 155             + toString(authOne));
 156         HTTPTestClient.connect(protocol, server, mode, authOne);
 157         count = authOne.count.get();
 158         if (count != expectedIncrement) {
 159             throw new AssertionError("Authenticator #1 called " + count(count)
 160                 + " expected it to be called " + expected(expectedIncrement));
 161         }
 162         count = authTwo.count.get();
 163         if (count != expectedIncrement) {
 164             throw new AssertionError("Authenticator #2 called " + count(count)
 165                 + " expected it to be called " + expected(expectedIncrement));
 166         }
 167         count =  AUTHENTICATOR.count.get();
 168         if (count != defaultCount) {
 169             throw new AssertionError("Default Authenticator called " + count(count)
 170                 + " expected it to be called " + expected(defaultCount));
 171         }
 172 
 173         // Now tries with the default authenticator: it should be invoked.
 174         System.out.println("\nClient: Using the default authenticator: "
 175             + toString(null));
 176         HTTPTestClient.connect(protocol, server, mode, null);
 177         count = authOne.count.get();
 178         if (count != expectedIncrement) {
 179             throw new AssertionError("Authenticator #1 called " + count(count)
 180                 + " expected it to be called " + expected(expectedIncrement));
 181         }
 182         count = authTwo.count.get();
 183         if (count != expectedIncrement) {
 184             throw new AssertionError("Authenticator #2 called " + count(count)
 185                 + " expected it to be called " + expected(expectedIncrement));
 186         }
 187         count =  AUTHENTICATOR.count.get();
 188         if (count != defaultCount + expectedIncrement) {
 189             throw new AssertionError("Default Authenticator called " + count(count)
 190                 + " expected it to be called " + expected(defaultCount + expectedIncrement));
 191         }
 192 
 193         // Now tries with explicitly setting the default authenticator: it should
 194         // be invoked again.
 195         // Uncomment the code below when 8169068 is available.
 196 //        System.out.println("\nClient: Explicitly setting the default authenticator: "
 197 //            + toString(Authenticator.getDefault()));
 198 //        HTTPTestClient.connect(protocol, server, mode, Authenticator.getDefault());
 199 //        count = authOne.count.get();
 200 //        if (count != expectedIncrement) {
 201 //            throw new AssertionError("Authenticator #1 called " + count(count)
 202 //                + " expected it to be called " + expected(expectedIncrement));
 203 //        }
 204 //        count = authTwo.count.get();
 205 //        if (count != expectedIncrement) {
 206 //            throw new AssertionError("Authenticator #2 called " + count(count)
 207 //                + " expected it to be called " + expected(expectedIncrement));
 208 //        }
 209 //        count =  AUTHENTICATOR.count.get();
 210 //        if (count != defaultCount + 2 * expectedIncrement) {
 211 //            throw new AssertionError("Default Authenticator called " + count(count)
 212 //                + " expected it to be called "
 213 //                + expected(defaultCount + 2 * expectedIncrement));
 214 //        }
 215 
 216         // Now tries to set an authenticator on a connected connection.
 217         URL url = url(protocol,  server.getAddress(), "/");
 218         Proxy proxy = proxy(server, mode);
 219         HttpURLConnection conn = openConnection(url, mode, proxy);
 220         try {
 221             conn.setAuthenticator(null);
 222             throw new RuntimeException("Expected NullPointerException"
 223                     + " trying to set a null authenticator"
 224                     + " not raised.");
 225         } catch (NullPointerException npe) {
 226             System.out.println("Client: caught expected NPE"
 227                     + " trying to set a null authenticator: "
 228                     + npe);
 229         }
 230         conn.connect();
 231         try {
 232             try {
 233                 conn.setAuthenticator(authOne);
 234                 throw new RuntimeException("Expected IllegalStateException"
 235                         + " trying to set an authenticator after connect"
 236                         + " not raised.");
 237             } catch (IllegalStateException ise) {
 238                 System.out.println("Client: caught expected ISE"
 239                         + " trying to set an authenticator after connect: "
 240                         + ise);
 241             }
 242             // Uncomment the code below when 8169068 is available.
 243 //            try {
 244 //                conn.setAuthenticator(Authenticator.getDefault());
 245 //                throw new RuntimeException("Expected IllegalStateException"
 246 //                        + " trying to set an authenticator after connect"
 247 //                        + " not raised.");
 248 //            } catch (IllegalStateException ise) {
 249 //                System.out.println("Client: caught expected ISE"
 250 //                        + " trying to set an authenticator after connect: "
 251 //                        + ise);
 252 //            }
 253             try {
 254                 conn.setAuthenticator(null);
 255                 throw new RuntimeException("Expected"
 256                         + " IllegalStateException or NullPointerException"
 257                         + " trying to set a null authenticator after connect"
 258                         + " not raised.");
 259             } catch (IllegalStateException | NullPointerException xxe) {
 260                 System.out.println("Client: caught expected "
 261                         + xxe.getClass().getSimpleName()
 262                         + " trying to set a null authenticator after connect: "
 263                         + xxe);
 264             }
 265         } finally {
 266             conn.disconnect();
 267         }
 268 
 269         // double check that authOne and authTwo haven't been invoked.
 270         count = authOne.count.get();
 271         if (count != expectedIncrement) {
 272             throw new AssertionError("Authenticator #1 called " + count(count)
 273                 + " expected it to be called " + expected(expectedIncrement));
 274         }
 275         count = authTwo.count.get();
 276         if (count != expectedIncrement) {
 277             throw new AssertionError("Authenticator #2 called " + count(count)
 278                 + " expected it to be called " + expected(expectedIncrement));
 279         }
 280 
 281         // All good!
 282         // return the number of times the default authenticator is supposed
 283         // to have been called.
 284         return scheme == HttpSchemeType.NONE ? 0 : 1 * EXPECTED_AUTH_CALLS_PER_TEST;
 285     }
 286 
 287     static String toString(Authenticator a) {
 288         return sun.net.www.protocol.http.AuthenticatorKeys.getKey(a);
 289     }
 290 
 291 }