Path: blob/master/test/jdk/java/net/HttpURLConnection/SetAuthenticator/HTTPTest.java
41152 views
/*1* Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223import java.io.IOException;24import java.io.UncheckedIOException;25import java.net.Authenticator;26import java.net.HttpURLConnection;27import java.net.InetSocketAddress;28import java.net.MalformedURLException;29import java.net.PasswordAuthentication;30import java.net.Proxy;31import java.net.URL;32import java.util.Locale;33import java.util.concurrent.atomic.AtomicInteger;34import java.util.logging.Level;35import java.util.logging.Logger;36import java.util.stream.Stream;37import javax.net.ssl.HostnameVerifier;38import javax.net.ssl.HttpsURLConnection;39import javax.net.ssl.SSLContext;40import javax.net.ssl.SSLSession;41import jdk.test.lib.net.SimpleSSLContext;42import static java.net.Proxy.NO_PROXY;4344/*45* @test46* @bug 816941547* @library /test/lib48* @modules java.logging49* java.base/sun.net.www50* jdk.httpserver/sun.net.httpserver51* @build jdk.test.lib.net.SimpleSSLContext HTTPTest HTTPTestServer HTTPTestClient52* @summary A simple HTTP test that starts an echo server supporting Digest53* authentication, then starts a regular HTTP client to invoke it.54* The client first does a GET request on "/", then follows on55* with a POST request that sends "Hello World!" to the server.56* The client expects to receive "Hello World!" in return.57* The test supports several execution modes:58* SERVER: The server performs Digest Server authentication;59* PROXY: The server pretends to be a proxy and performs60* Digest Proxy authentication;61* SERVER307: The server redirects the client (307) to another62* server that perform Digest authentication;63* PROXY305: The server attempts to redirect64* the client to a proxy using 305 code;65* @run main/othervm HTTPTest SERVER66* @run main/othervm HTTPTest PROXY67* @run main/othervm HTTPTest SERVER30768* @run main/othervm HTTPTest PROXY30569*70* @author danielfuchs71*/72public class HTTPTest {7374public static final boolean DEBUG =75Boolean.parseBoolean(System.getProperty("test.debug", "false"));76public static enum HttpAuthType { SERVER, PROXY, SERVER307, PROXY305 };77public static enum HttpProtocolType { HTTP, HTTPS };78public static enum HttpSchemeType { NONE, BASICSERVER, BASIC, DIGEST };79public static final HttpAuthType DEFAULT_HTTP_AUTH_TYPE = HttpAuthType.SERVER;80public static final HttpProtocolType DEFAULT_PROTOCOL_TYPE = HttpProtocolType.HTTP;81public static final HttpSchemeType DEFAULT_SCHEME_TYPE = HttpSchemeType.DIGEST;8283public static class HttpTestAuthenticator extends Authenticator {84private final String realm;85private final String username;86// Used to prevent incrementation of 'count' when calling the87// authenticator from the server side.88private final ThreadLocal<Boolean> skipCount = new ThreadLocal<>();89// count will be incremented every time getPasswordAuthentication()90// is called from the client side.91final AtomicInteger count = new AtomicInteger();92private final String name;9394public HttpTestAuthenticator(String name, String realm, String username) {95this.name = name;96this.realm = realm;97this.username = username;98}99100@Override101protected PasswordAuthentication getPasswordAuthentication() {102if (skipCount.get() == null || skipCount.get().booleanValue() == false) {103System.out.println("Authenticator " + name + " called: " + count.incrementAndGet());104}105return new PasswordAuthentication(getUserName(),106new char[] {'b','a','r'});107}108109// Called by the server side to get the password of the user110// being authentified.111public final char[] getPassword(String user) {112if (user.equals(username)) {113skipCount.set(Boolean.TRUE);114try {115return getPasswordAuthentication().getPassword();116} finally {117skipCount.set(Boolean.FALSE);118}119}120throw new SecurityException("User unknown: " + user);121}122123@Override124public String toString() {125return super.toString() + "[name=\"" + name + "\"]";126}127128public final String getUserName() {129return username;130}131public final String getRealm() {132return realm;133}134135}136public static final HttpTestAuthenticator AUTHENTICATOR;137static {138AUTHENTICATOR = new HttpTestAuthenticator("AUTHENTICATOR","dublin", "foox");139Authenticator.setDefault(AUTHENTICATOR);140}141142static {143try {144HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {145public boolean verify(String hostname, SSLSession session) {146return true;147}148});149SSLContext.setDefault(new SimpleSSLContext().get());150} catch (IOException ex) {151throw new ExceptionInInitializerError(ex);152}153}154155static final Logger logger = Logger.getLogger ("com.sun.net.httpserver");156static {157if (DEBUG) logger.setLevel(Level.ALL);158Stream.of(Logger.getLogger("").getHandlers())159.forEach(h -> h.setLevel(Level.ALL));160}161162static final int EXPECTED_AUTH_CALLS_PER_TEST = 1;163164public static void main(String[] args) throws Exception {165// new HTTPTest().execute(HttpAuthType.SERVER.name());166new HTTPTest().execute(args);167}168169public void execute(String... args) throws Exception {170Stream<HttpAuthType> modes;171if (args == null || args.length == 0) {172modes = Stream.of(HttpAuthType.values());173} else {174modes = Stream.of(args).map(HttpAuthType::valueOf);175}176modes.forEach(this::test);177System.out.println("Test PASSED - Authenticator called: "178+ expected(AUTHENTICATOR.count.get()));179}180181public void test(HttpAuthType mode) {182for (HttpProtocolType type: HttpProtocolType.values()) {183test(type, mode);184}185}186187public HttpSchemeType getHttpSchemeType() {188return DEFAULT_SCHEME_TYPE;189}190191public void test(HttpProtocolType protocol, HttpAuthType mode) {192if (mode == HttpAuthType.PROXY305 && protocol == HttpProtocolType.HTTPS ) {193// silently skip unsupported test combination194return;195}196System.out.println("\n**** Testing " + protocol + " "197+ mode + " mode ****\n");198int authCount = AUTHENTICATOR.count.get();199int expectedIncrement = 0;200try {201// Creates an HTTP server that echoes back whatever is in the202// request body.203HTTPTestServer server =204HTTPTestServer.create(protocol,205mode,206AUTHENTICATOR,207getHttpSchemeType());208try {209expectedIncrement += run(server, protocol, mode);210} finally {211server.stop();212}213} catch (IOException ex) {214ex.printStackTrace(System.err);215throw new UncheckedIOException(ex);216}217int count = AUTHENTICATOR.count.get();218if (count != authCount + expectedIncrement) {219throw new AssertionError("Authenticator called " + count(count)220+ " expected it to be called "221+ expected(authCount + expectedIncrement));222}223}224225/**226* Runs the test with the given parameters.227* @param server The server228* @param protocol The protocol (HTTP/HTTPS)229* @param mode The mode (PROXY, SERVER, SERVER307...)230* @return The number of times the default authenticator should have been231* called.232* @throws IOException in case of connection or protocol issues233*/234public int run(HTTPTestServer server,235HttpProtocolType protocol,236HttpAuthType mode)237throws IOException238{239// Connect to the server with a GET request, then with a240// POST that contains "Hello World!"241HTTPTestClient.connect(protocol, server, mode, null);242// return the number of times the default authenticator is supposed243// to have been called.244return EXPECTED_AUTH_CALLS_PER_TEST;245}246247public static String count(int count) {248switch(count) {249case 0: return "not even once";250case 1: return "once";251case 2: return "twice";252default: return String.valueOf(count) + " times";253}254}255256public static String expected(int count) {257switch(count) {258default: return count(count);259}260}261public static String protocol(HttpProtocolType type) {262return type.name().toLowerCase(Locale.US);263}264265public static URL url(HttpProtocolType protocol, InetSocketAddress address,266String path) throws MalformedURLException {267return new URL(protocol(protocol),268address.getAddress().getHostAddress(),269address.getPort(), path);270}271272public static Proxy proxy(HTTPTestServer server, HttpAuthType authType) {273if (authType != HttpAuthType.PROXY) return null;274275InetSocketAddress proxyAddress = server.getProxyAddress();276if (!proxyAddress.isUnresolved()) {277// Forces the proxy to use an unresolved address created278// from the actual IP address to avoid using the proxy279// address hostname which would result in resolving to280// a posibly different address. For instance we want to281// avoid cases such as:282// ::1 => "localhost" => 127.0.0.1283proxyAddress = InetSocketAddress.284createUnresolved(proxyAddress.getAddress().getHostAddress(),285proxyAddress.getPort());286}287288return new Proxy(Proxy.Type.HTTP, proxyAddress);289}290291public static HttpURLConnection openConnection(URL url,292HttpAuthType authType,293Proxy proxy)294throws IOException {295296HttpURLConnection conn = (HttpURLConnection)297(authType == HttpAuthType.PROXY298? url.openConnection(proxy)299: url.openConnection(NO_PROXY));300return conn;301}302}303304305