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.io.UncheckedIOException; 28 import java.net.Authenticator; 29 import java.net.HttpURLConnection; 30 import java.net.InetSocketAddress; 31 import java.net.MalformedURLException; 32 import java.net.PasswordAuthentication; 33 import java.net.Proxy; 34 import java.net.URL; 35 import java.util.Locale; 36 import java.util.concurrent.atomic.AtomicInteger; 37 import java.util.logging.Level; 38 import java.util.logging.Logger; 39 import java.util.stream.Stream; 40 import javax.net.ssl.HostnameVerifier; 41 import javax.net.ssl.HttpsURLConnection; 42 import javax.net.ssl.SSLContext; 43 import javax.net.ssl.SSLSession; 44 import jdk.testlibrary.SimpleSSLContext; 45 46 /* 47 * @test 48 * @bug 8169415 49 * @library /lib/testlibrary/ 50 * @modules java.logging 51 * java.base/sun.net.www 52 * jdk.httpserver/sun.net.httpserver 53 * @build jdk.testlibrary.SimpleSSLContext HTTPTest HTTPTestServer HTTPTestClient 54 * @summary A simple HTTP test that starts an echo server supporting Digest 55 * authentication, then starts a regular HTTP client to invoke it. 56 * The client first does a GET request on "/", then follows on 57 * with a POST request that sends "Hello World!" to the server. 58 * The client expects to receive "Hello World!" in return. 59 * The test supports several execution modes: 60 * SERVER: The server performs Digest Server authentication; 61 * PROXY: The server pretends to be a proxy and performs 62 * Digest Proxy authentication; 63 * SERVER307: The server redirects the client (307) to another 64 * server that perform Digest authentication; 65 * PROXY305: The server attempts to redirect 66 * the client to a proxy using 305 code; 67 * @run main/othervm HTTPTest SERVER 68 * @run main/othervm HTTPTest PROXY 69 * @run main/othervm HTTPTest SERVER307 70 * @run main/othervm HTTPTest PROXY305 71 * 72 * @author danielfuchs 73 */ 74 public class HTTPTest { 75 76 public static final boolean DEBUG = 77 Boolean.parseBoolean(System.getProperty("test.debug", "false")); 78 public static enum HttpAuthType { SERVER, PROXY, SERVER307, PROXY305 }; 79 public static enum HttpProtocolType { HTTP, HTTPS }; 80 public static enum HttpSchemeType { NONE, BASICSERVER, BASIC, DIGEST }; 81 public static final HttpAuthType DEFAULT_HTTP_AUTH_TYPE = HttpAuthType.SERVER; 82 public static final HttpProtocolType DEFAULT_PROTOCOL_TYPE = HttpProtocolType.HTTP; 83 public static final HttpSchemeType DEFAULT_SCHEME_TYPE = HttpSchemeType.DIGEST; 84 85 public static class HttpTestAuthenticator extends Authenticator { 86 private final String realm; 87 private final String username; 88 // Used to prevent incrementation of 'count' when calling the 89 // authenticator from the server side. 90 private final ThreadLocal<Boolean> skipCount = new ThreadLocal<>(); 91 // count will be incremented every time getPasswordAuthentication() 92 // is called from the client side. 93 final AtomicInteger count = new AtomicInteger(); 94 private final String name; 95 HttpTestAuthenticator(String name, String realm, String username)96 public HttpTestAuthenticator(String name, String realm, String username) { 97 this.name = name; 98 this.realm = realm; 99 this.username = username; 100 } 101 102 @Override getPasswordAuthentication()103 protected PasswordAuthentication getPasswordAuthentication() { 104 if (skipCount.get() == null || skipCount.get().booleanValue() == false) { 105 System.out.println("Authenticator " + name + " called: " + count.incrementAndGet()); 106 } 107 return new PasswordAuthentication(getUserName(), 108 new char[] {'b','a','r'}); 109 } 110 111 // Called by the server side to get the password of the user 112 // being authentified. getPassword(String user)113 public final char[] getPassword(String user) { 114 if (user.equals(username)) { 115 skipCount.set(Boolean.TRUE); 116 try { 117 return getPasswordAuthentication().getPassword(); 118 } finally { 119 skipCount.set(Boolean.FALSE); 120 } 121 } 122 throw new SecurityException("User unknown: " + user); 123 } 124 125 @Override toString()126 public String toString() { 127 return super.toString() + "[name=\"" + name + "\"]"; 128 } 129 getUserName()130 public final String getUserName() { 131 return username; 132 } getRealm()133 public final String getRealm() { 134 return realm; 135 } 136 137 } 138 public static final HttpTestAuthenticator AUTHENTICATOR; 139 static { 140 AUTHENTICATOR = new HttpTestAuthenticator("AUTHENTICATOR","dublin", "foox"); 141 Authenticator.setDefault(AUTHENTICATOR); 142 } 143 144 static { 145 try { HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() { public boolean verify(String hostname, SSLSession session) { return true; } })146 HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() { 147 public boolean verify(String hostname, SSLSession session) { 148 return true; 149 } 150 }); SSLContext.setDefault(new SimpleSSLContext().get())151 SSLContext.setDefault(new SimpleSSLContext().get()); 152 } catch (IOException ex) { 153 throw new ExceptionInInitializerError(ex); 154 } 155 } 156 157 static final Logger logger = Logger.getLogger ("com.sun.net.httpserver"); 158 static { 159 if (DEBUG) logger.setLevel(Level.ALL); 160 Stream.of(Logger.getLogger("").getHandlers()) 161 .forEach(h -> h.setLevel(Level.ALL)); 162 } 163 164 static final int EXPECTED_AUTH_CALLS_PER_TEST = 1; 165 main(String[] args)166 public static void main(String[] args) throws Exception { 167 // new HTTPTest().execute(HttpAuthType.SERVER.name()); 168 new HTTPTest().execute(args); 169 } 170 execute(String... args)171 public void execute(String... args) throws Exception { 172 Stream<HttpAuthType> modes; 173 if (args == null || args.length == 0) { 174 modes = Stream.of(HttpAuthType.values()); 175 } else { 176 modes = Stream.of(args).map(HttpAuthType::valueOf); 177 } 178 modes.forEach(this::test); 179 System.out.println("Test PASSED - Authenticator called: " 180 + expected(AUTHENTICATOR.count.get())); 181 } 182 test(HttpAuthType mode)183 public void test(HttpAuthType mode) { 184 for (HttpProtocolType type: HttpProtocolType.values()) { 185 test(type, mode); 186 } 187 } 188 getHttpSchemeType()189 public HttpSchemeType getHttpSchemeType() { 190 return DEFAULT_SCHEME_TYPE; 191 } 192 test(HttpProtocolType protocol, HttpAuthType mode)193 public void test(HttpProtocolType protocol, HttpAuthType mode) { 194 if (mode == HttpAuthType.PROXY305 && protocol == HttpProtocolType.HTTPS ) { 195 // silently skip unsupported test combination 196 return; 197 } 198 System.out.println("\n**** Testing " + protocol + " " 199 + mode + " mode ****\n"); 200 int authCount = AUTHENTICATOR.count.get(); 201 int expectedIncrement = 0; 202 try { 203 // Creates an HTTP server that echoes back whatever is in the 204 // request body. 205 HTTPTestServer server = 206 HTTPTestServer.create(protocol, 207 mode, 208 AUTHENTICATOR, 209 getHttpSchemeType()); 210 try { 211 expectedIncrement += run(server, protocol, mode); 212 } finally { 213 server.stop(); 214 } 215 } catch (IOException ex) { 216 ex.printStackTrace(System.err); 217 throw new UncheckedIOException(ex); 218 } 219 int count = AUTHENTICATOR.count.get(); 220 if (count != authCount + expectedIncrement) { 221 throw new AssertionError("Authenticator called " + count(count) 222 + " expected it to be called " 223 + expected(authCount + expectedIncrement)); 224 } 225 } 226 227 /** 228 * Runs the test with the given parameters. 229 * @param server The server 230 * @param protocol The protocol (HTTP/HTTPS) 231 * @param mode The mode (PROXY, SERVER, SERVER307...) 232 * @return The number of times the default authenticator should have been 233 * called. 234 * @throws IOException in case of connection or protocol issues 235 */ run(HTTPTestServer server, HttpProtocolType protocol, HttpAuthType mode)236 public int run(HTTPTestServer server, 237 HttpProtocolType protocol, 238 HttpAuthType mode) 239 throws IOException 240 { 241 // Connect to the server with a GET request, then with a 242 // POST that contains "Hello World!" 243 HTTPTestClient.connect(protocol, server, mode, null); 244 // return the number of times the default authenticator is supposed 245 // to have been called. 246 return EXPECTED_AUTH_CALLS_PER_TEST; 247 } 248 count(int count)249 public static String count(int count) { 250 switch(count) { 251 case 0: return "not even once"; 252 case 1: return "once"; 253 case 2: return "twice"; 254 default: return String.valueOf(count) + " times"; 255 } 256 } 257 expected(int count)258 public static String expected(int count) { 259 switch(count) { 260 default: return count(count); 261 } 262 } protocol(HttpProtocolType type)263 public static String protocol(HttpProtocolType type) { 264 return type.name().toLowerCase(Locale.US); 265 } 266 url(HttpProtocolType protocol, InetSocketAddress address, String path)267 public static URL url(HttpProtocolType protocol, InetSocketAddress address, 268 String path) throws MalformedURLException { 269 return new URL(protocol(protocol), 270 address.getHostString(), 271 address.getPort(), path); 272 } 273 proxy(HTTPTestServer server, HttpAuthType authType)274 public static Proxy proxy(HTTPTestServer server, HttpAuthType authType) { 275 return (authType == HttpAuthType.PROXY) 276 ? new Proxy(Proxy.Type.HTTP, server.getAddress()) 277 : null; 278 } 279 openConnection(URL url, HttpAuthType authType, Proxy proxy)280 public static HttpURLConnection openConnection(URL url, 281 HttpAuthType authType, 282 Proxy proxy) 283 throws IOException { 284 285 HttpURLConnection conn = (HttpURLConnection) 286 (authType == HttpAuthType.PROXY 287 ? url.openConnection(proxy) 288 : url.openConnection()); 289 return conn; 290 } 291 } 292