1 /* 2 * Copyright (c) 2016, 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 /** 25 * @test 26 * @modules jdk.incubator.httpclient 27 * jdk.httpserver 28 * @run main/othervm MultiAuthTest 29 * @summary Basic Authentication test with multiple clients issuing 30 * multiple requests. Includes password changes 31 * on server and client side. 32 */ 33 34 import com.sun.net.httpserver.BasicAuthenticator; 35 import com.sun.net.httpserver.HttpContext; 36 import com.sun.net.httpserver.HttpExchange; 37 import com.sun.net.httpserver.HttpHandler; 38 import com.sun.net.httpserver.HttpServer; 39 import java.io.IOException; 40 import java.io.InputStream; 41 import java.io.OutputStream; 42 import java.net.InetSocketAddress; 43 import java.net.PasswordAuthentication; 44 import java.net.URI; 45 import jdk.incubator.http.*; 46 import java.util.concurrent.ExecutorService; 47 import java.util.concurrent.Executors; 48 import static java.nio.charset.StandardCharsets.US_ASCII; 49 import static jdk.incubator.http.HttpRequest.BodyProcessor.fromString; 50 import static jdk.incubator.http.HttpResponse.BodyHandler.asString; 51 import java.util.UUID; 52 import java.util.concurrent.atomic.AtomicInteger; 53 import java.util.function.Function; 54 55 public class MultiAuthTest { 56 57 static volatile boolean ok; 58 static final String RESPONSE = "Hello world"; 59 static final String POST_BODY = "This is the POST body " + UUID.randomUUID(); 60 61 static HttpServer createServer(ExecutorService e, BasicAuthenticator sa) throws Exception { 62 HttpServer server = HttpServer.create(new InetSocketAddress(0), 10); 63 Handler h = new Handler(); 64 HttpContext serverContext = server.createContext("/test", h); 65 serverContext.setAuthenticator(sa); 66 server.setExecutor(e); 67 server.start(); 68 return server; 69 } 70 71 public interface HttpRequestBuilderFactory extends Function<URI, HttpRequest.Builder> { 72 73 default HttpRequest.Builder request(URI uri) { 74 return this.apply(uri); 75 } 76 } 77 78 79 public static void main(String[] args) throws Exception { 80 ExecutorService e = Executors.newCachedThreadPool(); 81 ServerAuth sa = new ServerAuth("foo realm"); 82 HttpServer server = createServer(e, sa); 83 int port = server.getAddress().getPort(); 84 System.out.println("Server port = " + port); 85 86 ClientAuth ca = new ClientAuth(); 87 HttpClient client1 = HttpClient.newBuilder() 88 .authenticator(ca) 89 .build(); 90 HttpClient client2 = HttpClient.newBuilder() 91 .authenticator(ca) 92 .build(); 93 HttpClient client3 = HttpClient.newHttpClient(); 94 95 try { 96 URI uri = new URI("http://127.0.0.1:" + port + "/test/foo"); 97 System.out.println("URI: " + uri); 98 99 System.out.println("\nTesting with client #1, Authenticator #1"); 100 test(client1, ca, uri, 1, null); 101 System.out.println("Testing again with client #1, Authenticator #1"); 102 test(client1, ca, uri, 1, null); 103 System.out.println("Testing with client #2, Authenticator #1"); 104 test(client2, ca, uri, 2, null); 105 106 System.out.println("Testing with default client" 107 + " (HttpClient.newHttpClient()), no authenticator"); 108 test(HttpClient.newHttpClient(), ca, uri, 2, IOException.class); 109 110 System.out.println("\nSetting default authenticator\n"); 111 java.net.Authenticator.setDefault(ca); 112 113 System.out.println("Testing default client" 114 + " (HttpClient.newHttpClient()), no authenticator"); 115 test(HttpClient.newHttpClient(), ca, uri, 3, IOException.class); 116 117 System.out.println("Testing with client #4, no authenticator"); 118 test(client3, ca, uri, 4, IOException.class); 119 120 String oldpwd = sa.passwd; 121 sa.passwd = "changed"; 122 System.out.println("\nChanged server password\n"); 123 124 sa.passwd = "changed"; 125 System.out.println("\nChanged server password\n"); 126 127 System.out.println("Testing with client #1, Authenticator #1" 128 + " (count=" + ca.count.get() +")"); 129 test(client1, ca, uri, 7, IOException.class); 130 System.out.println("Testing again with client #1, Authenticator #1" 131 + " (count=" + ca.count.get() +")"); 132 test(client1, ca, uri, 10, IOException.class); 133 System.out.println("Testing with client #2, Authenticator #1" 134 + " (count=" + ca.count.get() +")"); 135 test(client2, ca, uri, 14, IOException.class); 136 137 System.out.println("\nRestored server password" 138 + " (count=" + ca.count.get() +")\n"); 139 sa.passwd = oldpwd; 140 141 int count = ca.count.get(); // depends on retry limit... 142 System.out.println("Testing with client #1, Authenticator #1"); 143 test(client1, ca, uri, count+1, null); 144 System.out.println("Testing again with client #1, Authenticator #1"); 145 test(client1, ca, uri, count+1, null); 146 System.out.println("Testing with client #2, Authenticator #1"); 147 test(client2, ca, uri, count+2, null); 148 149 sa.passwd = ca.passwd = "changed#2"; 150 System.out.println("\nChanged password on both sides\n"); 151 152 System.out.println("Testing with client #1, Authenticator #1"); 153 test(client1, ca, uri, count+3, null); 154 System.out.println("Testing again with client #1, Authenticator #1"); 155 test(client1, ca, uri, count+3, null); 156 System.out.println("Testing with client #2, Authenticator #1"); 157 test(client2, ca, uri, count+4, null); 158 } finally { 159 server.stop(0); 160 e.shutdownNow(); 161 } 162 System.out.println("OK"); 163 } 164 165 static void test(HttpClient client, 166 ClientAuth ca, 167 URI uri, 168 int expectCount, 169 Class<? extends Exception> expectFailure) 170 throws IOException, InterruptedException 171 { 172 HttpRequest req = HttpRequest.newBuilder(uri).GET().build(); 173 174 HttpResponse resp; 175 try { 176 resp = client.send(req, asString()); 177 ok = resp.statusCode() == 200 && 178 resp.body().equals(RESPONSE); 179 if (expectFailure != null) { 180 throw new RuntimeException("Expected " + expectFailure.getName() 181 +" not raised"); 182 } 183 } catch (IOException io) { 184 if (expectFailure != null) { 185 if (expectFailure.isInstance(io)) { 186 System.out.println("Got expected exception: " + io); 187 return; 188 } 189 } 190 throw io; 191 } 192 193 if (!ok || ca.count.get() != expectCount) 194 throw new RuntimeException("Test failed: ok=" + ok 195 + " count=" + ca.count.get() + " (expected=" + expectCount+")"); 196 197 // repeat same request, should succeed but no additional authenticator calls 198 resp = client.send(req, asString()); 199 ok = resp.statusCode() == 200 && 200 resp.body().equals(RESPONSE); 201 202 if (!ok || ca.count.get() != expectCount) 203 throw new RuntimeException("Test failed: ok=" + ok 204 + " count=" + ca.count.get() + " (expected=" + expectCount+")"); 205 206 // try a POST 207 req = HttpRequest.newBuilder(uri) 208 .POST(fromString(POST_BODY)) 209 .build(); 210 resp = client.send(req, asString()); 211 ok = resp.statusCode() == 200; 212 213 if (!ok || ca.count.get() != expectCount) 214 throw new RuntimeException("Test failed"); 215 216 } 217 218 static class ServerAuth extends BasicAuthenticator { 219 220 volatile String passwd = "passwd"; 221 222 ServerAuth(String realm) { 223 super(realm); 224 } 225 226 @Override 227 public boolean checkCredentials(String username, String password) { 228 if (!"user".equals(username) || !passwd.equals(password)) { 229 return false; 230 } 231 return true; 232 } 233 234 } 235 236 static class ClientAuth extends java.net.Authenticator { 237 final AtomicInteger count = new AtomicInteger(); 238 volatile String passwd = "passwd"; 239 240 @Override 241 protected PasswordAuthentication getPasswordAuthentication() { 242 count.incrementAndGet(); 243 return new PasswordAuthentication("user", passwd.toCharArray()); 244 } 245 } 246 247 static class Handler implements HttpHandler { 248 static volatile boolean ok; 249 250 @Override 251 public void handle(HttpExchange he) throws IOException { 252 String method = he.getRequestMethod(); 253 InputStream is = he.getRequestBody(); 254 if (method.equalsIgnoreCase("POST")) { 255 String requestBody = new String(is.readAllBytes(), US_ASCII); 256 if (!requestBody.equals(POST_BODY)) { 257 he.sendResponseHeaders(500, -1); 258 ok = false; 259 } else { 260 he.sendResponseHeaders(200, -1); 261 ok = true; 262 } 263 } else { // GET 264 he.sendResponseHeaders(200, RESPONSE.length()); 265 OutputStream os = he.getResponseBody(); 266 os.write(RESPONSE.getBytes(US_ASCII)); 267 os.close(); 268 ok = true; 269 } 270 } 271 272 } 273 }