test/jdk/java/net/httpclient/AuthFilterCacheTest.java
changeset 59029 3786a0962570
equal deleted inserted replaced
59028:4dbdb7a8fa75 59029:3786a0962570
       
     1 /*
       
     2  * Copyright (c) 2019, 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.
       
     8  *
       
     9  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    12  * version 2 for more details (a copy is included in the LICENSE file that
       
    13  * accompanied this code).
       
    14  *
       
    15  * You should have received a copy of the GNU General Public License version
       
    16  * 2 along with this work; if not, write to the Free Software Foundation,
       
    17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    18  *
       
    19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    20  * or visit www.oracle.com if you need additional information or have any
       
    21  * questions.
       
    22  */
       
    23 
       
    24 import java.io.IOException;
       
    25 import java.net.*;
       
    26 import java.net.http.HttpClient;
       
    27 import java.net.http.HttpRequest;
       
    28 import java.net.http.HttpResponse;
       
    29 import java.nio.charset.StandardCharsets;
       
    30 import java.util.ArrayList;
       
    31 import java.util.List;
       
    32 import java.util.concurrent.*;
       
    33 import java.util.concurrent.atomic.AtomicLong;
       
    34 
       
    35 import com.sun.net.httpserver.HttpServer;
       
    36 import com.sun.net.httpserver.HttpsConfigurator;
       
    37 import com.sun.net.httpserver.HttpsServer;
       
    38 import org.testng.annotations.AfterClass;
       
    39 import org.testng.annotations.BeforeClass;
       
    40 import org.testng.annotations.DataProvider;
       
    41 import org.testng.annotations.Test;
       
    42 
       
    43 import javax.net.ssl.SSLContext;
       
    44 
       
    45 /**
       
    46  * @test
       
    47  * @bug 8232853
       
    48  * @summary AuthenticationFilter.Cache::remove may throw ConcurrentModificationException
       
    49  * @library /test/lib http2/server
       
    50  * @build jdk.test.lib.net.SimpleSSLContext HttpServerAdapters DigestEchoServer HttpRedirectTest
       
    51  * @modules java.net.http/jdk.internal.net.http.common
       
    52  * java.net.http/jdk.internal.net.http.frame
       
    53  * java.net.http/jdk.internal.net.http.hpack
       
    54  * java.logging
       
    55  * java.base/sun.net.www.http
       
    56  * java.base/sun.net.www
       
    57  * java.base/sun.net
       
    58  * @run testng/othervm -Dtest.requiresHost=true
       
    59  * -Djdk.httpclient.HttpClient.log=headers
       
    60  * -Djdk.internal.httpclient.debug=false
       
    61  * AuthFilterCacheTest
       
    62  */
       
    63 
       
    64 public class AuthFilterCacheTest implements HttpServerAdapters {
       
    65 
       
    66     static final String RESPONSE_BODY = "Hello World!";
       
    67     static final int REQUEST_COUNT = 5;
       
    68     static final int URI_COUNT = 6;
       
    69     static final CyclicBarrier barrier = new CyclicBarrier(REQUEST_COUNT * URI_COUNT);
       
    70     static final SSLContext context;
       
    71 
       
    72     static {
       
    73         try {
       
    74             context = new jdk.test.lib.net.SimpleSSLContext().get();
       
    75             SSLContext.setDefault(context);
       
    76         } catch (Exception x) {
       
    77             throw new ExceptionInInitializerError(x);
       
    78         }
       
    79     }
       
    80 
       
    81     HttpTestServer http1Server;
       
    82     HttpTestServer http2Server;
       
    83     HttpTestServer https1Server;
       
    84     HttpTestServer https2Server;
       
    85     DigestEchoServer.TunnelingProxy proxy;
       
    86     URI http1URI;
       
    87     URI https1URI;
       
    88     URI http2URI;
       
    89     URI https2URI;
       
    90     InetSocketAddress proxyAddress;
       
    91     ProxySelector proxySelector;
       
    92     MyAuthenticator auth;
       
    93     HttpClient client;
       
    94     Executor executor = Executors.newCachedThreadPool();
       
    95 
       
    96     @DataProvider(name = "uris")
       
    97     Object[][] testURIs() {
       
    98         Object[][] uris = new Object[][]{
       
    99                 {List.of(http1URI.resolve("direct/orig/"),
       
   100                         https1URI.resolve("direct/orig/"),
       
   101                         https1URI.resolve("proxy/orig/"),
       
   102                         http2URI.resolve("direct/orig/"),
       
   103                         https2URI.resolve("direct/orig/"),
       
   104                         https2URI.resolve("proxy/orig/"))}
       
   105         };
       
   106         return uris;
       
   107     }
       
   108 
       
   109     public HttpClient newHttpClient(ProxySelector ps, Authenticator auth) {
       
   110         HttpClient.Builder builder = HttpClient
       
   111                 .newBuilder()
       
   112                 .sslContext(context)
       
   113                 .authenticator(auth)
       
   114                 .proxy(ps);
       
   115         return builder.build();
       
   116     }
       
   117 
       
   118     @BeforeClass
       
   119     public void setUp() throws Exception {
       
   120         try {
       
   121             InetSocketAddress sa =
       
   122                     new InetSocketAddress(InetAddress.getLoopbackAddress(), 0);
       
   123             auth = new MyAuthenticator();
       
   124 
       
   125             // HTTP/1.1
       
   126             HttpServer server1 = HttpServer.create(sa, 0);
       
   127             server1.setExecutor(executor);
       
   128             http1Server = HttpTestServer.of(server1);
       
   129             http1Server.addHandler(new TestHandler(), "/AuthFilterCacheTest/http1/");
       
   130             http1Server.start();
       
   131             http1URI = new URI("http://" + http1Server.serverAuthority()
       
   132                     + "/AuthFilterCacheTest/http1/");
       
   133 
       
   134             // HTTPS/1.1
       
   135             HttpsServer sserver1 = HttpsServer.create(sa, 100);
       
   136             sserver1.setExecutor(executor);
       
   137             sserver1.setHttpsConfigurator(new HttpsConfigurator(context));
       
   138             https1Server = HttpTestServer.of(sserver1);
       
   139             https1Server.addHandler(new TestHandler(), "/AuthFilterCacheTest/https1/");
       
   140             https1Server.start();
       
   141             https1URI = new URI("https://" + https1Server.serverAuthority()
       
   142                     + "/AuthFilterCacheTest/https1/");
       
   143 
       
   144             // HTTP/2.0
       
   145             http2Server = HttpTestServer.of(
       
   146                     new Http2TestServer("localhost", false, 0));
       
   147             http2Server.addHandler(new TestHandler(), "/AuthFilterCacheTest/http2/");
       
   148             http2Server.start();
       
   149             http2URI = new URI("http://" + http2Server.serverAuthority()
       
   150                     + "/AuthFilterCacheTest/http2/");
       
   151 
       
   152             // HTTPS/2.0
       
   153             https2Server = HttpTestServer.of(
       
   154                     new Http2TestServer("localhost", true, 0));
       
   155             https2Server.addHandler(new TestHandler(), "/AuthFilterCacheTest/https2/");
       
   156             https2Server.start();
       
   157             https2URI = new URI("https://" + https2Server.serverAuthority()
       
   158                     + "/AuthFilterCacheTest/https2/");
       
   159 
       
   160             proxy = DigestEchoServer.createHttpsProxyTunnel(
       
   161                     DigestEchoServer.HttpAuthSchemeType.NONE);
       
   162             proxyAddress = proxy.getProxyAddress();
       
   163             proxySelector = new HttpProxySelector(proxyAddress);
       
   164             client = newHttpClient(proxySelector, auth);
       
   165 
       
   166             System.out.println("Setup: done");
       
   167         } catch (Exception x) {
       
   168             tearDown();
       
   169             throw x;
       
   170         } catch (Error e) {
       
   171             tearDown();
       
   172             throw e;
       
   173         }
       
   174     }
       
   175 
       
   176     @AfterClass
       
   177     public void tearDown() {
       
   178         proxy = stop(proxy, DigestEchoServer.TunnelingProxy::stop);
       
   179         http1Server = stop(http1Server, HttpTestServer::stop);
       
   180         https1Server = stop(https1Server, HttpTestServer::stop);
       
   181         http2Server = stop(http2Server, HttpTestServer::stop);
       
   182         https2Server = stop(https2Server, HttpTestServer::stop);
       
   183         client = null;
       
   184 
       
   185         System.out.println("Teardown: done");
       
   186     }
       
   187 
       
   188     private interface Stoppable<T> {
       
   189         void stop(T service) throws Exception;
       
   190     }
       
   191 
       
   192     static <T> T stop(T service, Stoppable<T> stop) {
       
   193         try {
       
   194             if (service != null) stop.stop(service);
       
   195         } catch (Throwable x) {
       
   196         }
       
   197         return null;
       
   198     }
       
   199 
       
   200     static class HttpProxySelector extends ProxySelector {
       
   201         private static final List<Proxy> NO_PROXY = List.of(Proxy.NO_PROXY);
       
   202         private final List<Proxy> proxyList;
       
   203 
       
   204         HttpProxySelector(InetSocketAddress proxyAddress) {
       
   205             proxyList = List.of(new Proxy(Proxy.Type.HTTP, proxyAddress));
       
   206         }
       
   207 
       
   208         @Override
       
   209         public List<Proxy> select(URI uri) {
       
   210             // Our proxy only supports tunneling
       
   211             if (uri.getScheme().equalsIgnoreCase("https")) {
       
   212                 if (uri.getPath().contains("/proxy/")) {
       
   213                     return proxyList;
       
   214                 }
       
   215             }
       
   216             return NO_PROXY;
       
   217         }
       
   218 
       
   219         @Override
       
   220         public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
       
   221             System.err.println("Connection to proxy failed: " + ioe);
       
   222             System.err.println("Proxy: " + sa);
       
   223             System.err.println("\tURI: " + uri);
       
   224             ioe.printStackTrace();
       
   225         }
       
   226     }
       
   227 
       
   228     public static class TestHandler implements HttpTestHandler {
       
   229         static final AtomicLong respCounter = new AtomicLong();
       
   230 
       
   231         @Override
       
   232         public void handle(HttpTestExchange t) throws IOException {
       
   233             var count = respCounter.incrementAndGet();
       
   234             System.out.println("Responses handled: " + count);
       
   235             t.getRequestBody().readAllBytes();
       
   236 
       
   237             if (t.getRequestMethod().equalsIgnoreCase("GET")) {
       
   238                 if (!t.getRequestHeaders().containsKey("Authorization")) {
       
   239                     t.getResponseHeaders()
       
   240                             .addHeader("WWW-Authenticate", "Basic realm=\"Earth\"");
       
   241                     t.sendResponseHeaders(401, 0);
       
   242                 } else {
       
   243                     byte[] resp = RESPONSE_BODY.getBytes(StandardCharsets.UTF_8);
       
   244                     t.sendResponseHeaders(200, resp.length);
       
   245                     try {
       
   246                         barrier.await();
       
   247                     } catch (Exception e) {
       
   248                         throw new IOException(e);
       
   249                     }
       
   250                     t.getResponseBody().write(resp);
       
   251                 }
       
   252             }
       
   253             t.close();
       
   254         }
       
   255     }
       
   256 
       
   257     void doClient(List<URI> uris) {
       
   258         assert uris.size() == URI_COUNT;
       
   259         barrier.reset();
       
   260         System.out.println("Client opening connection to: " + uris.toString());
       
   261 
       
   262         List<CompletableFuture<HttpResponse<String>>> cfs = new ArrayList<>();
       
   263 
       
   264         for (int i = 0; i < REQUEST_COUNT; i++) {
       
   265             for (URI uri : uris) {
       
   266                 HttpRequest req = HttpRequest.newBuilder()
       
   267                         .uri(uri)
       
   268                         .build();
       
   269                 cfs.add(client.sendAsync(req, HttpResponse.BodyHandlers.ofString()));
       
   270             }
       
   271         }
       
   272         CompletableFuture.allOf(cfs.toArray(new CompletableFuture[0])).join();
       
   273     }
       
   274 
       
   275     static class MyAuthenticator extends Authenticator {
       
   276         private int count = 0;
       
   277 
       
   278         MyAuthenticator() {
       
   279             super();
       
   280         }
       
   281 
       
   282         public PasswordAuthentication getPasswordAuthentication() {
       
   283             System.out.println("Authenticator called: " + ++count);
       
   284             return (new PasswordAuthentication("user" + count,
       
   285                     ("passwordNotCheckedAnyway" + count).toCharArray()));
       
   286         }
       
   287 
       
   288         public int getCount() {
       
   289             return count;
       
   290         }
       
   291     }
       
   292 
       
   293     @Test(dataProvider = "uris")
       
   294     public void test(List<URI> uris) throws Exception {
       
   295         System.out.println("Server listening at " + uris.toString());
       
   296         doClient(uris);
       
   297     }
       
   298 }