--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/Authenticator/B4769350.java Tue Sep 12 19:03:39 2017 +0200
@@ -0,0 +1,499 @@
+/*
+ * Copyright (c) 2002, 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.
+ *
+ * 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.
+ */
+
+/**
+ * @test
+ * @bug 4769350 8017779
+ * @modules jdk.httpserver
+ * @run main/othervm B4769350 server
+ * @run main/othervm B4769350 proxy
+ * @summary proxy authentication username and password caching only works in serial case
+ * Run in othervm since the test sets system properties that are read by the
+ * networking stack and cached when the HTTP handler is invoked, and previous
+ * tests may already have invoked the HTTP handler.
+ */
+
+import com.sun.net.httpserver.HttpExchange;
+import com.sun.net.httpserver.HttpHandler;
+import com.sun.net.httpserver.HttpServer;
+import java.io.*;
+import java.net.*;
+import java.util.concurrent.BrokenBarrierException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+public class B4769350 {
+
+ static int count = 0;
+ static boolean error = false;
+
+ static void read (InputStream is) throws IOException {
+ while (is.read() != -1) {
+ //System.out.write (c);
+ }
+ }
+
+ static class Client extends Thread {
+ String authority, path;
+ boolean allowerror;
+
+ Client (String authority, String path, boolean allowerror) {
+ super("Thread-" + path);
+ this.authority = authority;
+ this.path = path;
+ this.allowerror = allowerror;
+ }
+
+ @Override
+ public void run () {
+ try {
+ URI u = new URI ("http", authority, path, null, null);
+ URL url = u.toURL();
+ URLConnection urlc = url.openConnection();
+ try (InputStream is = urlc.getInputStream()) {
+ read (is);
+ }
+ } catch (URISyntaxException e) {
+ System.out.println (e);
+ error = true;
+ } catch (IOException e) {
+ if (!allowerror) {
+ System.out.println (Thread.currentThread().getName()
+ + " " + e);
+ e.printStackTrace();
+ error = true;
+ }
+ }
+ }
+ }
+
+ class Server implements AutoCloseable {
+ HttpServer server;
+ Executor executor;
+
+ public String getAddress() {
+ return server.getAddress().getHostName();
+ }
+
+ public void startServer() {
+ InetSocketAddress addr = new InetSocketAddress(0);
+
+ try {
+ server = HttpServer.create(addr, 0);
+ } catch (IOException ioe) {
+ throw new RuntimeException("Server could not be created");
+ }
+ executor = Executors.newFixedThreadPool(10);
+ server.setExecutor(executor);
+ server.createContext("/test/realm1/t1a",
+ new AuthenticationHandlerT1a() );
+ server.createContext("/test/realm2/t1b",
+ new AuthenticationHandlerT1b());
+ server.createContext("/test/realm1/t1c",
+ new AuthenticationHandlerT1c());
+ server.createContext("/test/realm2/t1d",
+ new AuthenticationHandlerT1d());
+ server.createContext("/test/realm3/t2a",
+ new AuthenticationHandlerT2a());
+ server.createContext("/test/realm3/t2b",
+ new AuthenticationHandlerT2b());
+ server.createContext("/test/realm4/t3a",
+ new AuthenticationHandlerT3a());
+ server.createContext("/test/realm4/t3b",
+ new AuthenticationHandlerT3bc());
+ server.createContext("/test/realm4/t3c",
+ new AuthenticationHandlerT3bc());
+ t1Cond1 = new CyclicBarrier(3);
+ server.start();
+ }
+
+ public int getPort() {
+ return server.getAddress().getPort();
+ }
+
+ @Override
+ public void close() {
+ if (executor != null)
+ ((ExecutorService)executor).shutdownNow();
+ if (server != null)
+ server.stop(0);
+ }
+
+ /* T1 tests the client by sending 4 requests to 2 different realms
+ * in parallel. The client should recognise two pairs of dependent requests
+ * and execute the first of each pair in parallel. When they both succeed
+ * the second requests should be executed without calling the authenticator.
+ * The test succeeds if the authenticator was only called twice.
+ */
+ class AuthenticationHandlerT1a implements HttpHandler
+ {
+ volatile int count = -1;
+
+ @Override
+ public void handle(HttpExchange exchange) throws IOException {
+ count++;
+ try {
+ switch(count) {
+ case 0:
+ AuthenticationHandler.errorReply(exchange,
+ "Basic realm=\"realm1\"");
+ break;
+ case 1:
+ t1Cond1.await();
+ AuthenticationHandler.okReply(exchange);
+ break;
+ default:
+ System.out.println ("Unexpected request");
+ }
+ } catch (InterruptedException |
+ BrokenBarrierException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ class AuthenticationHandlerT1b implements HttpHandler
+ {
+ volatile int count = -1;
+
+ @Override
+ public void handle(HttpExchange exchange) throws IOException {
+ count++;
+ try {
+ switch(count) {
+ case 0:
+ AuthenticationHandler.errorReply(exchange,
+ "Basic realm=\"realm2\"");
+ break;
+ case 1:
+ t1Cond1.await();
+ AuthenticationHandler.okReply(exchange);
+ break;
+ default:
+ System.out.println ("Unexpected request");
+ }
+ } catch (InterruptedException | BrokenBarrierException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ class AuthenticationHandlerT1c implements HttpHandler
+ {
+ volatile int count = -1;
+
+ @Override
+ public void handle(HttpExchange exchange) throws IOException {
+ count++;
+ switch(count) {
+ case 0:
+ AuthenticationHandler.errorReply(exchange,
+ "Basic realm=\"realm1\"");
+ break;
+ case 1:
+ AuthenticationHandler.okReply(exchange);
+ break;
+ default:
+ System.out.println ("Unexpected request");
+ }
+ }
+ }
+
+ class AuthenticationHandlerT1d implements HttpHandler
+ {
+ volatile int count = -1;
+
+ @Override
+ public void handle(HttpExchange exchange) throws IOException {
+ count++;
+ switch(count) {
+ case 0:
+ AuthenticationHandler.errorReply(exchange,
+ "Basic realm=\"realm2\"");
+ break;
+ case 1:
+ AuthenticationHandler.okReply(exchange);
+ break;
+ default:
+ System.out.println ("Unexpected request");
+ }
+ }
+ }
+
+ /* T2 tests to check that if initial authentication fails, the second will
+ * succeed, and the authenticator is called twice
+ */
+
+ class AuthenticationHandlerT2a implements HttpHandler
+ {
+ volatile int count = -1;
+
+ @Override
+ public void handle(HttpExchange exchange) throws IOException {
+ count++;
+ if (count == 1) {
+ t2condlatch.countDown();
+ }
+ AuthenticationHandler.errorReply(exchange,
+ "Basic realm=\"realm3\"");
+
+ }
+ }
+
+ class AuthenticationHandlerT2b implements HttpHandler
+ {
+ volatile int count = -1;
+
+ @Override
+ public void handle(HttpExchange exchange) throws IOException {
+ count++;
+ switch(count) {
+ case 0:
+ AuthenticationHandler.errorReply(exchange,
+ "Basic realm=\"realm3\"");
+ break;
+ case 1:
+ AuthenticationHandler.okReply(exchange);
+ break;
+ default:
+ System.out.println ("Unexpected request");
+ }
+ }
+ }
+
+ /* T3 tests proxy and server authentication. three threads request same
+ * resource at same time. Authenticator should be called once for server
+ * and once for proxy
+ */
+
+ class AuthenticationHandlerT3a implements HttpHandler
+ {
+ volatile int count = -1;
+
+ @Override
+ public void handle(HttpExchange exchange) throws IOException {
+ count++;
+ switch(count) {
+ case 0:
+ AuthenticationHandler.proxyReply(exchange,
+ "Basic realm=\"proxy\"");
+ break;
+ case 1:
+ t3cond1.countDown();
+ AuthenticationHandler.errorReply(exchange,
+ "Basic realm=\"realm4\"");
+ break;
+ case 2:
+ AuthenticationHandler.okReply(exchange);
+ break;
+ default:
+ System.out.println ("Unexpected request");
+ }
+ }
+ }
+
+ class AuthenticationHandlerT3bc implements HttpHandler
+ {
+ volatile int count = -1;
+
+ @Override
+ public void handle(HttpExchange exchange) throws IOException {
+ count++;
+ switch(count) {
+ case 0:
+ AuthenticationHandler.proxyReply(exchange,
+ "Basic realm=\"proxy\"");
+ break;
+ case 1:
+ AuthenticationHandler.okReply(exchange);
+ break;
+ default:
+ System.out.println ("Unexpected request");
+ }
+ }
+ }
+ }
+
+ static class AuthenticationHandler {
+ static void errorReply(HttpExchange exchange, String reply)
+ throws IOException
+ {
+ exchange.getResponseHeaders().add("Connection", "close");
+ exchange.getResponseHeaders().add("WWW-Authenticate", reply);
+ exchange.sendResponseHeaders(401, 0);
+ exchange.close();
+ }
+
+ static void proxyReply (HttpExchange exchange, String reply)
+ throws IOException
+ {
+ exchange.getResponseHeaders().add("Proxy-Authenticate", reply);
+ exchange.sendResponseHeaders(407, 0);
+ }
+
+ static void okReply (HttpExchange exchange) throws IOException {
+ exchange.getResponseHeaders().add("Connection", "close");
+ String response = "Hello .";
+ exchange.sendResponseHeaders(200, response.getBytes().length);
+ try (OutputStream os = exchange.getResponseBody()) {
+ os.write(response.getBytes());
+ }
+ exchange.close();
+ }
+ }
+
+ static Server server;
+ static MyAuthenticator auth = new MyAuthenticator ();
+
+ static int redirects = 4;
+
+ static Client c1,c2,c3,c4,c5,c6,c7,c8,c9;
+
+ static CountDownLatch t2condlatch;
+ static CountDownLatch t3cond1;
+ static CyclicBarrier t1Cond1;
+
+ static void doServerTests (String authority, Server server) throws Exception
+ {
+ System.out.println ("Doing Server tests");
+ System.out.println ("T1");
+ c1 = new Client (authority, "/test/realm1/t1a", false);
+ c2 = new Client (authority, "/test/realm2/t1b", false);
+ c3 = new Client (authority, "/test/realm1/t1c", false);
+ c4 = new Client (authority, "/test/realm2/t1d", false);
+ c1.start(); c2.start();
+ t1Cond1.await();
+ c3.start(); c4.start();
+ c1.join(); c2.join(); c3.join(); c4.join();
+
+ int f = auth.getCount();
+ if (f != 2) {
+ except ("Authenticator was called "+f+" times. Should be 2",
+ server);
+ }
+ if (error) {
+ except ("error occurred", server);
+ }
+
+ auth.resetCount();
+ System.out.println ("T2");
+
+ c5 = new Client (authority, "/test/realm3/t2a", true);
+ c6 = new Client (authority, "/test/realm3/t2b", false);
+ t2condlatch = new CountDownLatch(1);
+ c5.start ();
+ t2condlatch.await();
+ c6.start ();
+ c5.join(); c6.join();
+
+ f = auth.getCount();
+ if (f != redirects+1) {
+ except ("Authenticator was called "+f+" times. Should be: "
+ + redirects+1, server);
+ }
+ if (error) {
+ except ("error occurred", server);
+ }
+ }
+
+ static void doProxyTests (String authority, Server server) throws Exception
+ {
+ System.out.println ("Doing Proxy tests");
+ c7 = new Client (authority, "/test/realm4/t3a", false);
+ c8 = new Client (authority, "/test/realm4/t3b", false);
+ c9 = new Client (authority, "/test/realm4/t3c", false);
+ t3cond1 = new CountDownLatch(1);
+ c7.start ();
+ t3cond1.await();
+ c8.start ();
+ c9.start ();
+ c7.join(); c8.join(); c9.join();
+
+ int f = auth.getCount();
+ if (f != 2) {
+ except ("Authenticator was called "+f+" times. Should be: " + 2,
+ server);
+ }
+ if (error) {
+ except ("error occurred", server);
+ }
+ }
+
+ public static void main (String[] args) throws Exception {
+ new B4769350().runTest(args[0].equals ("proxy"));
+ }
+
+ public void runTest(boolean proxy) throws Exception {
+ System.setProperty ("http.maxRedirects", Integer.toString (redirects));
+ System.setProperty ("http.auth.serializeRequests", "true");
+ Authenticator.setDefault (auth);
+ try (Server server = new Server()) {
+ server.startServer();
+ System.out.println ("Server: listening on port: "
+ + server.getPort());
+ if (proxy) {
+ System.setProperty ("http.proxyHost", "localhost");
+ System.setProperty ("http.proxyPort",
+ Integer.toString(server.getPort()));
+ doProxyTests ("www.foo.com", server);
+ } else {
+ doServerTests ("localhost:"+server.getPort(), server);
+ }
+ }
+
+ }
+
+ public static void except (String s, Server server) {
+ server.close();
+ throw new RuntimeException (s);
+ }
+
+ static class MyAuthenticator extends Authenticator {
+ MyAuthenticator () {
+ super ();
+ }
+
+ volatile int count = 0;
+
+ @Override
+ public PasswordAuthentication getPasswordAuthentication () {
+ PasswordAuthentication pw;
+ pw = new PasswordAuthentication ("user", "pass1".toCharArray());
+ count ++;
+ return pw;
+ }
+
+ public void resetCount () {
+ count = 0;
+ }
+
+ public int getCount () {
+ return count;
+ }
+ }
+}
+